2

我在使用数百万行表时遇到了一些严重的性能问题,我觉得我应该能够相当快地获得结果。这是我所拥有的,我如何查询它,以及它需要多长时间:

  • 我正在运行 SQL Server 2008 Standard,因此分区目前不是一个选项

  • 我正在尝试汇总过去 30 天内特定帐户的所有库存的所有视图。

  • 所有视图都存储在下表中:

创建表 [dbo].[LogInvSearches_Daily](
    [ID] [bigint] IDENTITY(1,1) 非空,
    [Inv_ID] [int] 非空,
    [Site_ID] [int] 非空,
    [LogCount] [int] 非空,
    [LogDay] [smalldatetime] 非空,
 约束 [PK_LogInvSearches_Daily] 主键集群
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) 开 [主要]
  • 该表有 132,000,000 条记录,超过 4 个演出。

  • 表中 10 行的样本:

ID Inv_ID Site_ID LogCount LogDay
-------- ----------- ----------- -------- --- ------------
1 486752 48 14 2009-07-21 00:00:00
2 119314 51 16 2009-07-21 00:00:00
3 313678 48 25 2009-07-21 00:00:00
4 298863 0 1 2009-07-21 00:00:00
5 119996 0 2 2009-07-21 00:00:00
6 463777 534 7 2009-07-21 00:00:00
7 339976 503 2 2009-07-21 00:00:00
8 333501 570 4 2009-07-21 00:00:00
9 453955 0 12 2009-07-21 00:00:00
10 443291 0 4 2009-07-21 00:00:00

(10 行受影响)
  • 我在 LogInvSearches_Daily 上有以下索引:
