【问题标题】:complex mysql update query is slow复杂的mysql更新查询很慢
【发布时间】:2011-09-21 15:11:04
【问题描述】:

我有一个包含用户数据的导入表,我需要用重复的字段值标记行,因为它们不应该被导入。

CREATE TABLE `import` (
  ID int(10) unsigned NOT NULL AUTO_INCREMENT,
  method varchar(20) DEFAULT NULL,
  f1 text,
  f2 text,
  PRIMARY KEY (ID)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

f1 字段可以包含重复值。选择它们的查询有效:

SELECT id, a.f1 FROM import a INNER JOIN
(
    SELECT f1 FROM import
    WHERE f1 IS NOT NULL AND f1 != ''
    GROUP BY f1
    HAVING COUNT(id) > 1
) b
ON a.f1 = b.f1

问题在于执行更新的外部查询。这是整个shebang:

UPDATE import SET method = 'ERR_DUPLICATE' WHERE import.id IN
(
    SELECT id FROM
    (
        SELECT id, a.f1 FROM import a INNER JOIN
        (
            SELECT f1 FROM import
            WHERE f1 IS NOT NULL AND f1 != ''
            GROUP BY f1
            HAVING COUNT(id) > 1
        ) b
        ON a.f1 = b.f1
    ) c
)

该构造来自MySQL: You can't specify target table 'tasks' for update in FROM clause - 这是我之前遇到的错误。上述查询有效,但需要 0.5 秒。对于包含大约 30 个重复项的 20,000 行表。我将不得不处理更大的导入表,所以这是一个阻碍。

有什么想法可以加快速度吗?

【问题讨论】:

  • 你定义了哪些索引?
  • 好问题@Flimzy,只有主键。其他字段实际上是占位符,具有重复项的字段可以是其中任何一个。我应该尝试索引所有这些吗?总共有 40 个字段。
  • 我用索引将 f1 更改为 varchar(2048)。现在需要 2 秒!
  • 我建议为您加入的任何字段编制索引(在您的示例中为 a.f1)。我还建议查看 EXPLAIN 的输出以获取优化位置的线索。
  • 从长远来看,您要处理多少行?几百万,我接受?

标签: mysql


【解决方案1】:

试试这个修改后的版本:

CREATE TEMPORARY TABLE duplicate_ids 
        SELECT MAX(id) AS id FROM import 
        WHERE f1 IS NOT NULL AND f1 != ''
        GROUP BY f1 ORDER BY NULL
        HAVING COUNT(*) > 1;
UPDATE import SET method = 'ERR_DUPLICATE' WHERE import.id IN(
      SELECT id FROM duplicate_ids
);

这将为您提供更大的 ID 记录作为副本。 ORDER BY NULL 抑制了由于分组而导致的隐式排序。 此外,由于条件和使用 TEXT 列的 GROUPing 效率低下,您可以维护一个额外的列,该列将包含 f1 中文本的哈希码。

ALTER TABLE import ADD COLUMN f1_hash INT UNSIGNED NOT NULL;
ALTER TABLE import ADD KEY(f1_hash);

f1_hash 由 CRC32(f1) (http://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_crc32) 返回的值填充。 CRC32 可能存在冲突,因此您最终必须检查 f1 列。

CREATE TEMPORARY TABLE duplicate_ids 
SELECT i2.id FROM import i1 JOIN import i2 
ON i2.id<>i1.id AND i1.f1_hash = i2.f2_hash   
AND i1.f1_hash > 0 WHERE i1.f1 = i2.f1

然后像以前一样执行更新。 您当然不需要 f1 列上的 INDEX,因此最好将其删除,因为它会增加不必要的开销。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多