Головоломка SQL - увеличивайте значение на единицу, когда два столбца меняют значение - PullRequest
0 голосов
/ 15 марта 2019

Я ненавижу циклы while и люблю избегать их, когда это возможно.

Я столкнулся со сценарием, для решения которого может потребоваться цикл while.Тем не менее, позвольте мне спросить моих друзей в Интернете, прежде чем я закодирую цикл while.

Представьте, что у вас есть следующие данные ...

declare @tv_mytable table(recnum int identity(1,1) primary key, widgettype char(3), issecondarywidget bit default(0), priority int)

    insert @tv_mytable(widgettype, issecondarywidget) values('BAT', 0)
    insert @tv_mytable(widgettype, issecondarywidget) values('BAT', 0)
    insert @tv_mytable(widgettype, issecondarywidget) values('ANT', 0)
    insert @tv_mytable(widgettype, issecondarywidget) values('ANT', 0)
    insert @tv_mytable(widgettype, issecondarywidget) values('ANT', 1)
    insert @tv_mytable(widgettype, issecondarywidget) values('ANT', 1)
    insert @tv_mytable(widgettype, issecondarywidget) values('BAT', 1)
    insert @tv_mytable(widgettype, issecondarywidget) values('BAT', 1)

Вы попадаете с этим ...

recnum      widgettype issecondarywidget priority
----------- ---------- ----------------- -----------
1           BAT        0                 NULL
2           BAT        0                 NULL
3           ANT        0                 NULL
4           ANT        0                 NULL
5           ANT        1                 NULL
6           ANT        1                 NULL
7           BAT        1                 NULL
8           BAT        1                 NULL

Я хочу обновить столбец приоритетовтак что он начинается с 1, и каждый раз, когда виджет или issecondarywidget изменяют значение, приоритет увеличивается на 1.

Данные будут выглядеть следующим образом ...

recnum      widgettype issecondarywidget priority
----------- ---------- ----------------- -----------
1           BAT        0                 1
2           BAT        0                 1
3           ANT        0                 2
4           ANT        0                 2
5           ANT        1                 3
6           ANT        1                 3
7           BAT        1                 4
8           BAT        1                 4

Спасибо

Ответы [ 2 ]

3 голосов
/ 15 марта 2019

В этом случае вы можете сгенерировать идентификатор, используя dense_rank(). Для уникального значения:

select t.*, dense_rank() over (order by widgettype issecondarywidget) as priority
from t;

Если вы хотите сохранить исходный порядок, вам нужен еще один уровень оконных функций:

select t.*, dense_rank() over (order by min_id) as priority
from (select t.*, min(id) over (order by widgettype issecondarywidget) as min_id
      from t
     ) t

Чтобы поместить это в update:

with toupdate as (
      select t.*, dense_rank() over (order by min_id) as new_priority
      from (select t.*, min(id) over (order by widgettype issecondarywidget) as min_id
            from t
           ) t
     )
update toupdate
     set priority = new_priority;
2 голосов
/ 15 марта 2019

Немного другой, хотя и похожий, подход к решению Гордана с использованием CTE и ROW_NUMBER для построения групп:

WITH CTE AS(
    SELECT tv.recnum,
           tv.widgettype,
           tv.issecondarywidget,
           ROW_NUMBER() OVER (ORDER BY tv.recnum) - 
           ROW_NUMBER() OVER (PARTITION BY CONCAT(tv.widgettype,tv.issecondarywidget) ORDER BY tv.recnum) AS Grp
    FROM @tv_mytable tv)
SELECT CTE.recnum,
       CTE.widgettype,
       CTE.issecondarywidget,
       DENSE_RANK() OVER(ORDER BY CTE.Grp) AS priotity
FROM CTE;

Если вы хотите это как UPDATE, вы должны сделать:

WITH CTE AS(
    SELECT tv.recnum,
           tv.widgettype,
           tv.issecondarywidget,
           [Priority],
           ROW_NUMBER() OVER (ORDER BY tv.recnum) - 
           ROW_NUMBER() OVER (PARTITION BY CONCAT(tv.widgettype,tv.issecondarywidget) ORDER BY tv.recnum) AS Grp
    FROM @tv_mytable tv),
New AS(
    SELECT CTE.recnum,
           CTE.widgettype,
           CTE.issecondarywidget,
           [Priority],
           DENSE_RANK() OVER(ORDER BY CTe.Grp) AS NewPriotity
    FROM CTE)
UPDATE New
SET [priority] = NewPriotity;


SELECT *
FROM @tv_mytable
...