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

Is it possible to insert multiple rows at a time in an SQLite database?

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

1
2
3
4
5
INSERT INTO 'tablename' ('column1', 'column2') VALUES
    ('data1', 'data2'),
    ('data1', 'data2'),
    ('data1', 'data2'),
    ('data1', 'data2');

但是,当我尝试这样做时,我会得到一个错误。是否可以在一个sqlite数据库中一次插入多行?这样做的语法是什么?


更新

正如Briancampbell在这里指出的,sqlite 3.7.11及更高版本现在支持原始日志的简单语法。但是,如果您希望跨遗留数据库实现最大的兼容性,那么所显示的方法仍然适用。

原始答案

如果我有特权,我会打断安迪的回答:您可以在sqlite中插入多行,您只需要不同的语法。为了非常清楚,ops mysql示例:

1
2
3
4
5
INSERT INTO 'tablename' ('column1', 'column2') VALUES
  ('data1', 'data2'),
  ('data1', 'data2'),
  ('data1', 'data2'),
  ('data1', 'data2');

可以将其重新转换为sqlite,如下所示:

1
2
3
4
5
     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'

关于表演的说明

我最初使用这种技术来有效地从RubyonRails加载大型数据集。然而,正如Jaime Cook指出的,不清楚这是否能更快地将单个INSERTs包装在单个交易中:

1
2
3
4
5
BEGIN TRANSACTION;
INSERT INTO 'tablename' TABLE VALUES ('data1', 'data2');
INSERT INTO 'tablename' TABLE VALUES ('data3', 'data4');
...
COMMIT;

如果效率是你的目标,你应该先试试这个。

关于"联合"与"联合所有人"的注释

正如一些人所评论的,如果您使用UNION ALL(如上所示),所有行都将被插入,因此在本例中,您将得到四行data1, data2。如果省略了ALL,那么将消除重复的行(操作可能会慢一点)。我们正在使用union all,因为它更接近于原始post的语义。

合拢

附言:请+1安迪的回复,不是我的!他先提出了解决办法。


是的,这是可能的,但不能用逗号分隔的插入值。

试试这个…

1
2
3
4
INSERT INTO myTable (col1,col2)
     SELECT aValue AS col1,anotherValue AS col2
     UNION SELECT moreValue,evenMoreValue
     UNION...

是的,它有点难看,但很容易从一组值自动生成语句。此外,您似乎只需要在第一次选择中声明列名。


是的,从sqlite 3.7.11开始,它在sqlite中受支持。从sqlite文档中:

SQLite INSERT statement syntax

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

为了与旧版本的sqlite兼容,您可以使用安迪和无畏的傻瓜使用UNION所建议的技巧,但对于3.7.11及更高版本,应首选此处描述的简单语法。


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

1
2
3
4
5
BEGIN TRANSACTION;
INSERT INTO TABLE VALUES (1,1,1,1);
INSERT INTO TABLE VALUES (2,2,2,2);
...
COMMIT;


根据此页面,不支持:

  • 2007-12-03 : Multi-row INSERT a.k.a. compound INSERT not supported.
1
2
  INSERT INTO TABLE (col1, col2) VALUES
      ('row1col1', 'row1col2'), ('row2col1', 'row2col2'), ...

Actually, according to the SQL92 standard, a VALUES expression should be able to stand on itself. For example, the following should return a one-column table with three rows: VALUES 'john', 'mary', 'paul';

从3.7.11版开始,sqlite支持多行插入。Richard Hipp评论:

"The new multi-valued insert is merely syntactic suger (sic) for the compound
insert. There is no performance advantage one way or the other."


从版本2012-03-20(3.7.11)开始,sqlite支持以下插入语法:

1
2
3
4
5
INSERT INTO 'tablename' ('column1', 'column2') VALUES
  ('data1', 'data2'),
  ('data3', 'data4'),
  ('data5', 'data6'),
  ('data7', 'data8');

阅读文档:http://www.sqlite.org/lang_insert.html

附言:请+1到布莱恩坎贝尔的答复/回答。不是我的!他先提出了解决办法。


正如其他海报所说,sqlite不支持这种语法。我不知道复合插入是否是SQL标准的一部分,但根据我的经验,它们并没有在许多产品中实现。

另外,您应该知道,如果在一个显式事务中包装多个插入,那么SQLite中的插入性能会大大提高。


是的,SQL可以这样做,但使用不同的语法。顺便说一下,sqlite文档非常好。它还将告诉您插入多行的唯一方法是使用select语句作为要插入的数据源。


除了通过select,sqlite3不能在SQL中直接执行此操作,尽管select可以返回表达式的"行",但我不知道如何使它返回虚假的列。

但是,CLI可以做到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.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>

如果您确实围绕插入放置了一个循环,而不是使用cli .import命令,那么一定要遵循sqlite faq中有关插入速度的建议:

By default, each INSERT statement is
its own transaction. But if you
surround multiple INSERT statements
with BEGIN...COMMIT then all the
inserts are grouped into a single
transaction. The time needed to commit
the transaction is amortized over all
the enclosed insert statements and so
the time per insert statement is
greatly reduced.

Another option is to run PRAGMA
synchronous=OFF. This command will
cause SQLite to not wait on data to
reach the disk surface, which will
make write operations appear to be
much faster. But if you lose power in
the middle of a transaction, your
database file might go corrupt.


亚历克斯是对的:"选择……union语句将丢失对某些用户非常重要的排序。即使以特定的顺序插入,如果插入顺序很重要,SQLite也会更改内容,因此更喜欢使用事务。

1
2
3
4
5
6
7
8
9
10
11
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

