ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

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

C
Community

更新

BrianCampbell points out here 一样,SQLite 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 因为它更接近原始帖子的语义。

最后

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


进一步说明,sqlite 似乎每个查询最多支持 500 个这样的联合选择,因此如果您尝试输入更多数据,则需要将其分解为 500 个元素块 (sqlite.org/limits.html)
我不认为这对性能有太大影响。 SQLite 的事务在插入时非常快。
同意: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 来避免这种情况(或一些小的性能提升)。
r
river

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

尝试这个...

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

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


请使用 UNION ALL 而不是 UNION。除非您想删除重复项。
如果您希望 ID 自动递增,请给它们一个 NULL 值。
C
Community

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

https://www.sqlite.org/images/syntax/insert-stmt.gif

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

为了与旧版本的 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 fix,即 available in the draft documentation。我猜它会在下一个版本的官方文档中。
J
Jamie Cook

我编写了一些 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
t
typeseven

根据 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。理查德·希普评论:

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


无论哪种方式都没有性能优势。 - 你能告诉我你在哪里看到那句话吗?我在任何地方都找不到它。
m
mjb

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


S
SiKing

是的,sql 可以做到这一点,但语法不同。顺便说一句,sqlite documentation 相当不错。 will also tell you插入多行的唯一方法是使用选择语句作为要插入的数据源。


L
Larry Lustig

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

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


D
DigitalRoss

除了通过 SELECT 之外,Sqlite3 不能直接在 SQL 中执行此操作,虽然 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 synchronous=OFF。此命令将导致 SQLite 不等待数据到达磁盘表面,这将使写入操作看起来要快得多。但是,如果您在事务处理过程中断电,您的数据库文件可能会损坏。


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

A
AG1

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

L
LEO

无畏的傻瓜对旧版本有一个很好的答案。我只是想补充一点,您需要确保列出了所有列。因此,如果您有 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)


t
tuinstoel

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

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

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

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

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


g
g.revolution

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


T
Tim Cooper

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

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

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

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


X
XenKid

从 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


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

C
Chris S

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

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


a
ashakirov

在 sqlite 3.7.2 上:

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

等等


V
Vaibhav Saran

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

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


佚名

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


准备好的陈述总是一个好主意,但与 OP 提出的问题完全无关。他在问在一条语句中插入多个数据的基本语法是什么。
P
Pepik z Usti

简单的。不言自明。

测试版本 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 岁的便条拯救了我们的一天。


S
Sampath

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

M
Mahmoud Badri

您可以使用 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 自 API 级别 17 起已弃用


InsertHelper 自 API 17 起已被弃用
t
test30

如果您使用的是 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 混合使用。