2014-12-18 5 views
1

Предположим, у меня есть таблица, которая выглядит немного как это в SQL Server 2008:инкремента счетчика, основанный на перегородке

UID Std_RecordID 
------------------- 
1  10 
2  10 
3  12 
4  10 
5  10 
6  10 
7  12 

В основном будет последовательность 10 с последующим одним 12. 10 также может быть в списке 14, 50, 21, 24, 31, а 12 может быть в списке 16, 52, 23, 26, 33 соответственно. Приращение 2 в значительной степени означает конец набора.

Мне нужно увеличивать счетчик каждый раз, когда есть новый набор.

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

select ROW_NUMBER() over (partition by Std_RecordId order by UID) 'Ind' 
,UID 
from @inputTable 

Но это не совсем то, что я хочу, учитывая, что он будет производить следующее:

UID Std_RecordId Ind 
--------------------------- 
1  10    1 
2  10    2 
3  12    1 
4  10    1 
5  10    2 
6  10    3 
7  12    1 

I вам нужно сделать что-то вроде этого:

UID Std_RecordId Ind 
------------------------------ 
1  10    1 
2  10    1 
3  12    1 
4  10    2 
5  10    2 
6  10    2 
7  12    2 

Как можно решить эту проблему без разрешения rting к итерации? Я пытаюсь избавиться от итерации в процедуре, в которой я работаю, поскольку итерация является самой медленной частью процедуры (она загружает другие вещи, кроме этого).

ответ

1

Вы можете упростить свою проблему до «Сколько раз была сделана запись со значением 12, появившаяся до текущей записи», а затем добавьте ее к результату. Для того, чтобы получить количество записей, которое вы можете использовать OUTER APPLY:

DECLARE @InputTable TABLE (UID INT, Std_RecordId INT); 
INSERT @InputTable (UID, Std_RecordId) 
VALUES (1, 10), (2, 10), (3, 12), (4, 10), (5, 10), (6, 10), (7, 12); 

SELECT i.UID, 
     i.Std_RecordId, 
     t.Ind 
FROM @InputTable AS i 
     OUTER APPLY 
     ( SELECT Ind = COUNT(*) + 1 
      FROM @InputTable AS t 
      WHERE t.Std_RecordId = 12 
      AND  t.UID < i.UID 
     ) AS t; 

EDIT

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

сценария я побежал был:

DECLARE @InputTable TABLE (UID INT, Std_RecordId INT); 

INSERT @InputTable (UID, Std_RecordId) 
SELECT TOP 50000 
     ROW_NUMBER() OVER(ORDER BY a.object_id), 
     CEILING(RAND(CHECKSUM(NEWID())) * 20) 
FROM sys.all_objects a 
     CROSS JOIN sys.all_objects b; 

CREATE TABLE #InputTable (UID INT, Std_RecordId INT); 
INSERT #InputTable (UID, Std_RecordId) 
SELECT UID, Std_RecordId 
FROM @InputTable; 

SET STATISTICS TIME ON; 

SELECT i.UID, 
     i.Std_RecordId, 
     t.Ind 
FROM @InputTable AS i 
     OUTER APPLY 
     ( SELECT Ind = COUNT(*) + 1 
      FROM @InputTable AS t 
      WHERE t.Std_RecordId = 12 
      AND  t.UID < i.UID 
     ) AS t; 

SELECT i.UID, 
     i.Std_RecordId, 
     t.Ind 
FROM #InputTable AS i 
     OUTER APPLY 
     ( SELECT Ind = COUNT(*) + 1 
      FROM #InputTable AS t 
      WHERE t.Std_RecordId = 12 
      AND  t.UID < i.UID 
     ) AS t; 

SET STATISTICS TIME OFF; 

DROP TABLE #InputTable; 

Поскольку я увеличил размер выборки, размер зазора увеличился, но для 10 000 строк (мне стало скучно ждать больше) переменная таблицы последовательно занимала около 7,9 секунды, тогда как временная таблица составляла в среднем 0,4. Я запустил это один раз для 50 000 строк, а переменная таблицы заняла 190 секунд, таблица temp заняла 4.6, поэтому огромная разница.

Другим преимуществом является то, что ваша временная таблица может быть проиндексирована, однако лучшая производительность, которую я нашел в таблице temp, заключалась в создании новой таблицы temp для записи позиций ваших маркеров, а затем использовать ее, чтобы дать исходную таблицу ранг:

DECLARE @InputTable TABLE (UID INT, Std_RecordId INT); 

INSERT @InputTable (UID, Std_RecordId) 
SELECT TOP 1000000 
     ROW_NUMBER() OVER(ORDER BY a.object_id), 
     CEILING(RAND(CHECKSUM(NEWID())) * 20) 
FROM sys.all_objects a 
     CROSS JOIN sys.all_objects b; 

DECLARE @Counter TABLE (UID INT PRIMARY KEY, Ind INT NOT NULL); 
INSERT @Counter 
SELECT UID, ROW_NUMBER() OVER(ORDER BY UID) + 1 
FROM @InputTable 
WHERE Std_RecordId = 12; 

SELECT i.UID, 
     i.Std_RecordId, 
     Ind = ISNULL(t.Ind, 1) 
FROM @InputTable AS i 
     OUTER APPLY 
     ( SELECT TOP 1 Ind 
      FROM @Counter AS t 
      WHERE t.UID < i.UID 
     ) AS t 
ORDER BY i.UID; 

для 50000 строк это последовательно побежал в менее чем за секунду, даже за миллион он работает в течение 15-20 секунд.

+0

Я предполагаю, что могу заменить «t.Std_RecordId = 12» моим списком терминаторов, правильно? Если это так, ты качаешься, и я благодарю тебя, сэр! – Logan

+0

Когда я получу свою процедуру, я смогу проверить ее и пометить ее правильно. :) – Logan

+0

Я тестировал его и, похоже, работает, но он довольно медленный.Требуется 10m30s, чтобы пройти 42k записей, что совсем не так :( – Logan

Смежные вопросы