MySQL使用相同表列的计数更新所有行值

时间:2020-06-15 08:40:20

标签: mysql sql select sql-update window-functions

我有下表的数据:

| predp_id | strp_ID | predp_nas |
| -------- | ------- | --------- |
| 1        | 1       |   null    |
| 2        | 1       |   null    |
| 3        | 1       |   null    |
| 4        | 2       |   null    |
| 5        | 2       |   null    |
| 6        | 3       |   null    |
对于每行上相同的strp_ID,

predp_nas列应为strp_ID列的计数+ 1。

我当前正在使用下一个查询在每个新插入内容上实现此目标:

INSERT INTO PREDMETIP
    (`strp_ID`, `predp_nas`)
VALUES(
 1, 
 (SELECT counter + 1 FROM (SELECT COUNT(strp_ID) counter FROM PREDMETIP WHERE strp_ID = '1') t)
);

这给了我

| predp_id | strp_ID | predp_nas |
| -------- | ------- | --------- |
| 1        | 1       |   null    |
| 2        | 1       |   null    |
| 3        | 1       |   null    |
| 4        | 2       |   null    |
| 5        | 2       |   null    |
| 6        | 3       |   null    |
| 7        | 1       |     4     |

但是现在我已经导入了大量数据,并且需要立即更新所有predp_nas字段才能得到结果:

| predp_id | strp_ID | predp_nas |
| -------- | ------- | --------- |
| 1        | 1       |     1     |
| 2        | 1       |     2     |
| 3        | 1       |     3     |
| 4        | 2       |     1     |
| 5        | 2       |     2     |
| 6        | 3       |     1     |
| 7        | 1       |     4     |

我的数据库小提琴带有插入查询View on DB Fiddle,我很难理解如何为相同的内容编写查询,但要一次更新所有字段。任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:2)

您正在寻找的是ROW_NUMBER()(如果您使用的是MySQL 8+),但是由于您的提琴是在MySQL 5.7上的,因此我假设这是您的版本,因此您可以通过计算给定strp_ID的{​​{1}}较低的行数,并使用该行数来更新表:

predp_id

更新后的输出:

UPDATE PREDMETIP p1
JOIN (
  SELECT p1.predp_id,
       COUNT(p2.predp_id) + 1 AS rn
  FROM PREDMETIP p1
  LEFT JOIN PREDMETIP p2 ON p2.strp_ID = p1.strp_ID AND p2.predp_id < p1.predp_id
  GROUP BY p1.predp_id
) p2 ON p1.predp_id = p2.predp_id
SET p1.predp_nas = p2.rn
;
SELECT *
FROM PREDMETIP 

答案 1 :(得分:2)

您似乎正在寻找update查询。如果您正在运行MySQL 8.0,则可以使用row_number()

update predmetip p
inner join (
    select p.*, row_number() over(partition by predp_id order by strp_id) rn
    from predmetip p
) p1 on p1.predp_id = p.predp_id and p1.strp_id = p.strp_id
set p.predp_nas = p1.rn

另一方面,如果您正在运行MySQL 5.x版本,则一个选择是使用相关子查询,如in Nick's answer所示。效果很好-我赞成尼克的回答-但当数据量变大时,性能往往会迅速下降,因为您需要扫描表中结果集中的每一行。

可以使用用户变量来执行此操作,但这很棘手:因为与explained in the documentation一样,select子句中表达式的求值顺序是不确定的,我们需要用相同的表达式求值和赋值; case对此非常有用。另一个重要的事情是,我们需要在子查询 生效之前对行进行排序。

您将如下编写select语句:

set @rn := 0, @strp_id = '';
select 
    predp_id,
    strp_id,
    @rn := case 
        when @strp_id  = strp_id then @rn + 1 -- read
        when @strp_id := strp_id then 1       -- assign
    end as predp_nas
from (
      select * 
      from predmetip
      order by strp_id, predp_id
) t

然后可以将其变为update

set @rn := 0, @strp_id = '';
update predmetip p
inner join (
    select 
        predp_id,
        strp_id,
        @rn := case 
            when @strp_id  = strp_id then @rn + 1
            when @strp_id := strp_id then 1
        end as predp_nas
    from (
          select * 
          from predmetip
          order by strp_id, predp_id
    ) t
) p1 on p1.predp_id = p.predp_id and p1.strp_id = p.strp_id
set p.predp_nas = p1.predp_nas;

Demo on DB Fiddle (首先感谢Nick创造了它)。

要了解有关用户变量及其技巧的更多信息,我建议使用this excellent answer by Madhur Bhaiya,它还包含另一个有趣的博客链接。