使用一对多连接更新语句

时间:2015-01-07 19:08:01

标签: mysql

所以我遇到了一个奇怪的情况,根本没有表现出我的预期。

我们说我有以下表格:

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);

现在我想根据test更新test2

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使得在100之前插入200,则恰好相反。这让我相信MySQL实际上是按somekey进行分组,完全忽略test2中的一半行。但是,如果我执行完全相同的联接并仅根据test2更新test中的内容,则不会进行分组,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 个答案:

答案 0 :(得分:1)

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

来自the Docs

  

每个匹配的行都会更新一次,即使它与条件多次匹配也是如此。对于多表语法,不能使用ORDER BY和LIMIT。

据我所知,这意味着我们无法控制在更新期间将使用哪两个可能的行值--MySql选择了value = value选项。

您的第二次更新查询仅为test.value1test.value2定义了一个可能的值,从而消除了歧义。