/****** 对象:索引 [IX_LogInvSearches_Daily_LogDay] 脚本日期:05/12/2010 11:08:22 ******/
在 [dbo] 上创建非聚类索引 [IX_LogInvSearches_Daily_LogDay]。[LogInvSearches_Daily]
(
    [日志日] ASC
)
包括([Inv_ID],
[LogCount]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
  • 我只需要从特定帐户 ID 的库存中提取库存。我也有一个关于库存的索引。

我正在使用以下查询来聚合数据并为我提供前 5 条记录。此查询当前需要 24 秒才能返回 5 行:

文本
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------
选择前 5 名
    Sum(LogCount) AS 浏览量
    , DENSE_RANK() OVER(ORDER BY Sum(LogCount) DESC, Inv_ID DESC) AS Rank
    , Inv_ID
FROM LogInvSearches_Daily D (NOLOCK)
在哪里
    LogDay > DateAdd(d, -30, getdate())
    并且存在(
        从 propertyControlCenter.dbo.Inventory (NOLOCK) 中选择 NULL,其中 Acct_ID = 18731 AND Inv_ID = D.Inv_ID
    )
按 Inv_ID 分组


(1 行受影响)

文本
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------
  |--顶部(顶部表达式:((5)))
       |--序列项目(DEFINE:([Expr1007]=dense_rank))
            |--细分市场
                 |--细分市场
                      |--排序(排序方式:([Expr1006] DESC,[D].[Inv_ID] DESC))
                           |--Stream Aggregate(GROUP BY:([D].[Inv_ID]) DEFINE:([Expr1006]=SUM([LOALogs].[dbo].[LogInvSearches_Daily].[LogCount] as [D].[LogCount] )))
                                |--排序(ORDER BY:([D].[Inv_ID] ASC))
                                     |--嵌套循环(内连接,外引用:([D].[Inv_ID]))
                                          |--嵌套循环(内连接,外引用:([Expr1011],[Expr1012],[Expr1010]))
                                          | |--计算标量(DEFINE:(([Expr1011],[Expr1012],[Expr1010])=GetRangeWithMismatchedTypes(dateadd(day,(-30),getdate()),NULL,(6))))
                                          | | |--恒定扫描
                                          | |--Index Seek(OBJECT:([LOALogs].[dbo].[LogInvSearches_Daily].[IX_LogInvSearches_Daily_LogDay] AS [D]), SEEK:([D].[LogDay] > [Expr1011] AND [D].[ LogDay] < [Expr1012]) 已订购)
                                          |--索引查找(OBJECT:([propertyControlCenter].[dbo].[Inventory].[IX_Inventory_Acct_ID]), SEEK:([propertyControlCenter].[dbo].[Inventory].[Acct_ID]=(18731) AND [ propertyControlCenter].[dbo].[Inventory].[Inv_ID]=[LOA

(13 行受影响)

我尝试使用 CTE 首先获取行并聚合它们,但这并没有更快地运行,并且给了我基本相同的执行计划。

(1 行受影响)
文本
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------
--SET SHOWPLAN_TEXT ON;
使用 getSearches 作为 (
        选择
            日志计数
-- , DENSE_RANK() OVER(ORDER BY Sum(LogCount) DESC, Inv_ID DESC) AS Rank
            , D.Inv_ID
        FROM LogInvSearches_Daily D (NOLOCK)
            INNER JOIN propertyControlCenter.dbo.Inventory I (NOLOCK) ON Acct_ID = 18731 AND I.Inv_ID = D.Inv_ID
        在哪里
            LogDay > DateAdd(d, -30, getdate())
-- 按 Inv_ID 分组
)

SELECT Sum(LogCount) AS 视图,Inv_ID
来自 getSearches
按 Inv_ID 分组


(1 行受影响)

文本
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- --------------------------------------
  |--Stream Aggregate(GROUP BY:([D].[Inv_ID]) DEFINE:([Expr1004]=SUM([LOALogs].[dbo].[LogInvSearches_Daily].[LogCount] as [D].[LogCount] )))
       |--排序(ORDER BY:([D].[Inv_ID] ASC))
            |--嵌套循环(内连接,外引用:([D].[Inv_ID]))
                 |--嵌套循环(内连接,外引用:([Expr1008],[Expr1009],[Expr1007]))
                 | |--计算标量(DEFINE:(([Expr1008],[Expr1009],[Expr1007])=GetRangeWithMismatchedTypes(dateadd(day,(-30),getdate()),NULL,(6))))
                 | | |--恒定扫描
                 | |--Index Seek(OBJECT:([LOALogs].[dbo].[LogInvSearches_Daily].[IX_LogInvSearches_Daily_LogDay] AS [D]), SEEK:([D].[LogDay] > [Expr1008] AND [D].[ LogDay] < [Expr1009]) 已订购)
                 |--Index Seek(OBJECT:([propertyControlCenter].[dbo].[Inventory].[IX_Inventory_Acct_ID] AS [I]), SEEK:([I].[Acct_ID]=(18731) AND [I].[ Inv_ID]=[LOALogs].[dbo].[LogInvSearches_Daily].[Inv_ID] as [D].[Inv_ID])

(受影响的 8 行)


(1 行受影响)

因此,鉴于我在执行计划中获得了良好的 Index Seeks,我该怎么做才能让它运行得更快?

更新:

这是没有 DENSE_RANK() 的相同查询运行,它需要完全相同的 24 秒才能运行,并为我提供相同的基本查询计划:

文本
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------
--SET SHOWPLAN_TEXT ON
选择前 5 名
    Sum(LogCount) AS 浏览量
    , Inv_ID
FROM LogInvSearches_Daily D (NOLOCK)
在哪里
    LogDay > DateAdd(d, -30, getdate())
    并且存在(
        从 propertyControlCenter.dbo.Inventory (NOLOCK) 中选择 NULL,其中 Acct_ID = 18731 AND Inv_ID = D.Inv_ID
    )
按 Inv_ID 分组
按视图排序,Inv_ID
(1 行受影响)

文本
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------
  |--排序(前 5 名,排序方式:([Expr1006] ASC,[D].[Inv_ID] ASC))
       |--Stream Aggregate(GROUP BY:([D].[Inv_ID]) DEFINE:([Expr1006]=SUM([LOALogs].[dbo].[LogInvSearches_Daily].[LogCount] as [D].[LogCount] )))
            |--排序(ORDER BY:([D].[Inv_ID] ASC))
                 |--嵌套循环(内连接,外引用:([D].[Inv_ID]))
                      |--嵌套循环(内连接,外引用:([Expr1010],[Expr1011],[Expr1009]))
                      | |--计算标量(DEFINE:(([Expr1010],[Expr1011],[Expr1009])=GetRangeWithMismatchedTypes(dateadd(day,(-30),getdate()),NULL,(6))))
                      | | |--恒定扫描
                      | |--Index Seek(OBJECT:([LOALogs].[dbo].[LogInvSearches_Daily].[IX_LogInvSearches_Daily_LogDay] AS [D]), SEEK:([D].[LogDay] > [Expr1010] AND [D].[ LogDay] < [Expr1011]) 已订购)
                      |--Index Seek(OBJECT:([propertyControlCenter].[dbo].[Inventory].[IX_Inventory_Acct_ID]), SEEK:([propertyControlCenter].[dbo].[Inventory].[Acct_ID]=(18731) AND [ propertyControlCenter].[dbo].[Inventory].[Inv_ID]=[LOALogs].[dbo].[LogInvS

(9 行受影响)


谢谢,

4

3 回答 3

1

我还没有通读你的整个问题(我很快就会谈到),但要回答一个早期的评论:你可以在 SQL Server 2008 标准版中使用分区视图。仅限于企业版的是分区表(公认更灵活)。

分区视图信息:http: //msdn.microsoft.com/en-us/library/ms190019.aspx

关于更广泛的问题,我想知道您是否真的需要 DENSE_RANK 。我想知道您是否对 DENSE_RANK 中的 ORDER BY 和查询本身的 ORDER BY 感到困惑。就目前而言,您的 TOP 5 将返回 5条未定义的记录,因为 SQL Server 不保证记录的任何顺序,除非指定了 ORDER BY 子句(您还没有这样做)。如果您将 ORDER BY 从 DENSE_RANK 向下移动为整个查询 ORDER BY,如下所示,记录将按照我认为您想要的方式出现,它将消除对昂贵的 DENSE_RANK 聚合函数的需要。

SELECT TOP 5
    SUM([LogCount]) AS [Views],
    [Inv_ID]
FROM [LogInvSearches_Daily] D (NOLOCK)
WHERE 
    [LogDay] > DateAdd(d, -30, getdate())
    AND EXISTS(
        SELECT *
        FROM Inventory (NOLOCK)
        WHERE Acct_ID = 18731
            AND Inv_ID = D.Inv_ID
    )
GROUP BY
    Inv_ID
ORDER BY
    [Views] DESC,
    [Inv_ID]

更新:

时间可能在这里用完了:

|--Sort(ORDER BY:([D].[Inv_ID] ASC))

您可以尝试创建一个像这样的覆盖索引:

CREATE NONCLUSTERED INDEX [IX_LogInvSearches_Daily_Perf] ON [dbo].[LogInvSearches_Daily] 
(
    [Inv_ID] ASC,
    [LogDay] ASC
)
INCLUDE
(
    [LogCount]
)

请注意,我还稍微更改了 ORDER BY(Inv_ID 现在按 ASC 排序而不是 DESC)。我怀疑此更改不会以有问题的方式影响结果,但可能会提高性能,因为它将以与分组相同的顺序返回行(尽管这可能无关紧要!)。

于 2010-05-12T16:33:22.290 回答
1

除了分区,

根据我们对比您的更大的表的经验,我们将数据提取到临时表(不是表变量)中并在其上进行聚合。不是针对所有查询,而是针对更复杂的查询。

除此之外,我同意 Daniel Renshaw 关于 DENSE_RANK 的观察

我还考虑将 [Inv_ID]、[LogCount] 移动到索引中(不包括,可能使用 DESC 排序)

于 2010-05-12T17:03:50.173 回答
0

Acct_ID 位于 Inventory 表上,并且似乎有一个自身的索引 (IX_Inventory_Acct_ID)。也许如果 Inventory 在 (Acct_Id, Inv_Id) 上有一个索引,并且 LogInvSearches_Daily 在 (Inv_Id, LogDay) 周围聚集(或至少索引),那么您会有更多的运气。

顺便说一句,我不知道 LogInvSearches_Daily.ID 上的当前集群索引应该买给你什么。为什么要在磁盘上关闭具有关闭 ID 的记录?

于 2010-05-12T19:43:21.597 回答