无畏的傻瓜对旧版本有很好的答案。我只是想补充一下,您需要确保列出了所有列。因此,如果有3列,则需要确保选择"对3列执行"。

示例:我有3列,但我只想插入值为2列的数据。假设我不关心第一列,因为它是一个标准的整数ID。我可以执行以下操作…

1
2
3
4
5
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'

注意:记住"选择…"union"语句将丢失命令。(来自AG1)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
INSERT INTO TABLE_NAME
            (DATA1,
             DATA2)
VALUES      (VAL1,
             VAL2),
            (VAL1,
             VAL2),
            (VAL1,
             VAL2),
            (VAL1,
             VAL2),
            (VAL1,
             VAL2),
            (VAL1,
             VAL2),
            (VAL1,
             VAL2),
            (VAL1,
             VAL2);

你不能,但我不认为你错过了什么。

因为您总是在进程中调用sqlite,所以无论是执行1个insert语句还是100个insert语句,在性能上都几乎没有关系。然而,提交需要很多时间,所以将这100个插入放到事务中。

当您使用参数化查询时,sqlite速度要快得多(需要的解析要少得多),因此我不会像下面这样连接大型语句:

1
2
3
4
5
INSERT INTO mytable (col1, col2)
SELECT 'a','b'
UNION
SELECT 'c','d'
UNION ...

它们需要一次又一次地被解析,因为每个连接的语句都是不同的。


在mysql lite中,不能插入多个值,但您可以通过只打开一次连接,然后执行所有插入,然后关闭连接来节省时间。它节省了很多时间


使用事务的问题是,您也为读取而锁定了表。因此,如果您有很多数据需要插入,并且需要访问您的数据,例如预览,那么这种方法不太管用。

另一个解决方案的问题是您丢失了插入的顺序

1
2
3
4
5
6
7
8
INSERT INTO mytable (col)
SELECT 'c'
UNION
SELECT 'd'
UNION
SELECT 'a'
UNION
SELECT 'b';

在sqlite中,数据将存储在a、b、c、d…


As of version 3.7.11 SQLite does support multi-row-insert. Richard
Hipp comments:

我用的是3.6.13

我命令如下:

1
2
INSERT INTO xtable(f1,f2,f3) SELECT v1 AS f1, v2 AS f2, v3 AS f3
UNION SELECT nextV1+, nextV2+, nextV3+

一次插入50条记录,只需一秒钟或更短的时间。

使用sqlite一次插入多行是很有可能的。由安迪写道。

谢谢安迪+ 1


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

如果您使用sqlite manager firefox插件,它支持从INSERTsql语句进行大容量插入。

事实上,它不支持这一点,但sqlite浏览器支持这一点(适用于Windows、OS X、Linux)


我有一个如下的查询,但是对于ODBC驱动程序,sqlite有一个错误,它说。我在HTA(HTML应用程序)中运行vbscript。

1
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())


在sqlite 3.7.2上:

1
2
3
4
INSERT INTO TABLE_NAME (column1, column2)
                SELECT 'value1', 'value1'
          UNION SELECT 'value2', 'value2'
          UNION SELECT 'value3', 'value3'

等等


我能够使查询成为动态的。这是我的桌子:

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

我通过一个JSON获取所有数据,所以在获取NSArray中的所有内容之后,我遵循以下步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    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];

最后,输出查询是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
INSERT INTO tblPlanner (plannerid, probid, userid, selectedtime, isLocal, applicationid, subject, comment) VALUES

('pl1176428260', '', 'US32552', '2013-06-08 12:00:44 +0000', '0', 'subj', 'Hiss', 'ap19788'),

('pl2050411638', '', 'US32552', '2013-05-20 10:45:55 +0000', '0', 'TERI', 'Yahoooooooooo', 'ap19788'),

('pl1828600651', '', 'US32552', '2013-05-21 11:33:33 +0000', '0', 'test', 'Yest', 'ap19788'),

('pl549085534', '', 'US32552', '2013-05-19 11:45:04 +0000', '0', 'subj', 'Comment', 'ap19788'),

('pl665538927', '', 'US32552', '2013-05-29 11:45:41 +0000', '0', 'subj', '1234567890', 'ap19788'),

('pl1969438050', '', 'US32552', '2013-06-01 12:00:18 +0000', '0', 'subj', 'Cmt', 'ap19788'),

('pl672204050', '', 'US55240280', '2013-05-23 12:15:58 +0000', '0', 'aassdd', 'Cmt', 'ap19788'),

('pl1019026150', '', 'US32552', '2013-06-08 12:15:54 +0000', '0', 'exists', 'Cmt', 'ap19788'),

('pl790670523', '', 'US55240280', '2013-05-26 12:30:21 +0000', '0', 'qwerty', 'Cmt', 'ap19788')

它在代码中也运行得很好,我可以成功地将所有内容保存在sqlite中。

在此之前,我使UNION查询工具成为动态的,但这开始产生一些语法错误。不管怎样,这对我来说很好。


我很惊讶没有人提到准备好的声明。除非您单独使用SQL,而不是在任何其他语言中使用SQL,否则我认为在事务中包装的准备好的语句将是插入多行的最有效方法。


你可以用插入器,既方便又快捷

文档: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/

编辑:从API级别17开始,不推荐使用InsertHelper。


如果您使用的是bash shell,那么可以使用它:

1
2
3
4
5
6
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中,则需要执行以下操作:

1
2
3
4
5
6
7
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中有:

1
2
3
4
id INT
------
1
2

然后select a.id, b.id from tab a, tab b返回

1
2
3
4
5
6
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

第三次插入后,我们插入了大约4行,以此类推…

这是填充sqlite数据库的最快方法。