Чи можливо вставити кілька рядків одночасно в базу даних SQLite?


551

У MySQL ви можете вставити кілька рядків так:

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

Однак я отримую помилку, коли намагаюся зробити щось подібне. Чи можливо вставити кілька рядків одночасно в базу даних SQLite? Який синтаксис для цього робити?


26
Це не дурень, оскільки це питання задає конкретно SQLite, на відміну від SQL взагалі (хоча деякі відповіді на це питання корисні для цього).
Брайан Кемпбелл

5
На об'ємних вставок: stackoverflow.com/questions/1711631 / ...
tuinstoel

5
Питання в тому, чому ви повинні це робити. Оскільки SQlite працює на одній машині, і ви можете зафіксувати кілька вставок для транзакції, я не бачу необхідності?
andig

2
Так, почніть з версії 2012-03-20 (3.7.11), ваш синтаксис підтримується.
mjb

Відповіді:


607

оновлення

Як BrianCampbell вказує тут , SQLite 3.7.11 і вище тепер підтримує більш простий синтаксис вихідної публікації . Однак показаний підхід все-таки підходить, якщо ви хочете отримати максимальну сумісність у застарілих базах даних.

оригінальна відповідь

Якби у мене були привілеї, я б зіткнувся з річковою відповіддю : Ви можете вставити кілька рядків у SQLite, вам просто потрібен інший синтаксис . Щоб зробити це абсолютно зрозумілим, приклад OPS MySQL:

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

Це може бути перероблено на SQLite у вигляді:

     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'

примітка про виконання

Я спочатку використовував цю методику для ефективного завантаження великих наборів даних від Ruby on Rails. Однак , як вказує Хайме Кук , не ясно, що це швидше обгортання особи INSERTsв рамках однієї транзакції:

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

Якщо ефективність - ваша мета, спершу слід спробувати це.

примітка про UNION vs UNION ALL

Як коментували кілька людей, якщо ви користуєтесь UNION ALL(як показано вище), всі рядки будуть вставлені, тож у цьому випадку ви отримаєте чотири рядки data1, data2. Якщо ви опустите цей пункт ALL, то повторювані рядки будуть усунені (а операція, мабуть, буде трохи повільнішою). Ми використовуємо UNION ALL, оскільки він більше відповідає семантиці оригіналу публікації.

у закритті

PS: Будь ласка , відповідь Річки , оскільки вона спочатку представила рішення.


102
Як додаткове зауваження, sqlite, здається, підтримує до 500 таких об'єднань, що вибираються за запит, тому якщо ви намагаєтеся ввести більше даних, ніж вам, вам потрібно буде розбити їх на 500 елементів елементів ( sqlite.org/limits.html )
Джеймі Кук

3
Погоджено: SQLite - це не вузьке місце, це накладні витрати на окремі трансакції ORM (у моєму випадку Ruby On Rails). Тож це все-таки велика перемога.
безстрашний_фул

3
Як це рішення чи рішення 3.7.11 порівнюють із використанням транзакційних блоків? швидше insert into t values (data),(data),(data)чи begin transaction; insert into t values (data);insert into t values (data);insert into t values (data);Commit;?
День

5
В якості додаткової примітки: будьте обережні! цей синтаксис видаляє повторювані рядки! використовувати, UNION ALLщоб уникнути цього (або для незначного підвищення продуктивності).
чача15

1
@Shadowfax, щоб перевірити версію SQLite, подивіться sqlite3.sqlite_version(вона повинна бути 3.7.x або 3.8.x), а не sqlite3.version(що є лише версією модуля python).
макс

562

Так, це можливо, але не зі звичайними значеннями вставки, розділеними комами.

Спробуйте це...

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

Так, це трохи некрасиво, але досить просто, щоб автоматизувати генерацію оператора з набору значень. Крім того, видається, що вам потрібно лише оголосити назви стовпців під час першого вибору.


36
будь ласка, UNION ALLне використовуйте UNION. За винятком випадків, коли ви хочете видалити дублікати.
Бенуа

