2015-01-07 28 views
1

所以我遇到了一个奇怪的情况,并不像我期望的那样表现得很好。带一对多连接的更新语句

比方说,我有如下表:

DROP TABLE IF EXISTS test; 
CREATE TABLE test (somekey char(1) PRIMARY KEY, value1 int, value2 int); 

INSERT INTO test VALUES 
("a", 1, 1), 
("b", 2, 2), 
("c", 3, 3); 

DROP TABLE IF EXISTS test2; 
CREATE TABLE test2 (somekey char(1), thing int, value int); 

INSERT INTO test2 VALUES 
("a", 100, 10), 
("a", 200, 10), 
("b", 100, 20), 
("b", 200, 20), 
("c", 100, 30), 
("c", 200, 30); 

,现在我想基于test2更新test

UPDATE 
    test AS t 
    JOIN test2 AS t2 
     ON t.somekey = t2.somekey 
SET 
    t.value1 = IF(t2.thing = 100, t2.value, t.value1), 
    t.value2 = IF(t2.thing = 200, t2.value, t.value2); 

这是我的输出:

SELECT * FROM test; 
+---------+--------+--------+ 
| somekey | value1 | value2 | 
+---------+--------+--------+ 
| a  |  10 |  1 | 
| b  |  20 |  2 | 
| c  |  30 |  3 | 
+---------+--------+--------+ 

对于某些原因,value2未更新。

我发现如果我改变test2,使得200在100之前插入,则会发生完全相反的情况。这使我相信MySQL实际上是由somekey组成的,并且完全忽略了test2中的一半行。但是,如果我执行的是完全相同的连接,并基于test更新test2中的某些内容,则不会执行分组,test2中的所有六行都会更新。

简单的解决方法是简单的加盟test2两次,像这样:

UPDATE 
    test AS t 
    JOIN test2 AS t100 
     ON t100.somekey = t.somekey 
     AND t100.thing = 100 
    JOIN test2 AS t200 
     ON t200.somekey = t.somekey 
     AND t200.thing = 200 
SET 
    t.value1 = t100.value, 
    t.value2 = t200.value; 

,但我不觉得我应该做这种方式。第一次更新有什么问题?为什么MySQL的行为如此呢?

回答

1

使用JOIN进行更新时,如果没有过滤器(WHERE),将会有笛卡尔积。在这种情况下,由于每行test有2行test2,这意味着每行test会有两个更新。

the Docs

每个匹配的行更新一次,即使它的条件,多次匹配。对于多表语法,不能使用ORDER BY和LIMIT。 MySQL已经选择了value = value选项 - 其中就我所看到的手段,我们无法控制它的2个可能的行值将在更新过程中使用

您的第二个更新查询仅为test.value1test.value2定义了一个可能的值,因此消除了不明确性。

+0

啊,谢谢你在文档中发现。至少它似乎是有意的行为。 –