使用表格和数据:
CREATE TABLE test.tem(a INT,b INT,INDEX (a),INDEX (b));
INSERT INTO test.tem VALUES(1,2),(1,1),(1,NULL),(2,3);
现在数据应该是:
+------+------+
| a | b |
+------+------+
| 1 | 2 |
| 1 | 1 |
| 1 | NULL |
| 2 | 3 |
+------+------+
我想按列a将列b更新到min(b)组 我知道一个正确的SQL是:
UPDATE tem AS t1
JOIN (SELECT a,MIN(b) AS m FROM tem GROUP BY a) AS t2
USING (a)
SET t1.b = t2.m;
产生正确的结果是:
+------+------+
| a | b |
+------+------+
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 2 | 3 |
+------+------+
但是,使用此SQL查询在一个表中更新需要大约5分钟,其中包含450万条记录。
所以,我有一个自己的SQL:
UPDATE test.tem t1
JOIN test.tem t2
ON t1.a = t2.a
SET t1.b = t2.b
WHERE t1.b > t2.b
OR t1.b IS NULL;
但结果不正确:
+------+------+
| a | b |
+------+------+
| 1 | 1 |
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
+------+------+
我认为原因在于更新时MYSQL的工作原理。任何人都可以告诉我 错误的结果出来了吗?如果有人可以修复我的 SQL,那也会有所帮助。
答案 0 :(得分:2)
对于“查询未正确更新行”:
对于具有相同b
b
更新为a
的最小
您建议使用以下JOIN
来执行此操作:
UPDATE test.tem t1
JOIN test.tem t2
ON t1.a = t2.a
SET t1.b = t2.b
WHERE t1.b > t2.b
OR t1.b IS NULL;
与您的想法相反,JOIN
不会执行1-1 JOIN
。它实际上是一个多对多JOIN
,因为as I said yesterday你不在join子句中使用主键(也不是非null唯一键)。
事实上,将该查询重写为SELECT
可能会帮助您理解问题:
SELECT t1.a as t1a, t1.b as t1b, t2.a as t2a,t2.b as t2b FROM tem t1 JOIN tem t2
ON t1.a = t2.a
WHERE t1.b > t2.b
OR t1.b IS NULL;
+------+---------+------+--------+
| T1A | T1B | T2A | T2B |
+------+---------+------+--------+
| 1 | (null) | 1 | 2 |
| 1 | 2 | 1 | 1 |
| 1 | (null) | 1 | 1 |
| 1 | (null) | 1 | (null) |
+------+---------+------+--------+
http://sqlfiddle.com/#!2/856a7/8
正如您现在所看到的,行(1, null)
匹配(1, 1)
,(1, 2)
和(1, null)
。根据查询执行的(非确定性)顺序,这可能会为b
分配三个可能值中的任何一个('我不确定,但可能甚至更新它< em>几次次。在某种程度上,你很幸运在测试时发现了“错误”的结果!
我希望这可以解释为什么您的查询不会产生预期结果。由于多表UPDATE
语句不允许ORDER BY
或GROUP BY
条款,因为我自己要找到“好”结果,我没有看到许多其他选项而不是找到最小第一个通过子查询...
答案 1 :(得分:1)
为提高查询执行速度,您可以做的一件事就是使用正确的索引来优化WHERE
/ USING
子句。
UPDATE tem AS t1
JOIN (SELECT a,MIN(b) AS m FROM tem GROUP BY a) AS t2
USING (a)
SET t1.b = t2.m;
此请求具有USING(a)
子句。最快的MySQL能够匹配将运行最快查询的列a
。因此,您必须在该列上添加索引:
ALTER TABLE tem ADD INDEX (a);
事实上,由于子查询同时使用GROUB BY(a)
和MIN(b)
,(a,b)
上的索引最有可能表现得更好:
ALTER TABLE tem ADD INDEX (a,b);
为了确保这一点,您可能需要检查查询计划(请参阅下面的EXPLAIN
)。
第二个查询是:
UPDATE test.tem t1
JOIN test.tem t2
ON t1.a = t2.a
SET t1.b = t2.b
WHERE t1.b > t2.b
OR t1.b IS NULL;
我不在这里回答“我的查询有什么问题”。但是从纯粹的性能角度来看,由于此查询使用b
子句中的列WHERE
,因此您将优化其执行,至少要在b
上添加索引。
ALTER TABLE tem ADD INDEX (b);
作为提示,当您有慢 SELECT
查询时,您可以使用EXPLAIN SELECT ...
检查查询计划以便检查如果/如何使用MySQL。
在这里将两个查询重写为SELECT
语句,这将给出:
EXPLAIN SELECT t1.b = t2.b
FROM tem AS t1
JOIN (SELECT a,MIN(b) AS m FROM tem GROUP BY a) AS t2
USING (a);
EXPLAIN SELECT t1.b = t2.b
FROM test.tem t1
JOIN test.tem t2
ON t1.a = t2.a
WHERE t1.b > t2.b
OR t1.b IS NULL;