Как можно создать итератор в SQL, который считает строки, как если бы они были в наборе? - PullRequest
0 голосов
/ 27 февраля 2020

Я искал способы сделать это в одном операторе UPDATE, но не увенчался успехом.

Это пример того, как выглядит набор данных, с которым я работаю:

+-------------------------+----------+--------------+----+--------+
|        TIMESTAMP        | USERNAME |    VALUE     | ID | IsDupe |
+-------------------------+----------+--------------+----+--------+
| 2020-02-12 07:00:03.000 | LINA     | ORDER1       |  1 |      0 |
| 2020-02-12 07:00:03.000 | LINA     | ITEM1        |  2 |      0 |
| 2020-02-12 07:09:09.000 | LINA     | FINISH BUILD |  3 |      0 |
| 2020-02-12 07:09:10.000 | LINA     | ORDER1       |  4 |      0 |
| 2020-02-12 07:09:11.000 | LINA     | ITEM2        |  5 |      0 |
| 2020-02-12 07:24:07.000 | LINA     | FINISH BUILD |  6 |      0 |
| 2020-02-12 07:24:08.000 | NAGA     | ORDER2       |  7 |      0 |
| 2020-02-12 07:24:10.000 | NAGA     | ITEM3        |  8 |      0 |
| 2020-02-12 07:45:06.000 | NAGA     | FINISH BUILD |  9 |      0 |
| 2020-02-12 07:45:12.000 | NAGA     | FINISH BUILD | 10 |      1 |
| 2020-02-12 07:45:13.000 | XELLOS   | ORDER3       | 11 |      0 |
| 2020-02-12 07:45:14.000 | XELLOS   | ITEM4        | 12 |      0 |
| 2020-02-12 07:56:36.000 | XELLOS   | FINISH BUILD | 13 |      0 |
| 2020-02-12 07:56:39.000 | GOURRY   | ORDER4       | 14 |      0 |
| 2020-02-12 07:56:40.000 | GOURRY   | ITEM5        | 15 |      0 |
| 2020-02-12 08:30:11.000 | GOURRY   | FINISH BUILD | 17 |      0 |
+-------------------------+----------+--------------+----+--------+

Я хочу создать дополнительный столбец, который работает как итератор, разбивая каждый из этих строк в наборы по три, например, так:

+-------------------------+----------+--------------+-------+--------+-------+
|        TIMESTAMP        | USERNAME |    VALUE     | IDCol | IsDupe | SetID |
+-------------------------+----------+--------------+-------+--------+-------+
| 2020-02-12 07:00:03.000 | LINA     | ORDER1       |     1 |      0 | 1     |
| 2020-02-12 07:00:03.000 | LINA     | ITEM1        |     2 |      0 | 1     |
| 2020-02-12 07:09:09.000 | LINA     | FINISH BUILD |     3 |      0 | 1     |
| 2020-02-12 07:09:10.000 | LINA     | ORDER1       |     4 |      0 | 2     |
| 2020-02-12 07:09:11.000 | LINA     | ITEM2        |     5 |      0 | 2     |
| 2020-02-12 07:24:07.000 | LINA     | FINISH BUILD |     6 |      0 | 2     |
| 2020-02-12 07:24:08.000 | NAGA     | ORDER2       |     7 |      0 | 3     |
| 2020-02-12 07:24:10.000 | NAGA     | ITEM3        |     8 |      0 | 3     |
| 2020-02-12 07:45:06.000 | NAGA     | FINISH BUILD |     9 |      0 | 3     |
| 2020-02-12 07:45:12.000 | NAGA     | FINISH BUILD |    10 |      1 | NULL  |
| 2020-02-12 07:45:13.000 | XELLOS   | ORDER3       |    11 |      0 | 4     |
| 2020-02-12 07:45:14.000 | XELLOS   | ITEM4        |    12 |      0 | 4     |
| 2020-02-12 07:56:36.000 | XELLOS   | FINISH BUILD |    13 |      0 | 4     |
| 2020-02-12 07:56:39.000 | GOURRY   | ORDER4       |    14 |      0 | 5     |
| 2020-02-12 07:56:40.000 | GOURRY   | ITEM5        |    15 |      0 | 5     |
| 2020-02-12 08:30:11.000 | GOURRY   | FINISH BUILD |    17 |      0 | 5     |
+-------------------------+----------+--------------+-------+--------+-------+

Я пытался искать итеративные операторы в SQL, но были проблемы с производительностью, так как это будет относительно большой набор данных и этот оператор необходимо будет выполнить в течение дня, что повлияет на производительность.

Также обратите внимание, что в наборе данных могут быть дубликаты или другие ошибки. Строки, где IsDupe установлен в 1, должны игнорироваться в этом операторе.

Я пытался создать курсор, чтобы сделать это, но сталкивался с многочисленными проблемами с синтаксисом, а также просто с общими неопытными курсорами написания :

DECLARE @MyCursor CURSOR;
DECLARE @SetID INT;
DECLARE @OUTPUTNUM TINYINT;
DECLARE @COUNTER TINYINT;
BEGIN
    SET @MyCursor = CURSOR LOCAL FAST_FORWARD FOR
    SELECT IsDupe from dbo.MyDataTable
        WHERE IsDupe != 1
    OPEN @MyCursor 
    FETCH NEXT FROM @MyCursor INTO @SetID

    WHILE @@FETCH_STATUS = 0 BEGIN
      SET @COUNTER = 0;
      SET @OUTPUTNUM = 1;
      WHILE @COUNTER < 3
        BEGIN 
            UPDATE dbo.MyDataTable SET dbo.MyDataTable.SetID = @OUTPUTNUM
            SET @COUNTER = @COUNTER + 1
        END 
        SET @COUNTER = 0;
        SET @OUTPUTNUM =  @OUTPUTNUM + 1
      FETCH NEXT FROM @MyCursor 
      INTO @SetID 
    END; 

    CLOSE @MyCursor ;
    DEALLOCATE @MyCursor;
