使用LAST_INSERT_ID()在多个表上进行MySQL批量插入

时间:2014-12-01 09:58:22

标签: mysql database

我试图将大量用户插入带有两个表的MySQL数据库中:

第一个表包含用户数据。示例INSERT如下所示(id是主键,mail是唯一键):

INSERT INTO users (id, mail, name)  
VALUES (NULL, "foo@bar.tld", "John Smith") 
ON DUPLICATE KEY UPDATE name = VALUE(name)

第二个表包含用户所属的组。它只存储两个外键users_idgroups_id。示例查询如下所示:

INSERT INTO users_groups (users_id, groups_id)
VALUES (LAST_INSERT_ID(), 1)

此设置适用于小型数据集。当我导入大量数据(> 1M行)时,INSERT会变慢。显然,进行批量插入会更好:

INSERT INTO users (id, mail, name)  
VALUES (NULL, "foo@bar.tld", "John Smith"), (NULL, "baz@qux.tld", "Anna Smith") 
ON DUPLICATE KEY UPDATE name = VALUE(name)

INSERT INTO users_groups (users_id, groups_id)
VALUES (LAST_INSERT_ID(), 1), (LAST_INSERT_ID(), 4)

问题当然是,LAST_INSERT_ID()只返回批INSERT的一个(第一个)ID。
所以,我需要的是一个“嵌套”批处理INSERT,在MySQL中不存在IMO。

如何才能让INSERT更快?

4 个答案:

答案 0 :(得分:5)

默认情况下,批量插入提供顺序自动增量,有了这些知识,您可以像<; p>那样进行插入

INSERT INTO users (id, mail, name)  
VALUES  (NULL, "foo@bar.tld", "John Smith"), 
        (NULL, "baz@qux.tld", "Anna Smith"),
        (...)  # repeat n-times
;

SET @LASTID=LAST_INSERT_ID()
;

INSERT INTO users_groups (users_id, groups_id)
VALUES    (@LASTID - n  , 1), # Note n in descending sequence
          (@LASTID - n-1, 1),
          ...
          (@LASTID - 1  , 1), 
          (@LASTID - 0  , 4)
;

有关批量插入和自动增量的详细信息,请查看http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html

重要的是,请确保innodb_autoinc_lock_mode = 1

show global variables like 'innodb_autoinc_lock_mode'

否则请考虑将您的插页包装在LOCK TABLES

LOCK TABLES tbl_name WRITE
... sqls ...
UNLOCK TABLES

答案 1 :(得分:1)

如果您同时将数百万条已知行放入表中,请考虑使用LOAD DATA INFILE,因为它仅适用于该类型的方案,如the docs引用所示:

  

LOAD DATA INFILE语句将文本文件中的行读取到   桌子的速度非常快。

Speed of INSERT Statements

  

从文本文件加载表格时,请使用LOAD DATA INFILE。这是   通常比使用INSERT语句快20倍。

这假设您的源数据来自或可以作为文本文件提供。如果您在文件中也有组ID,则可以执行以下操作:

CREATE TEMPORARY TABLE load_users_groups (
  mail VARCHAR(60),
  name VARCHAR(60),
  groupid INT,
  PRIMARY KEY (mail, name)
);

LOAD DATA INFILE '/path/to/file.csv'
INTO TABLE load_users_groups
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n';  -- use whatever optional syntax required to parse your file

INSERT INTO users (mail, name)
SELECT mail, name FROM load_users_groups
ON DUPLICATE KEY UPDATE name = VALUES(name);

INSERT INTO users_groups (users_id, groups_id)
SELECT users.id, load_users_groups.groupid
FROM users JOIN load_users_groups USING (mail, name);

DROP TEMPORARY TABLE load_users_groups;

此方法最终是否比当前方法更快取决于您使用LOAD DATA INFILE节省的时间是否比执行另外两个INSERT ... SELECT语句以将数据移动到所需表中所花费的时间更长。您可能想要调整临时表上的键;我不能仅根据你问题的内容为你做基准测试。不过,我很想知道它是如何运作的。

该文档还提供了相当多的Bulk Data Loading for InnoDB TablesBulk Data Loading for MyISAM Tables提示。我不会详细介绍它们,尤其是因为您没有向我们提供任何DDL或服务器信息,但您可能会发现在您自己的时间阅读其中一个或另一个很有帮助。

答案 2 :(得分:0)

不得不处理类似的问题。

MySQL并没有真正提供很多方法来为此目的可靠地保留大批量的表ID。我花了半天时间研究无济于事。有一些黑客四处乱窜,但没有任何内容可以保存我的数据。

我只是使用逐个插入的用户表(比screwy更慢)并将新行的id返回给我的ORM。我有一个行ID可供使用,因此我可以将它和应该导入JSON的数据一起保存在一起。这使我更容易批量插入并保持数据匹配。

最佳。

答案 3 :(得分:-1)