2009-11-01 37 views
4

這是一個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 

要求4 SP /句話會給1調用SP/sentecne 3會給2 ,因爲在第3行中有2次。

我運行SQL Server 2008的

回答

1

更新:我產生一百萬行隨機數據,並放棄了遞歸CTE解決方案,因爲其查詢計劃沒有很好地利用優化器中的索引。

但是,原始發佈的非遞歸解決方案實際上效果很好,只要(SALEDITEMS,[DATE])上有一個額外的非聚集索引即可。這是有道理的,因爲查詢需要在兩個方向進行過濾(按日期和SALEDITEMS)。有了這個額外的索引,一百萬行的查詢在不到2秒的時間內就返回了我的(不是很強壯的)桌面數學運算。如果沒有這個索引,查詢就會很慢。

順便說一句,這是SQL Server基於成本的查詢優化在某些情況下完全崩潰的一個很好的例子。遞歸CTE解決方案的成本(在我的PC上)爲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和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 
SALEDITEMS上升日期我的測試數據 - 1,000,001行

這是我放棄的遞歸CTE解決方案: DECLARE @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

謝謝!在一個小表(1400行)上嘗試了第二個代碼(非遞歸)。在30000行的表上嘗試過它,它永遠不會結束,但日期列上沒有索引,所以我想我需要先開始修復它。我可以在聲明中設置MaxRecursion嗎?我應該如何取消設置或將其恢復爲默認值?遞歸會更快嗎? – Dani 2009-11-02 00:08:02

+0

我用更多信息更新瞭解決方案 - 一旦表格有很多行,非遞歸解決方案看起來就像是更好的解決方案。而且實際上你需要兩個索引 - 否則(正如你發現的那樣),當有很多行時,查詢會非常慢。添加第二個索引(見上面),你就可以在一秒鐘內完成你的30,000行查詢。 – 2009-11-02 01:57:37

0

未經檢驗的,因爲你沒有提供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 [email protected] AND [email protected] 
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; 
+0

我明白了......它可能工作,有一個錯誤附近最後的WHERE不知道什麼 – Dani 2009-11-01 23:14:21