Czy można wstawiać wiele wierszy jednocześnie do bazy danych SQLite?


551

W MySQL możesz wstawić wiele wierszy w ten sposób:

INSERT INTO 'tablename' ('column1', 'column2') VALUES
    ('data1', 'data2'),
    ('data1', 'data2'),
    ('data1', 'data2'),
    ('data1', 'data2');

Jednak pojawia się błąd, gdy próbuję zrobić coś takiego. Czy można wstawiać wiele wierszy jednocześnie do bazy danych SQLite? Jaka jest składnia, aby to zrobić?


26
To nie dupek, ponieważ to pytanie dotyczy konkretnie SQLite, w przeciwieństwie do SQL w ogóle (choć niektóre odpowiedzi na to pytanie są pomocne w tym przypadku).
Brian Campbell

5
W przypadku wkładek zbiorczych: stackoverflow.com/questions/1711631/…
tuinstoel

5
Pytanie brzmi, dlaczego powinieneś to zrobić. Ponieważ SQlite jest w trakcie przetwarzania na tej samej maszynie i możesz zawinąć wiele wstawek w transakcję, nie widzę potrzeby?
andig

2
Tak, zacznij od wersji 2012-03-20 (3.7.11), twoja składnia jest obsługiwana.
mjb

Odpowiedzi:


607

aktualizacja

Jak podkreśla BrianCampbell , SQLite 3.7.11 i nowsze obsługują teraz prostszą składnię oryginalnego postu . Jednak pokazane podejście jest nadal odpowiednie, jeśli chcesz uzyskać maksymalną kompatybilność ze starszymi bazami danych.

oryginalna odpowiedź

Gdybym miał uprawnienia, wybiłbym odpowiedź rzeki : możesz wstawić wiele wierszy w SQLite, potrzebujesz tylko innej składni . Aby to doskonale wyjaśnić, przykład OPS MySQL:

INSERT INTO 'tablename' ('column1', 'column2') VALUES
  ('data1', 'data2'),
  ('data1', 'data2'),
  ('data1', 'data2'),
  ('data1', 'data2');

Można to przekształcić w SQLite jako:

     INSERT INTO 'tablename'
          SELECT 'data1' AS 'column1', 'data2' AS 'column2'
UNION ALL SELECT 'data1', 'data2'
UNION ALL SELECT 'data1', 'data2'
UNION ALL SELECT 'data1', 'data2'

uwaga na temat wydajności

Pierwotnie użyłem tej techniki do wydajnego ładowania dużych zestawów danych z Ruby on Rails. Jednak , jak zauważa Jaime Cook , nie jest jasne, czy jest to szybsze pakowanie osoby INSERTsw ramach jednej transakcji:

BEGIN TRANSACTION;
INSERT INTO 'tablename' table VALUES ('data1', 'data2');
INSERT INTO 'tablename' table VALUES ('data3', 'data4');
...
COMMIT;

Jeśli Twoim celem jest wydajność, powinieneś najpierw spróbować.

uwaga na temat UNION vs UNION ALL

Jak skomentowało kilka osób, jeśli użyjesz UNION ALL(jak pokazano powyżej), wszystkie wiersze zostaną wstawione, więc w tym przypadku otrzymasz cztery wiersze data1, data2. Jeśli pominiesz to ALL, zduplikowane wiersze zostaną wyeliminowane (a operacja prawdopodobnie będzie nieco wolniejsza). Używamy UNION ALL, ponieważ bardziej pasuje do semantyki oryginalnego postu.

w zamknięciu

PS: Proszę o odpowiedź +1 rzeki , ponieważ najpierw przedstawiła rozwiązanie.


102
Kolejna uwaga: sqlite wydaje się obsługiwać do 500 takich unii wybranych na zapytanie, więc jeśli próbujesz dodać więcej danych, będziesz musiał podzielić je na 500 bloków elementów ( sqlite.org/limits.html )
Jamie Cook

3
Uzgodnione: SQLite nie jest wąskim gardłem, jest narzutem pojedynczych transakcji ORM (w moim przypadku Ruby On Rails). To wciąż duża wygrana.
nieustraszony_fool