4
Якщо ви хочете, щоб ідентифікатори автоматично збільшувались, надайте їм значення NULL.
lenooh

241

Так, для SQLite 3.7.11 це підтримується в SQLite. З документації на SQLite :

Синтаксис оператора SQLite INSERT

(коли ця відповідь була написана спочатку, її не підтримували)

Для сумісності з попередніми версіями SQLite, ви можете використовувати трюк , запропонований Енді і fearless_fool використання UNION, але і для 3.7.11 і пізніше простий синтаксис описаний тут повинен бути кращим.


3
Я б сказав, що діаграма допускає декілька рядків, оскільки після дужок є закритий цикл із комою VALUES.
Йоханнес Герер

@JohannesGerer Вони оновили це зображення, оскільки я вклав його. Ви можете побачити схему в той час, коли я вклав її в Інтернет-архів . Насправді, саме два місяці тому вони додали підтримку декількох рядків у вставці, і лише два дні тому вони випустили версію з цією зміною. Відповідно оновлю відповідь.
Брайан Кемпбелл

1
@ Брайан, НЕ могли б ви процитувати текст SQLite документації, що держави , що це IS можливо? Я перечитав вставку документів 3 рази і нічого не знайшов про вставлення декількох рядків, але лише цю картинку (і нічого про кому VALUES (...), (...)) :(
Prizoff

1
@Prizoff Я пов'язував із зобов’язанням, у який додано цю підтримку , включаючи тестові приклади. Ви можете побачити на діаграмі (порівняйте посилання ІА ), що навколо виразу після цього існує цикл VALUES, що вказує на те, що його можна повторити, розділившись комами. І я пов’язаний із примітками до випуску до версії, що додає функцію, в якій зазначено "Удосконалити синтаксис INSERT, щоб дозволити вставити декілька рядків за допомогою пункту VALUES".
Брайан Кемпбелл

1
@Prizoff Я згадав про це для технічного обслуговування SQLite, він вчинив виправлення, яке є в проекті документації . Я здогадуюсь, що це буде в офіційній документації з наступного випуску.
Брайан Кемпбелл

57

Я написав рубіновий код, щоб генерувати один рядковий багаторядковий вкладку з 500 елементів із серії операторів вставки, що було значно швидше, ніж виконання окремих вставок. Тоді я спробував просто обернути декілька вставок в одну транзакцію і виявив, що я можу отримати однаковий швидкість зі значно меншим кодом.

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

1
але це не спрацювало в коді, поки він працює безпосередньо в SQLite Manager. У код він вставляє лише 1-й рядок :(
Вайбхав Саран

4
Я використовую цей стиль вставки у своєму коді, і він відмінно працює. Вам потрібно лише переконатися, що ви надсилаєте ВСЕ SQL відразу. Це було величезне збільшення швидкості для мене, але мені цікаво, якщо прийнята відповідь буде швидшою чи повільнішою, ніж це?
День

Цей підхід дійсно добре масштабує при зміні декількох таблиць в межах однієї транзакції, що, як я вважаю, часто роблю під час завантаження пакетної бази даних.
Френк

Зверніть увагу, що такий підхід не буде працювати, якщо вам потрібно використовувати прив'язки . SQLite3 Engine передбачає, що всі ваші прив'язки повинні застосовуватися до першого оператора, і ігнорує наступні оператори. Дивіться це питання ТА для більш детального пояснення.
Філіп Хеберт

38

Відповідно до цієї сторінки вона не підтримується:

  • 2007-12-03: багаторядковий INSERT aka сполука INSERT не підтримується.
  INSERT INTO table (col1, col2) VALUES 
      ('row1col1', 'row1col2'), ('row2col1', 'row2col2'), ...

Насправді, відповідно до стандарту SQL92, вираз VALUES повинен мати можливість стояти на собі. Наприклад, слід повернути таблицю з одним стовпцем з трьома рядками:VALUES 'john', 'mary', 'paul';

У версії 3.7.11 SQLite робить підтримку багаторядну-вставку . Річард Хіпп коментує:

"Нова багатозначна вставка - це просто синтаксичний suger (sic) для складеної вставки. Немає жодної переваги щодо продуктивності в тому чи іншому випадку."


Немає переваги в продуктивності в тому чи іншому випадку. - Чи не могли б ви сказати мені, де ви бачили це зауваження? Я не міг його знайти ніде.
Алікс Аксель

15

Почніть з версії 2012-03-20 (3.7.11), sqlite підтримує наступний синтаксис INSERT:

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

Прочитайте документацію: http://www.sqlite.org/lang_insert.html

PS: Будь ласка, +1 до відповіді / відповіді Брайана Кемпбелла. не моє! Він подав рішення першим.


10

Як сказали інші афіші, SQLite не підтримує цей синтаксис. Я не знаю, чи є складові INSERT частиною стандарту SQL, але, на мій досвід, це не так реалізовані у багатьох продуктах.

Крім того, ви повинні пам'ятати, що продуктивність INSERT у SQLite значно покращується, якщо ви впакуєте декілька INSERT в явну транзакцію.


10

Так, sql може це зробити, але з іншим синтаксисом. До речі, документація sqlite досить гарна. Це також скаже вам, що єдиним способом вставити кілька рядків є використання оператора select як джерела даних, які потрібно вставити.


9

Sqlite3 не може цього робити безпосередньо в SQL, за винятком SELECT, і хоча SELECT може повернути "рядок" виразів, я не знаю жодного способу змусити його повернути фальшивий стовпчик.

Однак CLI може це зробити:

.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> 

Якщо ви поставите цикл навколо INSERT, а не використовуєте команду CLI .import, то не забудьте дотримуватися порад у FAQ FAQ для швидкості INSERT:

За замовчуванням кожен оператор INSERT є власною транзакцією. Але якщо ви оточуєте кілька операторів INSERT за допомогою BEGIN ... COMMIT, то всі вставки згруповані в одну транзакцію. Час, необхідний для здійснення транзакції, амортизується за всіма доданими виписками-вкладишами, і таким чином час на оператор вставки значно скорочується.

Інший варіант - запустити PRAGMA синхронно = OFF. Ця команда призведе до того, що SQLite не чекатиме, коли дані досягнуть поверхні диска, що зробить операції запису набагато швидшими. Але якщо ви втратите владу в середині транзакції, файл вашої бази може пошкодитися.


Якщо ви подивитеся на вихідний код .importкоманди SQLite , це просто цикл, зчитування рядка з вхідного файлу (або tty), а потім вираз INSERT для цього рядка. На жаль, не суттєво поліпшена ефективність.
Білл Карвін

8

Алекс правильний: заява "select ... union" втратить замовлення, що дуже важливо для деяких користувачів. Навіть коли ви вставляєте певний порядок, sqlite змінює речі, тому вважає за краще використовувати транзакції, якщо важливе впорядкування.

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

Безстрашний_фол має чудову відповідь для старих версій. Я просто хотів додати, що вам потрібно переконатися, що у вас є всі перелічені стовпці. Тож якщо у вас є 3 стовпчики, вам потрібно переконатися, що вибираєте акти на 3 стовпці.

Приклад: у мене є 3 стовпці, але я хочу лише вставити 2 стовпці, варті даних. Припустимо, мені не байдуже перший колонка, оскільки це стандартний цілий ідентифікатор. Я міг би зробити наступне ...

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'

Примітка. Пам'ятайте, що заявка "select ... union" втратить впорядкування. (Від AG1)


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

Ви не можете, але я не думаю, що ви нічого не сумуєте.

Оскільки ви завжди викликаєте sqlite в процесі роботи, це майже не має значення для продуктивності виконання 1 оператора вставки або 100 операторів вставки. Однак фіксація займає багато часу, тому помістіть ці 100 вставок всередину транзакції.

Sqlite набагато швидше, коли ви використовуєте параметризовані запити (потрібен набагато менший синтаксичний аналіз), тому я б не поєднував великі оператори, як це:

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

Їх потрібно розбирати знову і знову, тому що кожне об'єднане твердження різне.


6

у mysql lite ви не можете вставити кілька значень, але ви можете заощадити час, відкривши з'єднання лише один раз, а потім виконавши всі вставки та закривши з'єднання. Це економить багато часу


5

Проблема використання транзакції полягає в тому, що ви блокуєте таблицю також для читання. Тож якщо у вас дійсно багато даних для вставки і вам потрібно отримати доступ до своїх даних, наприклад, для попереднього перегляду чи так, цей спосіб не працює добре.

Проблема іншого рішення полягає в тому, що ви втрачаєте порядок вставки

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

У sqlite дані будуть зберігатися a, b, c, d ...


5

Станом на версію 3.7.11 SQLite підтримує вкладку з декількома рядками. Річард Хіпп коментує:

Я використовую 3.6.13

Я командую так:

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

Із 50 вставлених записів одночасно потрібно лише секунду чи менше.

Це правда за допомогою sqlite для вставки кількох рядків одночасно дуже можливо. Автор @Andy написав.

дякую Енді +1



2

Якщо ви використовуєте плагін Sqlite manager firefox, він підтримує масові вставки з INSERTоператорів SQL.

Infact не підтримує це, але браузер Sqlite робить (працює в Windows, OS X, Linux)


2

У мене є запит, як показано нижче, але у драйвера ODBC у SQLite є помилка з "", він говорить. Я запускаю vbscript у HTA (додаток 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

На sqlite 3.7.2:

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

і так далі


2

Я в змозі зробити запит динамічним. Це мій стіл:

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

і я отримую всі дані через JSON, тож після отримання всього всередині NSArrayя слідував за цим:

    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];

І нарешті вихідний запит такий:

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')

який також добре працює через код, і я в змозі успішно зберегти все в SQLite.

До цього я робив UNIONзапити динамічними, але це почало давати деяку синтаксичну помилку. У будь-якому випадку це для мене працює добре.


2

Я здивований, що ніхто не згадав підготовлені заяви . Якщо ви не використовуєте SQL самостійно, а не на будь-якій іншій мові, я б вважав, що підготовлені заяви, обернені транзакцією , будуть найбільш ефективним способом вставлення декількох рядків.


1
Підготовлені заяви завжди є гарною ідеєю, але зовсім не пов'язані з питанням, яке задає ОП. Він запитує, що є основним синтаксисом для вставки декількох даних в одне твердження.
Лейкі

0

ви можете використовувати InsertHelper, це легко і швидко

документація: http://developer.android.com/reference/android/database/DatabaseUtils.InsertHelper.html

підручник: http://www.outofwhatbox.com/blog/2010/12/android-using-databaseutils-inserthelper-for-faster-insertions-into-sqlite-database/

Редагувати: InsertHelper застарілий на рівні 17 API


3
InsertHelper застаріло з API 17
GregM

-1

Якщо ви використовуєте bash shell, ви можете використовувати це:

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;"'

Або якщо ви перебуваєте в sqlite CLI, вам потрібно зробити це:

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;

Як це працює? Він використовує це, якщо таблиця tab:

id int
------
1
2

потім select a.id, b.id from tab a, tab bповертається

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

і так далі. Після першого виконання вставляємо 2 ряди, потім 2 ^ 3 = 8. (три, бо маємоtab a, tab b, tab c )

Після другого виконання вставляємо додаткові (2+8)^3=1000рядки

Aftern thrid ми вставляємо про max(1000^3, 5e5)=500000рядки і так далі ...

Це найшвидший відомий мені метод заповнення бази даних SQLite.


2
Це не працює, якщо ви хочете вставити корисні дані.
ЗР.

@CL. це неправда. ви можете змішати його з випадковими датами та ідентифікаторами, як тільки ви хочете.
test30
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.