【问题标题】:Is it possible to insert multiple rows at a time in an SQLite database?是否可以在 SQLite 数据库中一次插入多行?
【发布时间】:2010-12-09 05:45:57
【问题描述】:

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

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

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

【问题讨论】:

  • 不是骗子,因为这个问题专门询问 SQLite,而不是一般的 SQL(尽管该问题的一些答案对这个问题有帮助)。
  • 问题是你为什么要这样做。由于 SQlite 在同一台机器上进行中,您可以将多个插入包装到一个事务中,我认为没有必要?
  • 是的,从版本 2012-03-20 (3.7.11) 开始,支持您的语法。

标签: sql sqlite syntax


【解决方案1】:

更新

作为BrianCampbell points out hereSQLite 3.7.11 及更高版本现在支持原始帖子的更简单语法。但是,如果您希望在旧数据库之间实现最大兼容性,则显示的方法仍然适用。

原答案

如果我有特权,我会撞river's reply:你可以在 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 有效地加载大型数据集。 但是as Jaime Cook points out,尚不清楚这是否会更快地在单个事务中包装个人 INSERTs

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

如果效率是您的目标,您应该先尝试一下。

关于 UNION 与 UNION ALL 的说明

正如一些人评论的那样,如果你使用UNION ALL(如上所示),所有行都会被插入,所以在这种情况下,你会得到四行data1, data2。如果省略ALL,则会消除重复的行(并且操作可能会慢一些)。我们使用 UNION ALL 是因为它更接近原始帖子的语义。

结束时

P.S.:请 +1 river's reply,因为它首先提出了解决方案。

【讨论】:

  • 进一步说明,sqlite 似乎只支持每个查询最多 500 个这样的联合选择,因此如果您尝试输入更多数据,则需要将其分解为 500 个元素块(@ 987654325@)
  • 同意:SQLite 不是瓶颈,而是单个 ORM 事务(在我的例子中是 Ruby On Rails)的开销。所以这仍然是一个巨大的胜利。
  • 此解决方案或 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; 是否更快?
  • 进一步说明:小心!此语法删除重复的行!使用UNION ALL 来避免这种情况(或一些小的性能提升)。
  • @Shadowfax 检查 SQLite 版本,查看 sqlite3.sqlite_version(应该是 3.7.x 或 3.8.x),而不是 sqlite3.version(这只是 python 模块的版本)。
【解决方案2】:

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

试试这个...

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

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

【讨论】:

  • 请使用UNION ALL 而不是UNION。除非您想删除重复项。
  • 如果您希望 ID 自动递增,请给它们一个 NULL 值。
【解决方案3】:

是的,从 SQLite 3.7.11 开始,SQLite 支持此功能。来自SQLite documentation

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

为了与旧版本的 SQLite 兼容,您可以使用 andyfearless_fool 建议的技巧,使用 UNION,但对于 3.7.11 及更高版本,应首选此处描述的更简单的语法。

