Найдите самую длинную последовательность значений в таблице - PullRequest
4 голосов
/ 02 ноября 2009

Это вопрос SQL, я думаю, что он сложный - я не уверен, что этого можно достичь простым предложением SQL или хранимой процедурой:

Я хочу найти номер самой длинной последовательности того же (известного) номера в столбце таблицы:

пример:

TABLE: 
DATE    SALEDITEMS
1/1/09       4
1/2/09       3
1/3/09       3
1/4/09       4
1/5/09       3

вызов sp / предложения для 4 даст 1 вызов sp / sentecne для 3 даст 2 как было 2 раза подряд номер 3.

Я использую SQL Server 2008.

Ответы [ 2 ]

1 голос
/ 02 ноября 2009

ОБНОВЛЕНИЕ: я сгенерировал миллион строк случайных данных и отказался от рекурсивного решения 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;
0 голосов
/ 02 ноября 2009

Не проверено, потому что вы не предоставили DDL и пример данных:

DECLARE @SALEDITEMS INT;
SET @SALEDITEMS=3;
SELECT MAX(cnt) FROM(
SELECT COUNT(*) FROM YourTable JOIN (
SELECT y1.[Date] AS d1, y2.[Date] AS d2
FROM YourTable AS y1 JOIN YourTable AS y2 
ON y1.SALEDITEMS=@SALEDITEMS AND y2.SALEDITEMS=@SALEDITEMS
AND NOT EXISTS(SELECT 1 FROM YourTable AS y 
WHERE y.SALEDITEMS<>@SALEDITEMS
AND y1.[Date] < y.[Date] AND y.[Date] < y2.[Date])
) AS t
WHERE [Date] BETWEEN t.d1 AND t.d2
) AS t;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...