3
Jak to rozwiązanie lub rozwiązanie 3.7.11 porównuje się do korzystania z bloków transakcyjnych? czy to jest szybsze do insert into t values (data),(data),(data)czy begin transaction; insert into t values (data);insert into t values (data);insert into t values (data);Commit;?
Dan

5
Jako dalsza uwaga: bądź ostrożny! ta składnia usuwa zduplikowane wiersze! użyj, UNION ALLaby tego uniknąć (lub w przypadku niewielkich przyrostów wydajności).
chacham15

1
@Shadowfax, aby sprawdzić wersję SQLite, spójrz na sqlite3.sqlite_version(powinna to być wersja 3.7.x lub 3.8.x), a nie sqlite3.version(która jest tylko wersją modułu python).
maks.

562

Tak, jest to możliwe, ale nie przy zwykłych wartościach wstawianych rozdzielanych przecinkami.

Spróbuj tego...

insert into myTable (col1,col2) 
     select aValue as col1,anotherValue as col2 
     union select moreValue,evenMoreValue 
     union...

Tak, jest to trochę brzydkie, ale wystarczająco łatwe, aby zautomatyzować generowanie instrukcji na podstawie zestawu wartości. Wygląda też na to, że musisz tylko zadeklarować nazwy kolumn przy pierwszym wyborze.


36
proszę UNION ALLnie używać UNION. Z wyjątkiem sytuacji, gdy chcesz usunąć duplikaty.
Benoit

4
Jeśli chcesz, aby identyfikatory automatycznie zwiększały wartość, podaj im wartość NULL.
lenooh

241

Tak, od SQLite 3.7.11 jest to obsługiwane w SQLite. Z dokumentacji SQLite :

Składnia instrukcji SQLite INSERT

(kiedy ta odpowiedź została pierwotnie napisana, nie była obsługiwana)

W celu zachowania zgodności ze starszymi wersjami SQLite, można użyć lewę sugerowane przez Andy i fearless_fool użyciu UNION, ale do 3.7.11 i później prostsza składnia opisane tutaj powinny być preferowane.


3
Powiedziałbym, że diagramm dopuszcza wiele wierszy, ponieważ po nawiasie jest zamknięta pętla z przecinkiem VALUES.
Johannes Gerer

@JohannesGerer Zaktualizowali ten obraz, odkąd go osadziłem. Możesz zobaczyć schemat w momencie, gdy go osadziłem w Archiwum internetowym . W rzeczywistości zaledwie dwa miesiące temu dodali obsługę wielu wierszy we wstawce, a zaledwie dwa dni temu wydali wersję z tą zmianą. Zaktualizuję odpowiednio swoją odpowiedź.
Brian Campbell

