ОБНОВЛЕНИЕ: я сгенерировал миллион строк случайных данных и отказался от рекурсивного решения CTE, так как его план запросов не использовал индексы в оптимизаторе.
Но нерекурсивное решение, которое я первоначально разместил, оказалось отличным, если на нем был дополнительный некластеризованный индекс (SALEDITEMS, [DATE]). Это имеет смысл, поскольку запрос должен фильтроваться в обоих направлениях (как по дате, так и по SALEDITEMS). С этим дополнительным индексом запросы на миллион строк возвращаются менее чем за 2 секунды на моем (не очень громоздком) настольном компьютере. Без этого индекса запрос был медленным.
Кстати, это отличный пример того, как оптимизация запросов на основе затрат SQL Server в некоторых случаях полностью не работает. Стоимость рекурсивного решения CTE (на моем ПК) составляет 42, и его завершение занимает не менее нескольких минут. Стоимость нерекурсивного решения составляет 15 446 (!!!) и выполняется за 1,5 секунды. Мораль этой истории: сравнивая планы запросов SQL Server, не думайте, что стоимость обязательно соотносится с производительностью запроса!
В любом случае, вот решение, которое я бы порекомендовал (тот же нерекурсивный CTE, который я выложил ранее):
DECLARE @SALEDITEMS INT = 3;
WITH SalesNoMatch ([DATE], SALEDITEMS, NoMatchDate)
AS
(
SELECT [DATE], SALEDITEMS,
(SELECT MIN([DATE]) FROM Sales s2 WHERE s2.SALEDITEMS <> @SALEDITEMS
AND s2.[DATE] > s1.[DATE]) as NoMatchDate
FROM Sales s1
)
, SalesMatchCount ([DATE], ConsecutiveCount) AS
(
SELECT [DATE], 1+(SELECT COUNT(1) FROM Sales s2 WHERE s2.[DATE] > s1.[DATE] AND s2.[DATE] < NoMatchDate)
FROM SalesNoMatch s1
WHERE s1.SALEDITEMS = @SALEDITEMS
)
SELECT MAX(ConsecutiveCount)
FROM SalesMatchCount;
Вот DDL, который я использовал для проверки этого, включая индексы, которые вам понадобятся:
CREATE TABLE [Sales](
[DATE] date NOT NULL,
[SALEDITEMS] int NOT NULL
);
CREATE UNIQUE CLUSTERED INDEX IX_Sales ON Sales ([DATE]);
CREATE UNIQUE NONCLUSTERED INDEX IX_Sales2 ON Sales (SALEDITEMS, [DATE]);
И вот как я создал свои тестовые данные - 1 000 001 строк с возрастающими датами, когда SALEDITEMS произвольно установлены в диапазоне от 1 до 10.
INSERT INTO Sales ([DATE], SALEDITEMS)
VALUES ('1/1/09', 5)
DECLARE @i int = 0;
WHILE (@i < 1000000)
BEGIN
INSERT INTO Sales ([DATE], SALEDITEMS)
SELECT DATEADD (d, 1, (SELECT MAX ([DATE]) FROM Sales)), ABS(CHECKSUM(NEWID())) % 10 + 1
SET @i = @i + 1;
END
Вот рекурсивное решение CTE, от которого я отказался:
ОБЪЯВИТЬ @SALEDITEMS INT = 3;
-- recursive CTE solution (remember to set MAXRECURSION!)
WITH SalesRowNum ([DATE], SALEDITEMS, RowNum)
AS
(
SELECT [DATE], SALEDITEMS, ROW_NUMBER() OVER (ORDER BY s1.[DATE]) as RowNum
FROM Sales s1
)
, SalesCTE (RowNum, [DATE], ConsecutiveCount)
AS
(
SELECT s1.RowNum, s1.[DATE], 1 AS ConsecutiveCount
FROM SalesRowNum s1
WHERE SALEDITEMS = @SALEDITEMS
UNION ALL
SELECT s1.RowNum, s1.[DATE], ConsecutiveCount + 1 AS ConsecutiveCount
FROM SalesRowNum s1
INNER JOIN SalesCTE s2 ON s1.RowNum = s2.RowNum + 1
WHERE SALEDITEMS = @SALEDITEMS
)
SELECT MAX(ConsecutiveCount)
FROM SalesCTE;