如何优化递归INSERT和SELECT DISTINCT mysql查询

时间:2016-01-29 13:19:32

标签: mysql sql

我有两个MySQL表WData-Clean和WData。 我在WData中导入数据,每次都有很多重复的记录。数据需要超时堆叠,因此无法删除重复项。 在WData-Clean中我导入了所有" unique" WData中的记录。此处示例中的唯一记录是与某些字段可能具有相同值的所有其他类似记录不同的记录。

要执行此操作,我使用以下查询:

INSERT INTO `WData-clean` (`field1`, `field2`, `field3`, `field4`, `field5`, `field6`) 
SELECT DISTINCT `field1`, `field2`, `field3`, `field4`, `field5`, `field6`
FROM WData cr
WHERE NOT EXISTS (SELECT * FROM `WData-clean` c
WHERE (cr.field1 = c.field1 AND cr.field2 = c.field2))

在WData累积超过200万条记录之前,这种情况一直很好用,而且这个查询真的很挣扎,因为它必须将每个提案与源表中的每个现有记录进行比较。

如何优化此查询的效果?

3 个答案:

答案 0 :(得分:0)

编写查询以规范化数据时,应始终使用GROUP BY

INSERT INTO `WData-clean` (`field1`, `field2`, `field3`, `field4`, `field5`, `field6`) 
(
    SELECT
        `field1`,
        `field2`,
        `field3`,
        `field4`,
        `field5`,
        `field6`
    FROM
        WData AS cr
    WHERE NOT EXISTS (
        SELECT
            *
        FROM
            `WData-clean` AS c
        WHERE
            cr.field1 = c.field1
            AND cr.field2 = c.field2)
    )
    GROUP BY
        `field1`, `field2`, `field3`, `field4`, `field5`, `field6`
)

此外,它超出了此问题的范围(有点),但我试图找到一种方法来避免WHERE NOT EXISTS。随着 WData-clean 的增长,该查询将开始花费更长的时间来匹配。

答案 1 :(得分:0)

我可以想到3种方法:

  1. 在wdata_clean表中,在所有需要唯一的字段上创建唯一索引。在wdata表中创建一个时间戳字段,用于记录上次更改记录的时间。还存储上次刷新wdata_clean时的时间戳。然后使用INSERT IGNORE ... SELECT ...选择自上次更新wdata_clean以来修改过的wdata记录的where条件。最后,更新上次同步的时间戳。

  2. 在wdata表中创建一个时间戳字段,在两个表中创建一个varchar字段。还存储数据上次同步的时间戳。在varchar字段中,计算必须唯一的所有字段的哈希值(例如sha1)。在insert ... select ...哈希字段上的wdata_clean左边连接是null:

    insert into wdata_clean select * from wdata left join wdata_clean on wdata.hashfield=wdata_clean.hashfield where wdata_clean.hashfield is null and wdata.timestampfield>'timestamp of last sync'

  3. 在wdata和wdata_clean表中的哈希字段的哈希和时间戳字段上创建复合索引。

    1. 如第二点所述,在wdata_clean中创建一个哈希字段,并在其上添加唯一索引。将插入/更新/删除触发器添加到wdata。在触发器中,计算字段的哈希值是唯一的,并在wdata_cleaned表中执行insert ignore ...

答案 2 :(得分:0)

当前查询对WData上的每一行执行一次子查询(因为子查询依赖于WData中的行)。

如果您在字段上有索引,我会根据可能匹配的字段执行简单的LEFT OUTER JOIN,并检查WHERE子句中找不到匹配的行: -

INSERT INTO WData-clean (field1, field2, field3, field4, field5, field6) 
SELECT DISTINCT cr.field1, cr.field2, cr.field3, cr.field4, cr.field5, cr.field6
FROM WData cr
LEFT OUTER JOIN WData-clean c
ON cr.field1 = c.field1 
AND cr.field2 = c.field2
WHERE c.field1 IS NULL

(这个例子假设c.field1不能合法地具有NULL值 - 最好使用WData-clean表中的主键来避免这个问题)