END;

Когда я запускаю это, я получаю следующее сообщение:

[2:07:54 PM]    Started executing query at Line 1

Commands completed successfully. 

Total execution time: 00:00:00.026

Но результатов нет, все значения столбца SetID по-прежнему равны нулю.

1 Ответ

1 голос
/ 27 февраля 2020

Вы можете сделать это без курсора, используя windows функции:

select [TIMESTAMP], USERNAME, VALUE, ID, IsDupe,  
case
when IsDupe = 1 then null
     else DENSE_RANK()over(order by GroupID)
end as SetID 
from(
    select 
    *, 
    case when value like 'ORDER%' then ID
         when value like 'ITEM%' then lag(ID,1)over (order by ID)  
         when value like 'FINISH BUILD%' then lag(ID,2)over (order by ID)
    end as GroupID
    from #tmp where IsDupe = 0
)a 

    union
    select 
    [TIMESTAMP], USERNAME, VALUE, ID, IsDupe, null as SetID   
    from #tmp where IsDupe = 1
order by ID

Вот мой полный пример:

drop table #tmp

select '2020-02-12 07:00:03.000' as TIMESTAMP, 'LINA'  as USERNAME   , 'ORDER1'  as VALUE     ,  1 as ID ,      0 as IsDupe   into #tmp
union select  '2020-02-12 07:00:03.000' , 'LINA'     , 'ITEM1'        ,  2 ,      0 
union select  '2020-02-12 07:09:09.000' , 'LINA'     , 'FINISH BUILD' ,  3 ,      0 
union select  '2020-02-12 07:09:10.000' , 'LINA'     , 'ORDER1'       ,  4 ,      0 
union select  '2020-02-12 07:09:11.000' , 'LINA'     , 'ITEM2'        ,  5 ,      0 
union select  '2020-02-12 07:24:07.000' , 'LINA'     , 'FINISH BUILD' ,  6 ,      0 
union select  '2020-02-12 07:24:08.000' , 'NAGA'     , 'ORDER2'       ,  7 ,      0 
union select  '2020-02-12 07:24:10.000' , 'NAGA'     , 'ITEM3'        ,  8 ,      0 
union select  '2020-02-12 07:45:06.000' , 'NAGA'     , 'FINISH BUILD' ,  9 ,      0 
union select  '2020-02-12 07:45:12.000' , 'NAGA'     , 'FINISH BUILD' , 10 ,      1 
union select  '2020-02-12 07:45:13.000' , 'XELLOS'   , 'ORDER3'       , 11 ,      0 
union select  '2020-02-12 07:45:14.000' , 'XELLOS'   , 'ITEM4'        , 12 ,      0 
union select  '2020-02-12 07:56:36.000' , 'XELLOS'   , 'FINISH BUILD' , 13 ,      0 
union select  '2020-02-12 07:56:39.000' , 'GOURRY'   , 'ORDER4'       , 14 ,      0 
union select  '2020-02-12 07:56:40.000' , 'GOURRY'  , 'ITEM5'        , 15 ,      0 
union select  '2020-02-12 08:30:11.000' , 'GOURRY'   , 'FINISH BUILD' , 17 ,      0  
order by ID

select [TIMESTAMP], USERNAME, VALUE, ID, IsDupe,  
case
when IsDupe = 1 then null
     else DENSE_RANK()over(order by GroupID)
end as SetID 
from(
    select 
    *, 
    case when value like 'ORDER%' then ID
         when value like 'ITEM%' then lag(ID,1)over (order by ID)  
         when value like 'FINISH BUILD%' then lag(ID,2)over (order by ID)
    end as GroupID
    from #tmp where IsDupe = 0
)a 

    union
    select 
    [TIMESTAMP], USERNAME, VALUE, ID, IsDupe, null as SetID   
    from #tmp where IsDupe = 1
order by ID

Вывод:

        TIMESTAMP         USERNAME     VALUE      IDCol  IsDupe  SetID 
 2020-02-12 07:00:03.000  LINA      ORDER1            1       0  1     
 2020-02-12 07:00:03.000  LINA      ITEM1             2       0  1     
 2020-02-12 07:09:09.000  LINA      FINISH BUILD      3       0  1     
 2020-02-12 07:09:10.000  LINA      ORDER1            4       0  2     
 2020-02-12 07:09:11.000  LINA      ITEM2             5       0  2     
 2020-02-12 07:24:07.000  LINA      FINISH BUILD      6       0  2     
 2020-02-12 07:24:08.000  NAGA      ORDER2            7       0  3     
 2020-02-12 07:24:10.000  NAGA      ITEM3             8       0  3     
 2020-02-12 07:45:06.000  NAGA      FINISH BUILD      9       0  3     
 2020-02-12 07:45:12.000  NAGA      FINISH BUILD     10       1  NULL  
 2020-02-12 07:45:13.000  XELLOS    ORDER3           11       0  4     
 2020-02-12 07:45:14.000  XELLOS    ITEM4            12       0  4     
 2020-02-12 07:56:36.000  XELLOS    FINISH BUILD     13       0  4     
 2020-02-12 07:56:39.000  GOURRY    ORDER4           14       0  5     
 2020-02-12 07:56:40.000  GOURRY    ITEM5            15       0  5     
 2020-02-12 08:30:11.000  GOURRY    FINISH BUILD     17       0  5      
...