2015-07-20 62 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

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

相关问题