替代MySQL LOAD LOCAL INFILE REPLACE来更新表

时间:2014-06-25 23:27:45

标签: mysql sql linux bash

如果我有这个文件:

1  someval1
2  someval2
...

和MySQL中的这个表

-------------
|id | value |
|-----------|
| 1 | NULL  |
| 2 | NULL  | 
...

我的目标是将表格右栏中的值设置为我的csv文件右栏中id与id匹配的值。我可以使用此查询(但我不想)

LOAD DATA 
  LOCAL INFILE 'file'
  REPLACE
  INTO TABLE myTable
  FIELDS TERMINATED BY ' '
  LINES TERMINATED BY '\n'
  (id, value);

我不想使用REPLACE的原因是因为根据MySQL文档,只要主键匹配(在本例中为'id'),就会删除整行,并重新插入包含该信息的行在我的档案中。我假设这意味着在未来的任何时候,如果我要在此表中添加一列,我必须确保我使用此查询的任何文件都包含每行的所有列值可能会更新;否则将删除非目标列。

使用本地文件更新表是否有更好的选择,其中输入文件中的一列(或更多?)包含需要匹配的主键?

2 个答案:

答案 0 :(得分:3)

一种可能的方法:

使用LOAD DATA INFILE将CSV数据加载到临时表中。

然后,使用INSERT ...SELECT ...ON DUPLICATE KEY UPDATE执行更新。

编辑,带有工作&测试示例:

由OP确定的LOAD DATA INFILE问题是LOAD DATA INFILE针对速度进行了优化,在处理传入数据中的冲突时非常愚蠢。

它只有两个选项:REPLACEIGNOREREPLACE是解决方案的真正大锤。找到重复键时,它只删除原始行并插入新行。这里的问题是CSV文件中未包含的现有行中的任何数据都将丢失。

另一种选择是IGNORE'但这种情况稍微好一些。它不会影响现有数据,但是当它完成时,无法分辨CSV文件中的哪些行已经应用,哪些没有。

LOAD DATA INFILE非常适合最擅长:快速批量上传新数据。对于现有数据的更新,它可能有其用途,但它不是工具。

我们可以用我们喜欢的语言(或者我们使用的语言,即使它不是我们最喜欢的语言 - PHP任何人?)编写例程,解析CSV文件并使用{{1来应用更新查询,但这引入了额外的脚本级别,并否认MySQL有机会优化它正在做的事情。在任何情况下,大量单个INSERT...ON DUPLICATE UPDATE...查询都远没有效率。我们可以使用允许多个值集的INSERT语法,但对于大型导入,这可能会遇到最大请求大小限制。

解决方案是使用两阶段方法,并允许MySQL做它最擅长的事情。

将CSV文件导入到INSERT的临时表中,然后使用LOAD DATA INFILE以更智能的方式处理更新过程。

假设我们有一个这样的表:

INSERT...SELECT...ON DUPLICATE KEY UPDATE

我们希望定期从其他来源导入CSV数据,如下所示:

CREATE TABLE `test`.`bestmate` (
  `id` INT NOT NULL,
  `name` VARCHAR(45) NULL,
  `age` INT NULL,
  `bestmate` VARCHAR(45) NULL,
  PRIMARY KEY (`id`),
  UNIQUE INDEX `id_UNIQUE` (`id` ASC));

我们的id, name, age 1, fred, 16 2, alan, 22 3, george, 26 专栏已在线更新,因此我们的CSV文件中没有这些信息,我们也不想丢失。

首先,创建一个临时表来保存我们的CSV数据并导入文件。

bestmate

现在我们可以在一个命令中插入或更新drop table if exists `csvTable` ; CREATE TEMPORARY TABLE `csvTable` (`id` INT NOT NULL, `name` VARCHAR(45) NULL, `age` INT NULL); LOAD DATA LOCAL INFILE 'c:\\users.csv' INTO TABLE `csvTable` FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\r\n' IGNORE 1 lines; 表中的行,不会触及表中的任何额外数据。

bestmate

最后,整理一下自己。

INSERT `bestmate` (`id`,`name`,`age`) SELECT * from `csvTable` 
  ON DUPLICATE KEY UPDATE
    `bestmate`.`name` = `csvTable`.`name`,
    `bestmate`.`age` = `csvTable`.`age`;

答案 1 :(得分:1)

您可以从file生成UPDATE语句,然后执行它们:

    awk "{ print \"UPDATE TABLE myTable SET value = '\" \$2 \"' WHERE id ='\" \$1 \"';\" }" < file | mysql db