Import CSV działa w CLI, a nie w SQL
W dialekcie SQL używanym przez SQLite nie ma instrukcji IMPORT. Wczytywanie CSV to funkcja powłoki wiersza poleceń sqlite3: polecenie z kropką o nazwie .import. To ważna zmiana myślenia, jeśli znasz LOAD DATA INFILE z MySQL albo COPY z Postgresa: one działają po stronie serwera, a .import wykonuje za ciebie narzędzie klienckie, które czyta plik i w tle wysyła instrukcje INSERT.
Dlatego wszystko na tej stronie zakłada, że jesteś w powłoce sqlite3:
sqlite3 mydata.db
Jeśli zamiast tego musisz importować z kodu aplikacji (Python, Node, Go), wczytasz CSV w swoim języku i użyjesz sparametryzowanych instrukcji INSERT. Ten sposób omawiamy w rozdziale o integracji z aplikacjami. Tutaj skupiamy się na CLI.
Podstawowe .import
Najkrótsza droga: powiedz SQLite, że plik jest w formacie CSV, a potem wskaż .import plik i nazwę tabeli.
.mode csv
.import people.csv people
Dzieją się dwie różne rzeczy, zależnie od tego, czy people już istnieje:
- Tabela nie istnieje: SQLite ją tworzy, biorąc pierwszy wiersz CSV jako nazwy kolumn. Każda kolumna dostaje powinowactwo
TEXT. - Tabela istnieje: SQLite wstawia każdy wiersz pliku jako dane. Wiersz nagłówka, jeśli jest, staje się zwykłym wierszem.
Na tym drugim przypadku większość osób łapie się przy pierwszej próbie. Jeśli CSV ma nagłówek, a tabela już istnieje, musisz go jawnie pominąć.
Pomijanie nagłówka przy istniejącej tabeli
Użyj --skip 1, żeby .import zignorowało pierwsze N linii:
CREATE TABLE people (
name TEXT,
age INTEGER,
city TEXT
);
.import --csv --skip 1 people.csv people
--csv to skrót od .mode csv obowiązujący tylko dla tego jednego polecenia, więc nie musisz osobno ustawiać trybu. --skip 1 pomija nagłówek. Pozostałe linie trafiają do people w kolejności kolumn.
Szybkie sprawdzenie po imporcie:
SELECT count(*) FROM people;
SELECT * FROM people LIMIT 5;
Kolejność kolumn w pliku musi odpowiadać kolejności kolumn w tabeli. Nie ma dopasowania po nagłówkach: .import po prostu przypisuje N-te pole do N-tej kolumny.
Niech SQLite utworzy tabelę za ciebie
Przy pracy eksploracyjnej najprościej całkiem pominąć CREATE TABLE i pozwolić .import zbudować tabelę na podstawie nagłówka:
.mode csv
.import sales.csv sales
.schema sales
.schema sales pokaże coś takiego:
CREATE TABLE sales(
"order_id" TEXT,
"amount" TEXT,
"ordered_at" TEXT
);
Zwróć uwagę, że każda kolumna jest TEXT. To celowe: .import nie próbuje zgadywać typów. Jeśli chcesz, żeby amount było liczbą rzeczywistą, a ordered_at porządnym znacznikiem czasu, najpierw sam utwórz tabelę z właściwymi typami, a potem importuj z --skip 1. Powinowactwo typów w SQLite zamieni przy wstawianiu ciągi liczbowe na liczby całkowite i rzeczywiste.
Własne separatory: TSV, kreska pionowa, średnik
.mode csv używa przecinka. Dla plików rozdzielanych tabulatorami zmień tryb:
.mode tabs
.import data.tsv events
Dla innych separatorów użyj .separator po wybraniu trybu:
.mode csv
.separator "|"
.import pipe_data.txt events
Jedna rzecz do zapamiętania: .mode csv stosuje zasady cudzysłowów z RFC 4180, więc pola z przecinkami lub znakami nowej linii w środku działają, o ile są poprawnie ujęte w ". .mode tabs to prostszy tryb, który dzieli tekst po znaku, bez obsługi cudzysłowów. Jeśli plik ma pola w cudzysłowach z separatorami w środku, zostań przy .mode csv i zmień separator.
Realistyczny przykład krok po kroku
Załóżmy, że orders.csv wygląda tak:
order_id,customer,amount,ordered_at
1001,Ada,49.99,2026-01-12
1002,Boris,12.50,2026-01-13
1003,"Chen, Wei",199.00,2026-01-14
Zwróć uwagę, że wiersz 3 ma przecinek wewnątrz pola w cudzysłowie. Oto cała sesja:
W prawdziwej powłoce blok INSERT zastąpiłoby jedno .import --csv --skip 1 orders.csv orders. Pole "Chen, Wei" pozostaje nienaruszone, bo tryb CSV respektuje cudzysłowy. Dzięki typom kolumn amount staje się liczbą rzeczywistą, a order_id liczbą całkowitą.
Import w transakcji
.import wysyła jedno INSERT na wiersz. Przy kilku tysiącach wierszy to nie problem. Przy milionie jest to boleśnie wolne, chyba że opakujesz całość w transakcję, żeby SQLite nie zatwierdzał zmian po każdym wierszu:
BEGIN;
.import --csv --skip 1 big_file.csv events
COMMIT;
Ta jedna zmiana potrafi skrócić import z kilku minut do kilku sekund. Jeśli coś się nie uda w trakcie importu, ROLLBACK cofa częściowo wczytane dane, co przydaje się też przy ponownych próbach.
Możesz jeszcze bardziej przyspieszyć import, usuwając indeksy przed nim i tworząc je ponownie po nim: utrzymywanie indeksu przy każdym wierszu sporo kosztuje.
Częste błędy i jak je naprawić
Error: expected N columns but found M: liczba pól w wierszu nie zgadza się z tabelą. Zwykle przyczyną jest:
- Zbędny przecinek w polu bez cudzysłowów. Wyeksportuj plik ponownie z poprawnymi cudzysłowami CSV albo przełącz się na
.mode csv(RFC 4180) zamiast.mode tabs. - Pusta linia na końcu pliku. Popraw plik albo pomysłowo użyj
--skip. - Tabela ma więcej kolumn niż CSV. Dodaj brakujące kolumny do pliku albo wstaw dane do tabeli pośredniej o właściwym kształcie i skopiuj je do docelowej tabeli.
Nagłówek pojawia się jako dane: przy istniejącej tabeli zabrakło --skip 1. Usuń ten wiersz (DELETE FROM t WHERE rowid = 1) i uruchom import ponownie z flagą.
Liczby zapisane jako tekst: .import samo utworzyło tabelę, więc każda kolumna jest TEXT. Usuń tabelę, zdefiniuj ją jawnie z kolumnami INTEGER/REAL i zaimportuj ponownie.
Error: no such file: ścieżka jest względna wobec miejsca, z którego uruchomiono sqlite3, a nie wobec pliku bazy. Użyj ścieżki bezwzględnej albo przejdź przez cd do właściwego katalogu przed otwarciem powłoki.
CLI wypisuje przy błędach numery linii, co jest najszybszym sposobem na znalezienie problematycznego wiersza w dużym pliku.
Krótkie podsumowanie
.importto polecenie z kropką w CLI, a nie SQL. Uruchamiaj je w powłocesqlite3.- Użyj
--csv, żeby poprawnie obsłużyć cudzysłowy, i--skip 1, żeby zignorować wiersz nagłówka. - Jeśli tabela nie istnieje,
.importtworzy ją na podstawie nagłówka, ale każda kolumna będzieTEXT. Żeby mieć właściwe typy, utwórz tabelę sam. - Duże importy opakuj w
BEGIN/COMMIT, żeby uniknąć osobnej transakcji dla każdego wiersza. - Kolejność kolumn w pliku musi odpowiadać kolejności kolumn w tabeli.
Dalej: eksport danych na zewnątrz
Import to tylko połowa historii. Ta sama powłoka potrafi zrzucić wyniki zapytań albo całe tabele z powrotem do CSV, JSON lub SQL, co przydaje się przy kopiach zapasowych, potokach danych i przekazywaniu danych do innych narzędzi. O tym w części o eksporcie danych.
Najczęściej zadawane pytania
Jak zaimportować plik CSV do SQLite?
Otwórz bazę w CLI sqlite3, przełącz się w tryb CSV przez .mode csv, a potem uruchom .import data.csv table_name. Jeśli tabela jeszcze nie istnieje, SQLite ją tworzy, biorąc pierwszy wiersz pliku jako nazwy kolumn. Jeśli istnieje, każdy wiersz pliku zostaje wstawiony jako dane, więc zwykle potrzebujesz .import --skip 1, żeby pominąć nagłówek.
Jak zaimportować CSV z nagłówkiem do istniejącej tabeli SQLite?
Użyj .import --csv --skip 1 data.csv table_name. Flaga --skip 1 każe SQLite zignorować pierwszą linię, żeby nagłówek nie stał się wierszem danych. Bez niej skończysz z wierszem zawierającym dosłowne nazwy kolumn.
Dlaczego import CSV do SQLite kończy się błędem 'expected N columns but found M'?
Plik ma wiersze z inną liczbą kolumn niż tabela, zwykle przez przecinki w środku pól, niezabezpieczone cudzysłowy albo pustą linię na końcu. Użyj .mode csv (lub --csv) zamiast .mode tabs, żeby SQLite obsługiwał cudzysłowy zgodnie z RFC 4180, i sprawdź plik w edytorze tekstu pod kątem zbędnych separatorów. CLI wypisuje numer problematycznej linii, co jest najszybszym sposobem na znalezienie złego wiersza.