2015-07-20 28 views
1

考慮下面的例子:是否可以提高基於ID的行列表的排序性能?

SET NOCOUNT ON; 
    CREATE TABLE #Users 
    (
     ID   INT IDENTITY(1,1), 
     Name VARCHAR(50) 
    ); 
    CREATE CLUSTERED INDEX IDX_C_Users_UserID ON #Users(ID); 

    -- CREATE INDEX IDX_Users_Name ON #Users(Name); -- It doesn't work. 

    CREATE TABLE #Towns 
    (
     ID   INT IDENTITY(1,1), 
     Name VARCHAR(50) 
    ); 
    CREATE CLUSTERED INDEX IDX_C_Towns_UserID ON #Towns(ID) 

    CREATE TABLE #BeenHere 
    (
     ID INT IDENTITY(1,1), -- for some business reason we can't use clustered index on them 
     UserID INT, 
     TownID INT 
    ); 

    CREATE UNIQUE INDEX IDX_BEEN_THERE ON #BeenHere(TownID, UserID); 


    INSERT INTO #Towns 
    SELECT Prefix+Suffix FROM (
     SELECT Prefix, Suffix FROM 
     (SELECT 'China' UNION ALL 
     SELECT 'Ham' UNION ALL 
     SELECT 'Chicken' UNION ALL 
     SELECT 'Great' UNION ALL 
     SELECT 'Loud' 
     ) as A(Prefix) 
     CROSS JOIN 
     (SELECT 'town' UNION ALL 
     SELECT 'water' UNION ALL 
     SELECT ' City' UNION ALL 
     SELECT 'burg' UNION ALL 
     SELECT 'berg') AS B(Suffix) 
    ) Q 
    ORDER BY NEWID() 
    ; 


    INSERT INTO #Users(Name) 
    SELECT Name + ' ' + Surname FROM (
     SELECT Name, Surname FROM 
     (SELECT 'John' UNION ALL 
     SELECT 'Mary' UNION ALL 
     SELECT 'Ann' UNION ALL 
     SELECT 'Salomon' UNION ALL 
     SELECT 'Lisa' UNION ALL 
     SELECT 'Patricia' UNION ALL 
     SELECT 'David' UNION ALL 
     SELECT 'Patrick' UNION ALL 
     SELECT 'John' UNION ALL 
     SELECT 'Harry' UNION ALL 
     SELECT 'Richard' UNION ALL 
     SELECT 'George' 
     ) as A(Name) 
     CROSS JOIN 
     (SELECT 'Smith' UNION ALL 
     SELECT 'Kowalski' UNION ALL 
     SELECT 'Bush' UNION ALL 
     SELECT 'Truman' UNION ALL 
     SELECT 'Clinton' UNION ALL 
     SELECT 'Reagan' UNION ALL 
     SELECT 'Lincoln' UNION ALL 
     SELECT 'Goldberg' UNION ALL 
     SELECT 'Adams' UNION ALL 
     SELECT 'Wilson' UNION ALL 
     SELECT 'Carter') as B(Surname) 

    ) P 
    ORDER BY NEWID(); 


    INSERT INTO #BeenHere(UserID, TownID) 
    SELECT 
     TOP 10 PERCENT 
     #Users.ID, 
     #Towns.ID 
     FROM 
     #Users 
     CROSS JOIN 
     #Towns 
     ORDER BY NEWID(); 

SET NOCOUNT OFF; 

SELECT 
    Towns.Name, 
    (SELECT Users.ID, Users.Name FROM #Users Users INNER JOIN #BeenHere BH ON Users.ID = BH.UserID WHERE BH.TownID = Towns.ID ORDER BY Users.Name FOR XML PATH('User'), ROOT('Users'), TYPE) as BeenThere 
    FROM #Towns Towns 
    ORDER BY Towns.Name; 

DROP TABLE #BeenHere; 
DROP TABLE #Users; 
DROP TABLE #Towns; 

正如我們在執行計劃中看到,分揀用戶成本由去年的查詢所消耗的資源的78%。

是否可以在這些表上放置一些索引以提高排序性能?我無法向數據庫引入向後不兼容的更改,例如在#BeenHere(UserID, TownID)上提供聚簇索引。

+1

不,我們在執行計劃中看不到任何東西,因爲您沒有包含它。 –

+0

只是提醒一下,計劃中的百分比只是估計值 –

+0

那麼在第一個創建索引時,你已經用a關閉了它;爲什麼在#BeenHere中甚至都有ID INT IDENTITY?爲什麼在inserts上使用newID排序索引中的heck? – Paparazzi

回答

0

你的問題是使用相關的子查詢。停止使用它們並使用連接(如果必須的話,包括派生表)。相關的子查詢逐行運行,而不是針對整個集合,因此是性能高峯。一。

+0

它會削弱期望XML字段(遺留代碼庫)的客戶端。 – Ginden

1

我簡單地更換您的聚集索引成這樣:

CREATE CLUSTERED INDEX IDX_C_Users_Name_UserID ON #Users(Name, ID); 

所以,現在你的表是由Name排序,而不是ID

SORT運營商從執行計劃中消失。 enter image description here

UPDATE
正如你所說,你不能改變聚集索引。有一種方法可以做到這一點,如果你想。您的NONCLUSTERED INDEX僅用於名稱列是好的,但SQL Server決定不使用它。你可以做的是一個提示添加到您的表使用這個索引:

SELECT Towns.Name 
    , (
     SELECT Users.ID, Users.Name 
     FROM #Users Users WITH (INDEX (IDX_Users_Name)) 
     INNER JOIN #BeenHere BH 
      ON Users.ID = BH.UserID 
     WHERE BH.TownID = Towns.ID 
     ORDER BY Users.Name 
     FOR XML PATH('User'), ROOT('Users'), TYPE 
     ) AS BeenThere 
FROM #Towns Towns 
ORDER BY Towns.Name; 

那麼你的查詢將使用這個索引和排序操作將不再存在。但是,我不確定它是否是最有效的方法。 SQL Server必須掃描索引,而不是尋找它。 enter image description here

+0

我沒有使用外鍵來簡化測試用例。無論如何 - 我無法做出這樣的改變,因爲它需要修改數百個表格和數百個SP。 – Ginden

+1

我已經更新了我的答案。 –

相關問題