是否可以一次在SQLite数据库中插入多行?


551

在MySQL中,您可以像这样插入多行:

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

但是,尝试执行此类操作时出现错误。是否可以一次在SQLite数据库中插入多行?这样做的语法是什么?


26
这不是一个骗子,因为这个问题专门针对SQLite,而不是一般的SQL(尽管该问题的一些答案对此很有帮助)。
布赖恩·坎贝尔


5
问题是为什么要这样做。由于SQlite是在同一台计算机上进行的,因此您可以将多个插入内容包装到事务中,我认为不需要吗?
andig 2012年

2
是的,从版本2012-03-20(3.7.11)开始,支持您的语法。
mjb

Answers:


607

更新

正如BrianCampbell在此处指出的那样SQLite 3.7.11及更高版本现在支持原始文章的更简单语法。但是,如果您希望在旧数据库之间实现最大兼容性,则所示的方法仍然适用。

原始答案

如果有特权,我会激怒River的回答:您可以在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有效地加载大型数据集。 但是正如Jaime Cook所指出的那样,尚不清楚这是否可以更快地将INSERTs单个交易包装到单个交易中:

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

如果效率是您的目标,则应首先尝试这样做。

关于UNION vs UNION ALL的注释

正如几个人评论的那样,如果您使用UNION ALL(如上所示),将插入所有行,因此,在这种情况下,您将获得4行data1, data2。如果您省略ALL,则将消除重复的行(并且该操作可能会慢一些)。我们正在使用UNION ALL,因为它与原始帖子的语义更加匹配。

在结束时

PS:请+1 River的回复,因为它首先提出了解决方案。


102
作为进一步的说明,只源码似乎支持高达每查询500种这样的工会选择,所以如果你想在更多的数据比扔,你将需要它分解成500元块(sqlite.org/limits.html
杰米·库克(Jamie Cook)

3
同意:SQLite不是瓶颈,而是单个ORM事务的开销(在我的例子中是Ruby On Rails)。因此,这仍然是一个巨大的胜利。
fearless_fool 2013年

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;
2014年

5
进一步说明:小心!此语法删除重复的行!使用UNION ALL以避免(或一些小的性能提升)。
chacham15年

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。除非您要删除重复项。
Benoit 2012年

4
如果您希望ID自动递增,请为它们提供一个NULL值。
lenooh

241

是的,从SQLite 3.7.11开始,SQLite 支持此功能。从SQLite文档中

SQLite INSERT语句语法

(最初写此答案时,不支持此答案)

为了与旧版本的SQLite的兼容性,可以使用所建议的伎俩安迪fearless_fool使用UNION,但对于3.7.11,后来在这里所描述的简单的语法应该是首选。


3
我会说图表允许多行,因为在后面的圆括号外有一个闭环VALUES
Johannes Gerer 2012年

@JohannesGerer自从我嵌入图像以来,他们已经更新了该图像。当我将其嵌入Internet存档时,您可以看到该图。实际上,仅仅两个月前,他们在插入中添加了对多行的支持,而仅仅两天前,他们发布了具有此更改的版本。我将相应地更新答案。
Brian Campbell

1
@Brian,能否请您引用SQLite文档文字,指出这可能的?我重读了3次insert docs,却发现没有关于插入多行的任何信息,但只发现了这张图片(也没有关于逗号的信息VALUES (...), (...)):(
Prizoff 2012年

1
@Prizoff我链接到添加了此支持的提交,包括测试用例。您可以在图中看到(比较IA链接),之后的表达式周围有一个循环VALUES,表示可以将其重复并用逗号分隔。然后,我链接到添加该功能的版本的发行说明,其中指出“增强INSERT语法以允许通过VALUES子句插入多行。”
布莱恩·坎贝尔

1
@Prizoff我向SQLite维护人员提到了这一点,他已经提交了一份修订该修订可以在文档草案中找到。我猜它会在下一个版本的正式文档中发布。
布莱恩·坎贝尔

57

我编写了一些ruby代码,从一系列插入语句生成一个500元素的多行插入,这比运行单个插入要快得多。然后,我尝试将多个插入内容包装到一个事务中,发现用更少的代码即可获得相同的速度。

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

1
但这在代码中不起作用,而直接在SQLite Manager中起作用。在代码中,它仅插入第一行:(
Vaibhav Saran

4
我在代码中使用了这种插入样式,因此效果很好。您只需要确保立即提交所有SQL。这对我来说是一个巨大的速度提升,但是我很好奇,如果接受的答案是更快还是更慢,那么这呢?

当在单个事务中修改多个表时,这种方法的伸缩性非常好,我发现自己在批量加载数据库时经常这样做。
弗兰克

请注意,如果需要使用绑定,此方法将不起作用。SQLite3引擎将假定您的所有绑定都将应用在第一条语句上,而忽略以下语句。有关更多详细说明,请参见此SO问题
菲利普·赫伯特

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 确实支持multi-row-insert。理查德·希普评论:

“新的多值插入仅仅是复合插入的语法求和(sic)。一种或另一种方式没有性能优势。”


一种方法没有性能优势。-你能告诉我你在哪里看到这句话吗?我在任何地方都找不到。
Alix Axel

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

附言:请+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命令,那么请确保遵循sqlite FAQ中的建议以提高INSERT速度:

默认情况下,每个INSERT语句都是其自己的事务。但是,如果用BEGIN ... COMMIT包围多个INSERT语句,则所有插入都被分组为一个事务。提交事务所需的时间在所有随附的插入语句中摊销,因此每个插入语句的时间大大减少。

另一种选择是运行PRAGMA同步= OFF。此命令将导致SQLite不等待数据到达磁盘表面,这将使写入操作显得更快。但是,如果您在事务处理过程中断电,则数据库文件可能会损坏。


如果您查看SQLite .import命令的源代码,那只是一个循环,从输入文件(或tty)中读取一行,然后从该行读取INSERT语句。不幸的是效率没有明显提高。
Bill Karwin

8

Alex是正确的:“ 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

fearless_fool对于旧版本有很好的答案。我只是想补充一下,您需要确保列出了所有列。因此,如果您有3列,则需要确保select对3列起作用。

示例:我有3列,但我只想插入2列的数据。假设我不在乎第一列,因为它是一个标准的整数ID。我可以做以下事情...

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


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


2

我有一个类似下面的查询,但是使用ODBC驱动程序SQLite时出现“”错误,它说。我在HTA(HTML应用程序)中运行vbscript。

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,因此在将所有内容都放入a 之后,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
准备好的语句始终是个好主意,但与OP提出的问题完全无关。他在问在一条语句中插入多个数据的基本语法是什么。
莱基,2015年


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

它是如何工作的?它利用了if表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

之后,我们插入大约max(1000^3, 5e5)=500000行,依此类推...

这是对我来说最快的填充SQLite数据库的方法。


2
如果要插入有用的数据,则此方法不起作用。
CL。

@CL。这不是真的。您可以根据需要将其与随机日期和ID混合使用。
test30 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.