ROW_NUMBER()性能优化

时间:2013-11-26 23:29:17

标签: sql sql-server

首先,我想提一下,我已经检查了所有其他问题,但没有一个与我的相似,所以我不认为这是重复的。

我有两个表格,“Article_tbl”到目前为止有超过300,000行,而“ArticleZone_tbl”的行数几乎相同。

“Article_tbl”包含Identity主键“ArticleID”。 “ArticleZone_tbl”包含由三列组成的主键,“ArticleID”,“ChannelID”,“ZoneID”;其中“ArticleID”是“Article_tbl”

中的外键

在列上创建非聚集索引以按顺序排序。

SQL查询:

WITH OrderedOrders AS(
Select ROW_NUMBER() Over(Order by LastEditDate desc, ArticleOrder Asc, LastEditDateTime desc) as RowNum, dbo.Article_tbl.*, ArticleZone_tbl.ChannelID, ArticleZone_tbl.ZoneID, ArticleZone_tbl.ArticleOrder
From Article_tbl INNER JOIN ArticleZone_tbl
    ON dbo.Article_tbl.ArticleID = dbo.ArticleZone_tbl.ArticleID
Where ChannelID=1 And ZoneID=0)

SELECT * FROM OrderedOrders Where RowNum Between 1 And 10

以上查询大约需要2秒钟才能完成,有没有办法优化此查询?

更多信息: 操作系统:Windows WebServer 2008R2 SQL Sever:2008R2 内存:32GB 硬盘:160GB SSD

提前致谢。

祝你好运, McHaimech

3 个答案:

答案 0 :(得分:4)

您可以尝试在两个表格上创建Indexed View

CREATE VIEW dbo.YourIndexedView
WITH SCHEMABINDING 
AS
    SELECT  az.ArticleID,
            az.ChannnelID,
            az.ZoneID,
            a.LastEditDate,
            a.LastEditDateTime,
            az.ArticleOrder
    FROM    dbo.Article_tbl a
            INNER JOIN dbo.ArticleZone_tbl az
                ON a.ArticleID = az.AtricleID;

GO
CREATE UNIQUE CLUSTERED INDEX UQ_YourIndexView_ArticleID_ChannelID_ZoneID 
    ON dbo.YourIndexedView (ArticleID, ChannelID, ZoneID);

准备好聚集索引后,您可以创建一个非聚集索引来帮助进行排序:

CREATE NONCLUSTERED INDEX IX_YourIndexedView_LastEditDate_ArticleOrder_LastEditDateTime
    ON dbo.YourIndexedView (LastEditDate DESC, ArticleOrder ASC, LastEditDateTime DESC);

然后,您可以在查询中引用它:

WITH OrderedOrders AS
(   SELECT  RowNum = ROW_NUMBER() OVER(ORDER BY LastEditDate DESC, ArticleOrder ASC, LastEditDateTime DESC),
            ArticleID,
            ChannelID,
            ZoneID,
            LastEditDateTime,
            ArticleOrder
    FROM    dbo.YourIndexedView WITH (NOEXPAND)
    WHERE   ChannelID = 1 
    AND     ZoneID = 0
)
SELECT  *
FROM    OrderedOrders
WHERE   RowNum BETWEEN 1 AND 10;

N.B。我可能错过了您的文章表中的某些列,但我无法从问题

中推断出它们

此外,如果您的查询始终具有相同的区域和频道,则可以过滤视图,然后您的聚集索引列将变为ArticleID

CREATE VIEW dbo.YourIndexedView
WITH SCHEMABINDING 
AS
    SELECT  az.ArticleID,
            az.ChannnelID,
            az.ZoneID,
            a.LastEditDate,
            a.LastEditDateTime,
            az.ArticleOrder
    FROM    Article_tbl a
            INNER JOIN ArticleZone_tbl az
                ON a.ArticleID = az.AtricleID
    WHERE   az.ChannelID = 1
    AND     Az.ZoneID = 1;

GO
CREATE UNIQUE CLUSTERED INDEX UQ_YourIndexView_ArticleID 
    ON dbo.YourIndexedView (ArticleID);

这意味着您的索引会更小,使用起来更快。

答案 1 :(得分:1)

如你所说“相同查询”Over(按Article_tbl.ArticleID asc排序)“需要40ms”,毫无疑问你缺少一个索引。您应该研究查询计划(包括SSMS中的实际执行计划按钮) 所有字段覆盖您的OVER(ORDER BY ..)的一个索引可能会给您带来好的结果。 ArticleId在这里是有意义的,因为它是你的集群,尊重你的OVER子句的ASC / DESC命令。

尝试:

CREATE INDEX xxx on Article_tcl(LastEditDate desc, ArticleOrder asc, LastEditDateTime desc)

asc是您不需要指定的默认值,此处为了清晰起见

答案 2 :(得分:0)

我将查询分解为组件部分。具体来说,您不需要返回子查询中的所有列数据,这些数据确定行号和主键。此外,分解查询将使调试和优化变得更加容易。

CREATE TABLE #OrderedRows( RowNumber int primary key, ArticleID int);
Create nonclustered index IX_OrderedRows_ByArticled on #OrderedRows (ArticleID);

insert into #OrderedRows
Select ROW_NUMBER() Over(Order by LastEditDate desc, ArticleOrder Asc, LastEditDateTime desc) as RowNumber, Article_tbl.ArticleID
From Article_tbl 
INNER JOIN ArticleZone_tbl
    ON dbo.Article_tbl.ArticleID = dbo.ArticleZone_tbl.ArticleID
Where ChannelID=1 And ZoneID=0

SELECT oo.RowNumber, at.*, azt.ChannelID, azt.ZoneID, azt.ArticleOrder
FROM #OrderedOrders oo
join Article_tbl at
  on oo.ArticleID = at.ArticleID
INNER JOIN ArticleZone_tbl azt
    ON at.ArticleID = azt.ArticleID
    and azt.ChannelID = 1
    and azt.ZoneID = 0
Where oo.RowNumber Between 1 And 10

Drop table #OrderedRows;

此外,添加以下索引会影响性能吗?

create nonclustered index IX_Article_tbl_ByArticle_IncOrder on Article_tbl (ArticleID) Include (ArticleOrder);
create nonclustered index IX_ArticleZone_tbl_Cover on ArticleZone_tbl (ChannelID, ZoneID, ArticleID, ArticleDate, ArticleTime);