使用总计来提高性能

gar*_*rik 7 performance sql-server-2008 query aggregate materialized-view

我有两个表:详细信息和这些详细信息的总计。

详细信息(缓慢的解决方案):

select 
    OrderId             =   r.OrderId                   
  , TotalQty            =   SUM(r.Quantity)
  , TotalGrossConsid    =   SUM(r.Price * r.Quantity)
from dbo.Order r
group by r.OrderId
Run Code Online (Sandbox Code Playgroud)

总计(快速解决方案):

select 
    t.OrderId                           
  , t.TotalQty          
  , t.TotalGrossConsid  
  , t.IsValid
from dbo.OrderTotal t
Run Code Online (Sandbox Code Playgroud)

有时总数变得无效(某些作业必须重新计算更改的总数,但会延迟)。如您所知,第二个查询更快,有效总数的数量多于无效总数。因此,我正在寻找一个组合查询,该查询从第二个表 (totals) 返回有效总数,并使用第一个慢查询返回动态重新计算的总数。所以我的目标将实现:所有总数都是有效的,响应时间比完全重新计算要快。

这是我的尝试(混合解决方案):

with fast_static(OrderId, TotalQty, TotalGrossConsid, IsValid)
as
(
    select 
        t.OrderId                           
      , t.TotalQty          
      , t.TotalGrossConsid  
      , t.IsValid
    from dbo.OrderTotal t
)

, slow_dynamic(OrderId, TotalQty, TotalGrossConsid)
(
   select 
        OrderId             =   r.OrderId                   
      , TotalQty            =   SUM(r.Quantity)
      , TotalGrossConsid    =   SUM(r.Price * r.Quantity)
    from dbo.Order r
)
select
    OrderId, TotalQty, TotalGrossConsid
from fast_static 
where IsValid = 1  
union all
select
  OrderId, TotalQty, TotalGrossConsid 
from slow_dynamic s 
    --inner join fast_static ff
    --on ff.OrderId = s.OrderId 
where   --ff.Valid = 0 -- too slow!!!
    s.OrderId in (select OrderId from fast_static f where f.Valid = 0)
Run Code Online (Sandbox Code Playgroud)

我比较了快速解决方案和混合解决方案,我得到了 32% 到 68%(相对查询成本)。如果您可以看到注释变体,则它等于 1% 到 99%(太糟糕了)。是否可以改进此查询?

添加

@gbn:

Valid  =  case when i.OrderId is null then 1 else 0 end
...
dbo.OrderTotal t  left join dbo.InvalidOrders i
Run Code Online (Sandbox Code Playgroud)

是的,我有一项重新计算总数的工作,但此过程与查询请求不同步。InvalidOrders 表是一个小表,用于存储记录以了解总计无效(要重新计算)

解决方案

索引视图是最佳选择。请注意 SQL Server 版本(非企业版本的noexpand 提示)并准备好重新创建一些数据库对象( SET ANSI_NULLS ON, SET QUOTED_IDENTIFIER ON) 以开始在客户端使用索引视图。

gbn*_*gbn 5

我不明白为什么你不能在飞行中聚合,为什么它这么慢。“有效”的想法是处理 OrderTotals 或某些业务流程滞后的解决方法吗?

这两种想法都放弃了 InvalidOrder 表,这是一种针对不良索引的解决方法。

  • 建议1:

创建计算列

ALTER TABLE dbo.Order ADD PriceXQuantity AS Price * Quantity PERSISTED
Run Code Online (Sandbox Code Playgroud)

添加索引

CREATE INDEX IX_Totals ON dbo.Order (OrderID) INCLUDE Quantity, PriceXQuantity)
Run Code Online (Sandbox Code Playgroud)

走着瞧吧

  • 建议2:

使用索引视图

CREATE VIEW OrderTotals
WITH SCHEMABINDING
AS
select 
    OrderId             =   r.OrderId                   
  , TotalQty            =   SUM(r.Quantity)
  , TotalGrossConsid    =   SUM(r.Price * r.Quantity)
  , COUNT_BIG(*) AS Dummy
from dbo.Order r
group by r.OrderId
GO
CREATE UNIQUE CLUSTERED INDEX IXCU_OrderTotals ON OrderTotals (OrderId9
GO
Run Code Online (Sandbox Code Playgroud)

您也可以在此处使用计算列

  • @gbn - 这是一个非常酷的功能,索引视图是始终与基表同步还是在您提交时刷新? (2认同)
  • @garik:我倾向于总是使用它(即使在 Enterprise 上):我发现 SQL Server frankyl 有时在使用索引视图时非常脑残。您还可以将索引视图包装在另一个没有扩展的视图中,因为提示传播“向内”:所以不需要在您的外部视图中 (2认同)