1
@Brian, czy mógłbyś zacytować tekst dokumentacji SQLite, który stwierdza, że JEST TO możliwe? Ponownie przeczytałem wstawianie dokumentów 3 razy i nie znalazłem nic o wstawianiu wielu wierszy, ale tylko to zdjęcie (i nic o przecinku VALUES (...), (...)) :(
Prizoff

1
@Prizoff I link do zatwierdzenia, w którym dodano tę obsługę , w tym przypadków testowych. Na diagramie (porównaj łącze IA ) widać, że wokół wyrażenia występuje pętla VALUESwskazująca, że ​​można ją powtórzyć, oddzielając ją przecinkami. Połączyłem się z uwagami do wydania dla wersji, dodając funkcję, która mówi „Ulepsz składnię INSERT, aby umożliwić wstawianie wielu wierszy za pomocą klauzuli VALUES”.
Brian Campbell

1
@Prizoff Wspomniałem o tym opiekunowi SQLite, który wprowadził poprawkę, która jest dostępna w dokumentacji roboczej . Domyślam się, że będzie to w oficjalnej dokumentacji od następnego wydania.
Brian Campbell

57

Napisałem trochę kodu ruby, aby wygenerować pojedynczą 500-elementową wielowierszową wstawkę z serii instrukcji insert, co było znacznie szybsze niż uruchamianie poszczególnych insertów. Potem próbowałem po prostu zawinąć wiele wstawek w jedną transakcję i odkryłem, że mogę uzyskać ten sam rodzaj przyspieszenia przy znacznie mniejszym kodzie.

BEGIN TRANSACTION;
INSERT INTO table VALUES (1,1,1,1);
INSERT INTO table VALUES (2,2,2,2);
...
COMMIT;

1
ale to nie działało w kodzie, podczas gdy działa bezpośrednio w menedżerze SQLite. W kodzie wstawia tylko pierwszy wiersz :(
Vaibhav Saran

4
Używam tego stylu wstawiania w moim kodzie i działa idealnie. Musisz tylko upewnić się, że prześlesz WSZYSTKO SQL jednocześnie. To był dla mnie ogromny wzrost prędkości, ale jestem ciekawy, czy zaakceptowana odpowiedź jest szybsza czy wolniejsza?
Dan

To podejście bardzo dobrze skaluje się podczas modyfikowania wielu tabel w ramach jednej transakcji, co często robię, gdy wsadowo ładuję bazę danych.
Frank

Pamiętaj, że to podejście nie zadziała, jeśli będziesz potrzebować powiązań . Aparat SQLite3 przyjmie, że wszystkie twoje wiązania mają być zastosowane do pierwszej instrukcji i zignoruje następujące instrukcje. Zobacz to SO pytanie, aby uzyskać bardziej szczegółowe wyjaśnienie.
Philippe Hebert

38

Według tej strony nie jest obsługiwane:

  • 2007-12-03: Wielorzędowe INSERT aka złożone INSERT nie jest obsługiwane.
  INSERT INTO table (col1, col2) VALUES 
      ('row1col1', 'row1col2'), ('row2col1', 'row2col2'), ...

W rzeczywistości, zgodnie ze standardem SQL92, wyrażenie VALUES powinno być zdolne do samodzielnego działania. Na przykład następujące powinny zwrócić tabelę z jedną kolumną z trzema wierszami:VALUES 'john', 'mary', 'paul';

Począwszy od wersji 3.7.11 SQLite ma wsparcia wielorzędowe-insert . Richard Hipp komentuje:

„Nowa wielowartościowa wstawka jest po prostu syntaktycznym suger (sic) dla złożonej wkładki. Nie ma żadnej przewagi wydajności w taki czy inny sposób.”


Tak czy inaczej nie ma przewagi wydajności. - Czy możesz mi powiedzieć, gdzie widziałeś tę uwagę? Nigdzie nie mogłem go znaleźć.
Alix Axel

15

Rozpocznij od wersji 2012-03-20 (3.7.11), sqlite obsługuje następującą składnię INSERT:

INSERT INTO 'tablename' ('column1', 'column2') VALUES
  ('data1', 'data2'),
  ('data3', 'data4'),
  ('data5', 'data6'),
  ('data7', 'data8');

Przeczytaj dokumentację: http://www.sqlite.org/lang_insert.html

PS: Proszę +1 do odpowiedzi / odpowiedzi Briana Campbella. nie moje! Najpierw przedstawił rozwiązanie.


10

Jak powiedzieli inni plakaty, SQLite nie obsługuje tej składni. Nie wiem, czy złożone INSERT są częścią standardu SQL, ale z mojego doświadczenia wynika, że nieone implementowane w wielu produktach.

Nawiasem mówiąc, należy pamiętać, że wydajność INSERT w SQLite znacznie się poprawi, jeśli zawrzesz wiele INSERT w jawnej transakcji.


10

Tak, sql może to zrobić, ale z inną składnią. Sqlite dokumentacja jest dość dobra, tak przy okazji. Informuje również, że jedynym sposobem wstawienia kilku wierszy jest użycie instrukcji select jako źródła danych do wstawienia.


9

Sqlite3 nie może tego zrobić bezpośrednio w SQL, chyba że za pomocą SELECT, i chociaż SELECT może zwrócić „wiersz” wyrażeń, nie wiem, jak sprawić, by zwrócił fałszywą kolumnę.

Jednak CLI może to zrobić:

.import FILE TABLE     Import data from FILE into TABLE
.separator STRING      Change separator used by output mode and .import

$ sqlite3 /tmp/test.db
SQLite version 3.5.9
Enter ".help" for instructions
sqlite> create table abc (a);
sqlite> .import /dev/tty abc
1
2
3
99
^D
sqlite> select * from abc;
1
2
3
99
sqlite> 

Jeśli utworzysz pętlę wokół INSERT, zamiast korzystać z .importpolecenia CLI , postępuj zgodnie ze wskazówkami w sqlite FAQ dla INSERT speed:

Domyślnie każda instrukcja INSERT jest własną transakcją. Ale jeśli otoczysz wiele instrukcji INSERT BEGIN ... COMMIT, wszystkie wstawki zostaną zgrupowane w jedną transakcję. Czas potrzebny na zatwierdzenie transakcji jest amortyzowany przez wszystkie załączone instrukcje insert, dzięki czemu czas na instrukcję insert jest znacznie skrócony.

Inną opcją jest uruchomienie PRAGMA synchronicznie = WYŁ. To polecenie spowoduje, że SQLite nie będzie czekać na dane, które dotrą do powierzchni dysku, co spowoduje, że operacje zapisu będą wydawać się znacznie szybsze. Ale jeśli stracisz moc w trakcie transakcji, plik bazy danych może ulec uszkodzeniu.


Jeśli spojrzysz na kod źródłowy .importpolecenia SQLite , jest to po prostu pętla, odczytująca linię z pliku wejściowego (lub tty), a następnie instrukcja INSERT dla tej linii. Niestety nieznacznie poprawiona wydajność.
Bill Karwin

8

Alex ma rację: instrukcja „wybierz ... związek” utraci kolejność, co jest bardzo ważne dla niektórych użytkowników. Nawet po wstawieniu w określonej kolejności sqlite zmienia rzeczy, więc wolę używać transakcji, jeśli ważna jest kolejność wstawiania.

create table t_example (qid int not null, primary key (qid));
begin transaction;
insert into "t_example" (qid) values (8);
insert into "t_example" (qid) values (4);
insert into "t_example" (qid) values (9);
end transaction;    

select rowid,* from t_example;
1|8
2|4
3|9

8

nieustraszony_fool ma świetną odpowiedź na starsze wersje. Chciałem tylko dodać, że musisz się upewnić, że masz wszystkie kolumny na liście. Więc jeśli masz 3 kolumny, musisz upewnić się, że wybierz działa na 3 kolumny.

Przykład: Mam 3 kolumny, ale chcę wstawić tylko dane o wartości 2 kolumn. Załóżmy, że nie obchodzi mnie pierwsza kolumna, ponieważ jest to standardowy identyfikator liczby całkowitej. Mógłbym zrobić następujące ...

INSERT INTO 'tablename'
      SELECT NULL AS 'column1', 'data1' AS 'column2', 'data2' AS 'column3'
UNION SELECT NULL, 'data3', 'data4'
UNION SELECT NULL, 'data5', 'data6'
UNION SELECT NULL, 'data7', 'data8'

Uwaga: Pamiętaj, że instrukcja „wybierz ... związek” utraci zamówienie. (Od OS1)


7
INSERT INTO TABLE_NAME 
            (DATA1, 
             DATA2) 
VALUES      (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2); 

6

Nie możesz, ale nie sądzę, żebyś za niczym tęsknił.

Ponieważ zawsze wywołujesz sqlite, prawie nie ma znaczenia, czy wykonasz 1 instrukcję insert, czy 100 instrukcji insert. Zatwierdzenie zajmuje jednak dużo czasu, więc umieść te 100 wstawek w transakcji.

Sqlite jest znacznie szybszy, gdy używasz sparametryzowanych zapytań (potrzeba znacznie mniej analizy), więc nie łączę dużych instrukcji takich jak to:

insert into mytable (col1, col2)
select 'a','b'
union 
select 'c','d'
union ...

Muszą być analizowane raz po raz, ponieważ każde zestawione zdanie jest inne.


6

w mysql lite nie można wstawiać wielu wartości, ale można zaoszczędzić czas, otwierając połączenie tylko raz, a następnie wykonując wszystkie wstawienia, a następnie zamykając połączenie. Oszczędza dużo czasu


5

Problem z korzystaniem z transakcji polega na tym, że blokujesz tabelę również do odczytu. Więc jeśli masz naprawdę dużo danych do wstawienia i potrzebujesz dostępu do swoich danych, na przykład podglądu, ten sposób nie działa dobrze.

Problem z innym rozwiązaniem polega na tym, że tracisz kolejność wstawiania

insert into mytable (col)
select 'c'
union 
select 'd'
union 
select 'a'
union 
select 'b';

W sqlite dane będą przechowywane a, b, c, d ...


5

Począwszy od wersji 3.7.11 SQLite obsługuje wstawianie wielu wierszy. Richard Hipp komentuje:

Używam 3.6.13

Polecam w ten sposób:

insert into xtable(f1,f2,f3) select v1 as f1, v2 as f2, v3 as f3 
union select nextV1+, nextV2+, nextV3+

Po wstawieniu 50 rekordów na raz zajmuje to tylko sekundę lub krócej.

To prawda, że ​​użycie sqlite do wstawiania wielu wierszy jednocześnie jest bardzo możliwe. Przez @Andy napisał.

dzięki Andy +1


4
INSERT INTO tabela(coluna1,coluna2) 
SELECT 'texto','outro'
UNION ALL 
SELECT 'mais texto','novo texto';


2

Mam zapytanie jak poniżej, ale ze sterownikiem ODBC SQLite ma błąd z „”, mówi. Uruchamiam vbscript w HTA (aplikacja HTML).

INSERT INTO evrak_ilac_iliskileri (evrak_id, ilac_id, baglayan_kullanici_id, tarih) VALUES (4150,762,1,datetime()),(4150,9770,1,datetime()),(4150,6609,1,datetime()),(4150,3628,1,datetime()),(4150,9422,1,datetime())

2

Na sqlite 3.7.2:

INSERT INTO table_name (column1, column2) 
                SELECT 'value1', 'value1' 
          UNION SELECT 'value2', 'value2' 
          UNION SELECT 'value3', 'value3' 

i tak dalej


2

Jestem w stanie uczynić zapytanie dynamicznym. To jest mój stół:

CREATE TABLE "tblPlanner" ("probid" text,"userid" TEXT,"selectedtime" DATETIME,"plannerid" TEXT,"isLocal" BOOL,"applicationid" TEXT, "comment" TEXT, "subject" TEXT)

i otrzymuję wszystkie dane przez JSON, więc po przejściu wszystkiego do środka postępowałem NSArraynastępująco:

    NSMutableString *query = [[NSMutableString alloc]init];
    for (int i = 0; i < arr.count; i++)
    {
        NSString *sqlQuery = nil;
        sqlQuery = [NSString stringWithFormat:@" ('%@', '%@', '%@', '%@', '%@', '%@', '%@', '%@'),",
                    [[arr objectAtIndex:i] objectForKey:@"plannerid"],
                    [[arr objectAtIndex:i] objectForKey:@"probid"],
                    [[arr objectAtIndex:i] objectForKey:@"userid"],
                    [[arr objectAtIndex:i] objectForKey:@"selectedtime"],
                    [[arr objectAtIndex:i] objectForKey:@"isLocal"],
                    [[arr objectAtIndex:i] objectForKey:@"subject"],
                    [[arr objectAtIndex:i] objectForKey:@"comment"],
                    [[NSUserDefaults standardUserDefaults] objectForKey:@"applicationid"]
                    ];
        [query appendString:sqlQuery];
    }
    // REMOVING LAST COMMA NOW
    [query deleteCharactersInRange:NSMakeRange([query length]-1, 1)];

    query = [NSString stringWithFormat:@"insert into tblPlanner (plannerid, probid, userid, selectedtime, isLocal, applicationid, subject, comment) values%@",query];

I na koniec zapytanie wyjściowe jest następujące:

insert into tblPlanner (plannerid, probid, userid, selectedtime, isLocal, applicationid, subject, comment) values 
<append 1>
('pl1176428260', '', 'US32552', '2013-06-08 12:00:44 +0000', '0', 'subj', 'Hiss', 'ap19788'),
<append 2>
('pl2050411638', '', 'US32552', '2013-05-20 10:45:55 +0000', '0', 'TERI', 'Yahoooooooooo', 'ap19788'), 
<append 3>
('pl1828600651', '', 'US32552', '2013-05-21 11:33:33 +0000', '0', 'test', 'Yest', 'ap19788'),
<append 4>
('pl549085534', '', 'US32552', '2013-05-19 11:45:04 +0000', '0', 'subj', 'Comment', 'ap19788'), 
<append 5>
('pl665538927', '', 'US32552', '2013-05-29 11:45:41 +0000', '0', 'subj', '1234567890', 'ap19788'), 
<append 6>
('pl1969438050', '', 'US32552', '2013-06-01 12:00:18 +0000', '0', 'subj', 'Cmt', 'ap19788'),
<append 7>
('pl672204050', '', 'US55240280', '2013-05-23 12:15:58 +0000', '0', 'aassdd', 'Cmt', 'ap19788'), 
<append 8>
('pl1019026150', '', 'US32552', '2013-06-08 12:15:54 +0000', '0', 'exists', 'Cmt', 'ap19788'), 
<append 9>
('pl790670523', '', 'US55240280', '2013-05-26 12:30:21 +0000', '0', 'qwerty', 'Cmt', 'ap19788')

który działa również dobrze przez kod i jestem w stanie pomyślnie zapisać wszystko w SQLite.

Wcześniej sprawiłem, że UNIONzapytania były dynamiczne, ale zaczęło to dawać jakiś błąd składniowy. W każdym razie działa to dobrze dla mnie.


2

Dziwi mnie, że nikt nie wspomniał o przygotowanych oświadczeniach . O ile nie używasz SQL samodzielnie, a nie w żadnym innym języku, pomyślałem, że przygotowane instrukcje zawinięte w transakcję byłyby najbardziej efektywnym sposobem wstawiania wielu wierszy.


1
Przygotowane oświadczenia są zawsze dobrym pomysłem, ale wcale nie są związane z pytaniem, jakie stawia OP. Pyta, jaka jest podstawowa składnia wstawiania wielu danych do jednej instrukcji.
Lakey,


-1

Jeśli używasz powłoki bash, możesz użyć tego:

time bash -c $'
FILE=/dev/shm/test.db
sqlite3 $FILE "create table if not exists tab(id int);"
sqlite3 $FILE "insert into tab values (1),(2)"
for i in 1 2 3 4; do sqlite3 $FILE "INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5"; done; 
sqlite3 $FILE "select count(*) from tab;"'

Lub jeśli korzystasz z sqlite CLI, musisz to zrobić:

create table if not exists tab(id int);"
insert into tab values (1),(2);
INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5;
INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5;
INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5;
INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5;
select count(*) from tab;

Jak to działa? Wykorzystuje to, jeśli tabela tab:

id int
------
1
2

następnie select a.id, b.id from tab a, tab bwraca

a.id int | b.id int
------------------
    1    | 1
    2    | 1
    1    | 2
    2    | 2

i tak dalej. Po pierwszym wykonaniu wstawiamy 2 wiersze, a następnie 2 ^ 3 = 8. (trzy, ponieważ mamy tab a, tab b, tab c)

Po drugim wykonaniu wstawiamy dodatkowe (2+8)^3=1000 wiersze

Następnie wstawiamy max(1000^3, 5e5)=500000wiersze i tak dalej ...

Jest to najszybsza znana mi metoda zapełniania bazy danych SQLite.


2
To nie działa, jeśli chcesz wstawić przydatne dane.
CL.

@CL. to nie prawda. możesz mieszać z losowymi datami i identyfikatorami, jak chcesz.
test30
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.