更新重复行

时间:2016-02-21 11:47:00

标签: mysql sql

我有一张桌子:

id    name
1     a
2     a
3     a
4     b
5     b
6     c

我正在寻找一个更新语句,将name列更新为:

id    name
1     a
2     a-2
3     a-3
4     b
5     b-2
6     c

SQL Server我会使用:

;with cte as(select *, row_number() over(partition by name order by id) rn from table)
update cte set name = name + '-' + cast(rn as varchar(10))
where rn <> 1

我在MySQL非标准查询中并不强大。 我可以在MySQL中执行类似的操作吗?

3 个答案:

答案 0 :(得分:4)

在MySQL中,您可以使用变量来模拟ROW_NUMBER窗口函数:

SELECT id, CONCAT(name, IF(rn = 1, '', CONCAT('-', rn))) AS name
FROM (
SELECT id, name,
       @rn := IF(name = @n, @rn + 1,
                 IF(@n := name, 1, 1)) AS rn
FROM mytable
CROSS JOIN (SELECT @rn := 0, @n := '') AS vars
ORDER BY name, id) AS t

UPDATE你可以使用:

UPDATE mytable AS t1
SET name = (
   SELECT CONCAT(name, IF(rn = 1, '', CONCAT('-', rn))) AS name
   FROM (
      SELECT id, name,
             @rn := IF(name = @n, @rn + 1,
                       IF(@n := name, 1, 1)) AS rn
      FROM mytable
      CROSS JOIN (SELECT @rn := 0, @n := '') AS vars
   ORDER BY name, id) AS t2
   WHERE t1.id = t2.id)

Demo here

您还可以UPDATE使用JOIN语法:

UPDATE mytable AS t1
JOIN (
   SELECT id, rn, CONCAT(name, IF(rn = 1, '', CONCAT('-', rn))) AS name
   FROM (
      SELECT id, name,
             @rn := IF(name = @n, @rn + 1,
                       IF(@n := name, 1, 1)) AS rn
      FROM mytable
      CROSS JOIN (SELECT @rn := 0, @n := '') AS vars
      ORDER BY name, id) AS x
) AS t2 ON t2.rn <> 1 AND t1.id = t2.id
SET t1.name = t2.name;

后者可能比前者更快,因为它执行的UPDATE操作更少。

答案 1 :(得分:4)

你可以这样做:

UPDATE YourTable p
       JOIN(SELECT t.id,t.name,count(*) as rnk
            FROM YourTable t
            INNER JOIN YourTable s on(t.name = s.name and t.id <= s.id)
            GROUP BY t.id,t.name) f
       ON(p.id = f.id)
SET p.name = concat(p.name,'-',f.rnk)
WHERE rnk > 1

这将基本上使用join和count来获得与ROW_NUMBER()相同的内容,并且只更新那些结果超过1的人(意味着第二个,第三个ETC不包括第一个)

答案 2 :(得分:2)

下一个查询将以更少的工作量完成数据库:

UPDATE
    tab AS tu
INNER JOIN
    --  result set containing only duplicate rows that must to be updated
    (
        SELECT
            t.id,
            COUNT(*) AS cnt
        FROM
            tab AS t
        --  join the same table by smaller id and equal value. That way you will exclude rows that are not duplicated
        INNER JOIN
            tab AS tp
        ON
            tp.name = t.name
        AND
            tp.id < t.id
        GROUP BY
            t.id
    ) AS tc
ON
    tu.id = tc.id
SET
    tu.name = CONCAT(tu.name, '-', tc.cnt + 1)