【讨论】:

  • 我会说图表允许多行,因为VALUES 之后的括号外有一个带逗号的闭环。
  • @JohannesGerer 自从我嵌入这张图片以来,他们已经更新了它。您可以在我将它嵌入到 Internet Archive 时看到该图表。事实上,正是just two months ago 他们在插入中添加了对多行的支持,而就在两天前,他们发布了一个具有该更改的版本。我会相应地更新我的答案。
  • @Brian,您能否引用 SQLite 文档文本,说明这 IS 可能?我重读了 3 次插入文档,但没有发现关于插入多行的内容,但只有这张图片(VALUES (...), (...) 中没有关于逗号的内容):(
  • @Prizoff 我链接到the commit in which this support was added,包括测试用例。图中可以看到(对比IA link),VALUES后面的表达式有一个循环,表示可以重复,用逗号隔开。我链接到 release notes 以获取添加该功能的版本,其中声明“增强 INSERT 语法以允许通过 VALUES 子句插入多行。”
  • @Prizoff 我向 SQLite 维护者提到了这一点,他有committed a fixavailable in the draft documentation。我猜它会在下一个版本的官方文档中。
【解决方案4】:

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

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

【讨论】:

  • 但这在代码中不起作用,而它直接在 SQLite 管理器中工作。在代码中它只插入第一行:(
  • 我在我的代码中使用了这种插入方式,并且效果很好。您只需确保一次提交所有 SQL。这对我来说是一个巨大的速度提升,但我很好奇接受的答案是更快还是更慢?
  • 这种方法在单个事务中修改多个表时非常适合,我发现自己在批量加载数据库时经常这样做。
  • 请注意,如果您需要使用绑定,这种方法将不起作用。 SQLite3 引擎将假定您的所有绑定都将应用于第一条语句,并忽略以下语句。有关更详细的说明,请参阅此SO question
【解决方案5】:

根据this page不支持:

  • 2007-12-03:不支持多行插入,即复合插入。
  INSERT INTO table (col1, col2) VALUES 
      ('row1col1', 'row1col2'), ('row2col1', 'row2col2'), ...

实际上,根据 SQL92 标准,VALUES 表达式应该能够自立。例如,以下应该返回一个包含三行的单列表:VALUES 'john', 'mary', 'paul';

自 3.7.11 版起,SQLite确实支持multi-row-insert。理查德·希普 cmets:

“新的多值插入仅仅是复合词的语法糖(原文如此) 插入。无论哪种方式都没有性能优势。”

【讨论】:

  • 无论哪种方式都没有性能优势。 - 你能告诉我你在哪里看到这句话吗?我在任何地方都找不到。
【解决方案6】:

从版本 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 对 Brian Campbell 的回复/回答。不是我的!他首先提出了解决方案。

【讨论】:

    【解决方案7】:

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

    顺便说一句,您应该知道,如果您将多个 INSERT 包装在显式事务中,SQLite 中的 INSERT 性能会大大提高。

    【讨论】:

      【解决方案8】:

      是的,sql 可以做到这一点,但语法不同。顺便说一句,sqlite documentation 相当不错。 will also tell you 插入多行的唯一方法是使用 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 语句都是 自己的交易。但是如果你 包围多个 INSERT 语句 与 BEGIN...COMMIT 然后所有 插入被分组为一个 交易。提交所需的时间 交易全部摊销 随附的插入语句等 每个插入语句的时间是 大大减少了。

        另一种选择是运行 PRAGMA 同步=关闭。该命令将 导致 SQLite 不等待数据 到达磁盘表面,这将 使写操作看起来是 快多了。但是如果你在 在交易中间,您的 数据库文件可能会损坏。

        【讨论】:

        • 如果您查看 SQLite 的 .import 命令的源代码,它只是一个循环,从输入文件(或 tty)读取一行,然后为该行读取一个 INSERT 语句。不幸的是,效率没有显着提高。
        【解决方案10】:
        INSERT INTO TABLE_NAME 
                    (DATA1, DATA2) 
        VALUES      (VAL1, VAL2), 
                    (VAL1, VAL2), 
                    (VAL1, VAL2), 
                    (VAL1, VAL2), 
                    (VAL1, VAL2), 
                    (VAL1, VAL2), 
                    (VAL1, VAL2), 
                    (VAL1, VAL2); 
        

        【讨论】:

          【解决方案11】:

          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
          

          【讨论】:

            【解决方案12】:

            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)

            【讨论】:

              【解决方案13】:

              你不能,但我认为你不会错过任何东西。

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

              当您使用参数化查询时,Sqlite 会快得多(需要的解析要少得多),所以我不会像这样连接大语句:

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

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

              【讨论】:

                【解决方案14】:

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

                【讨论】:

                  【解决方案15】:

                  使用事务的问题是您还锁定表以供读取。因此,如果您要插入的数据非常多,并且您需要访问您的数据,例如预览等,这种方式效果不佳。

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

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

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

                  【讨论】:

                    【解决方案16】:

                    从 3.7.11 版开始,SQLite 支持多行插入。理查德 Hipp cmets:

                    我使用的是 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

                    【讨论】:

                      【解决方案17】:
                      INSERT INTO tabela(coluna1,coluna2) 
                      SELECT 'texto','outro'
                      UNION ALL 
                      SELECT 'mais texto','novo texto';
                      

                      【讨论】:

                        【解决方案18】:

                        如果您使用Sqlite manager firefox 插件,它支持从INSERT SQL 语句批量插入。

                        事实上它不支持这个,但 Sqlite Browser 支持(适用于 Windows、OS X、Linux)

                        【讨论】:

                          【解决方案19】:

                          在 sqlite 3.7.2 上:

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

                          等等

                          【讨论】:

                            【解决方案20】:

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

                            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 查询内容设为动态,但这开始给出一些语法错误。无论如何,这对我来说运行良好。

                            【讨论】:

                              【解决方案21】:

                              我很惊讶没有人提到准备好的陈述。除非您自己使用 SQL 而不是在任何其他语言中使用 SQL,否则我认为 prepared statements 包装在 transaction 中将是插入多行的最有效方式.

                              【讨论】:

                              • 准备好的陈述总是一个好主意,但与OP提出的问题完全无关。他在问在一个语句中插入多个数据的基本语法是什么。
                              【解决方案22】:

                              简单。不言自明。

                              在 3.36.0 11/20/21 版本上测试。

                              CREATE TEMP TABLE x (col1 TEXT, col2 TEXT, col3 TEXT);
                              
                              INSERT INTO x 
                              VALUES 
                              ('xx','yy','cc'),
                              ('xx','yy','cc'),
                              ('xx','yy','cc'),
                              ('xx','yy','cc'),
                              ('xx','yy','cc'),
                              ('xx','yy','cc');
                              
                              SELECT * FROM x;
                              

                              输出:

                              col1|col2|col3|
                              ----+----+----+
                              xx  |yy  |cc  |
                              xx  |yy  |cc  |
                              xx  |yy  |cc  |
                              xx  |yy  |cc  |
                              xx  |yy  |cc  |
                              xx  |yy  |cc  |
                              

                              版本检查:

                              SELECT sqlite_version();
                              

                              输出:

                              sqlite_version()|
                              ----------------+
                              3.36.0          |
                              

                              有些人可能会说 - 所有“.. UNION ..”的答案都已过时;然而,它们非常有用。有时我们都坐在办公桌上“过去的爆炸”,然后 15 岁的便条挽救了我们的一天。

                              【讨论】:

                                【解决方案23】:

                                我有一个类似下面的查询,但是使用 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())
                                

                                【讨论】:

                                  【解决方案24】:

                                  【讨论】:

                                  • InsertHelper 自 API 17 起已被弃用
                                  【解决方案25】:

                                  如果你使用的是 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 数据库的方法。

                                  【讨论】:

                                  • 如果你想插入有用的数据,这不起作用。
                                  • @CL。这不是真的。您可以随意将其与随机日期和 ID 混合使用。
                                  猜你喜欢
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2013-05-07
                                  • 2011-11-04
                                  • 2011-02-19
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多