4

我需要将表的标志'A'从一行'X''Y'update_date 满足条件的位置重置1. update_date > 1 month, 2. flag = 'X' & 3. type = 1

并且根据另一个表检查 update_date 'B'。我希望下面的查询能解释我到底需要什么。这个查询也适用于我。但问题是耗时太长。实际上我的表A & B要大得多,几乎包含十亿行,大约有 10 列。

当我运行用于选择的子查询时,A.id我立即得到了结果。

 SELECT a.id 
 FROM A a 
 JOIN B b 
 ON (a.id = b.id 
       AND a.name = b.name 
       AND a.type = 1 
       AND a.flag = 'X' 
       AND a.update_date > DATE_SUB(NOW(), INTERVAL 1 MONTH) tmp_table)

但是即使我设置了限制,只有更新查询也需要很多时间。

UPDATE A 
SET flag='Y' 
WHERE id IN (SELECT a.id 
             FROM A a 
             JOIN B b 
             ON (a.id = b.id 
                   AND a.name = b.name 
                   AND a.type = 1 
                   AND a.flag = 'X' 
                   AND a.update_date > DATE_SUB(NOW(), INTERVAL 1 MONTH) tmp_table))  
             LIMIT 100

我正在寻找我的查询的替代解决方案,以使其快速。希望我可以为它编写一个存储过程。但是在SP我应该循环遍历每个 target_ids 对吗?

我不想在 PHP 中编写两个单独的查询,因为我的 PHP 脚本有很多线程在 cron 上运行,它们返回相同的结果(时间延迟)。

还要注意,我确实有足够的列索引。

希望按限制更新限制。即,每次运行更新 1000 多条记录。

4

3 回答 3

3

改变存在

EXISTS 会更快,因为一旦引擎找到命中,它就会退出寻找,因为条件已被证明是正确的。使用 IN 它将在进一步处理之前从子查询中收集所有结果。

UPDATE A a
JOIN B b 
ON (a.id = b.id 
               AND a.name = b.name 
               AND a.type = 1 
               AND a.flag = 'X' 
               AND a.update_date > DATE_SUB(NOW(), INTERVAL 1 MONTH))
SET a.flag='Y'
ORDER BY a.id LIMIT 1000;

已编辑支持 LIMIT 的替代品(它只会更新第 100 条记录)

SET @rn = 0;
UPDATE A a
JOIN (SELECT @rn:=@rn+1 AS rId, id, name FROM B b 
      JOIN A a 
      ON (@rn < 100 AND a.id = b.id 
               AND a.name = b.name 
               AND a.type = 1 
               AND a.flag = 'X' 
               AND a.update_date > DATE_SUB(NOW(), INTERVAL 1 MONTH)
      ) 

) b 
ON (a.id=b.id) 
SET a.flag='Y'
WHERE b.rId < 100;

使用存在子句

Update A a 
SET a.flag='Y'
WHERE EXISTS (SELECT 1 FROM B b WHERE a.id = b.id 
               AND a.name = b.name 
               AND a.type = 1 
               AND a.flag = 'X' 
               AND a.update_date > DATE_SUB(NOW(), INTERVAL 1 MONTH)) 
ORDER BY a.id LIMIT 1000;

希望这可以帮助

于 2013-03-06T11:45:39.313 回答
0

您也可以使用联接

UPDATE A
  LEFT JOIN (SELECT
           a.id
         FROM A AS a
           JOIN B AS b
         ON a.id = b.id
         WHERE a.name = b.name
         AND a.type = 1
         AND a.flag = 'X'
         AND a.update_date > DATE_SUB(NOW(), INTERVAL 1 MONTH)) AS l
    ON l.id = A.id
SET flag = 'Y'
WHERE id = l.id
于 2013-03-06T11:51:53.383 回答
0

最后,我得到了性能更好的优化查询。只需加入临时表。

UPDATE A AS a JOIN (
SELECT a.id FROM A AS a JOIN B AS b ON
    b.type = a.type 
    AND b.name = a.name 
    AND b.last_update_date < DATE_SUB(NOW(), INTERVAL 1 MONTH) 
    AND a.type = 1 
    AND a.flag = 'X' 
ORDER BY a.id DESC LIMIT 1000)
AS source ON source.id = a.id
SET flag = 'Y';

感谢http://www.xaprb.com/blog/2006/08/10/how-to-use-order-by-and-limit-on-multi-table-updates-in-mysql

于 2013-03-07T11:48:37.747 回答