MySQL:对于表中的每一行,更改另一个表中的一行

时间:2014-02-19 01:31:31

标签: mysql sql join sql-update

我有两张桌子:

SELECT * FROM data;
+----+---+---+
| id | c | g |
+----+---+---+
|  1 | 1 | 2 |
|  2 | 1 | 2 |
|  3 | 1 | 2 |
|  4 | 1 | 3 |
|  5 | 2 | 2 |
|  6 | 2 | 3 |
|  7 | 2 | 3 |
+----+---+---+

SELECT * FROM changes;
+----+-------+-------+---+
| id | c_old | c_new | g |
+----+-------+-------+---+
|  1 |     1 |     2 | 2 |
|  2 |     2 |     1 | 3 |
|  3 |     1 |     2 | 2 |
+----+-------+-------+---+

对于changes中的每一行,我需要更改data data.g=changes.g and data.c=changes.c_old中的一行。 (假设总会有足够的匹配)

我正在尝试使用此查询执行此操作:

UPDATE 
data INNER JOIN changes ON 
  data.c=changes.c_old AND p.g=changes.g 
SET data.c_id=changes.c_new 
WHERE data.id IN( 
  SELECT id FROM (
    SELECT data.id from 
    data INNER JOIN changes ON
      data.c=changes.c_old AND data.g=changes.g 
    GROUP BY changes.id
  ) AS another_table
)

现在,我很惊讶查询的乱七八糟。但是,它没有做我需要的。最里面的select返回此表:

+----+
| id |
+----+
|  1 |
|  6 |
|  1 |
+----+

请注意1出现两次。这意味着当我需要三个更改时,只有两行被更改(或第一行更改了两次)。有没有办法确保该子查询中的每个id都是唯一的?有没有更好的方法来完成这个混乱?

提前致谢!

2 个答案:

答案 0 :(得分:1)

您正在选择一个不属于该组或正在聚合的字段。

SELECT data.id from 
data INNER JOIN changes ON
    data.c=changes.c_old AND data.g=changes.g 
GROUP BY changes.id

你应该在select中的data.id上使用聚合函数,或者将data.id添加到groupby(虽然我怀疑这不是你想要的结果)

INNER JOIN是此数据集的结果

+---------+--------+--------+------------+---------------+---------------+-----------+
| data.id | data.c | data.g | changes.id | changes.c_old | changes.c_new | changes.g |
+---------+--------+--------+------------+---------------+---------------+-----------+
|       1 |      1 |      2 |          1 |             1 |             2 |         2 |
|       1 |      1 |      2 |          3 |             1 |             2 |         2 |
|       2 |      1 |      2 |          1 |             1 |             2 |         2 |
|       2 |      1 |      2 |          3 |             1 |             2 |         2 |
|       3 |      1 |      2 |          1 |             1 |             2 |         2 |
|       3 |      1 |      2 |          3 |             1 |             2 |         2 |
|       6 |      2 |      3 |          2 |             2 |             1 |         3 |
|       7 |      2 |      3 |          2 |             2 |             1 |         3 |
+---------+--------+--------+------------+---------------+---------------+-----------+
由于连接中的多个匹配,

1,2,3被扩展,并且由于没有匹配而消除了4,5

然后您按change.id进行分组,这将导致(在分组后显示CSV列表中的值)

+---------+--------+--------+------------+---------------+---------------+-----------+
| data.id | data.c | data.g | changes.id | changes.c_old | changes.c_new | changes.g |
+---------+--------+--------+------------+---------------+---------------+-----------+
|   1,2,3 |  1,1,1 |  2,2,2 |          1 |         1,1,1 |         2,2,2 |     2,2,2 |
|   1,2,3 |  1,1,1 |  2,2,2 |          3 |         1,1,1 |         2,2,2 |     2,2,2 |
|     6,7 |    2,2 |    3,3 |          2 |           2,2 |           1,1 |       3,3 |
+---------+--------+--------+------------+---------------+---------------+-----------+

由于没有从可用选项中选择值的聚合或确定性方法,因此为两个changes.id 1和3选择了data.id中的1

根据您的需要,您想要3行吗?所有不同的价值?您应该将该确定性行为添加到选择中。

顺便说一句,我很确定其他SQL引擎不允许选择(例如MSSQL),因为它的含糊不清。至于那种情况下的MySQL行为,我相信它会从存储的第一行中选择第一个值,因此在两种情况下你可能都会获得1,但可以自由选择它想要的任何值。

http://dev.mysql.com/doc/refman/5.7/en/group-by-extensions.html

  

MySQL扩展了GROUP BY的使用,因此选择列表可以引用GROUP BY子句中未命名的非聚合列。这意味着前面的查询在MySQL中是合法的。您可以通过避免不必要的列排序和分组来使用此功能来获得更好的性能。但是,当GROUP BY中未命名的每个非聚合列中的所有值对于每个组都相同时,这非常有用。服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选的值是不确定的。此外,添加ORDER BY子句不会影响每个组中值的选择。选择值后会对结果集进行排序,而ORDER BY不会影响服务器选择的每个组中的值。

答案 1 :(得分:0)

让我们将这个过程分为两个任务:

  1. id中更新要更新的行data以及设置c的值。
  2. 更新值。
  3. 任务1可以通过以下查询来实现(注意使用distinct来删除重复项):

    select distinct d.id, c.c_new
    from
        data as d
        inner join changes as c
            on  d.c = c.c_old
            and d.g = c.g
    

    这应该给你以下内容:

    | id | c_new |
    |----|-------|
    |  1 |     2 |
    |  2 |     2 |
    |  3 |     2 |
    |  6 |     1 |
    |  7 |     1 |
    

    现在进行更新只需使用上一个查询作为派生表表达式并将其加入data

    update
        data as da
        inner join (
            select distinct d.id, c.c_new
            from
                data as d
                inner join changes as c
                on  d.c = c.c_old
                and d.g = c.g
        ) as dc
        on da.id = dc.id
    set d.c = dc.c_new;
    

    你完成了,最终结果:

    | ID | C | G |
    |----|---|---|
    |  1 | 2 | 2 |
    |  2 | 2 | 2 |
    |  3 | 2 | 2 |
    |  4 | 1 | 3 |
    |  5 | 2 | 2 |
    |  6 | 1 | 3 |
    |  7 | 1 | 3 |