Totals(sums/counts) 跨多个子表查询优化

Zoi*_*nky 7 sql-server sql-server-2014

我们的数据库中有 12 种类型的费用,其中一些有相当不同的数据减去金额字段。我们在应用程序和报告中有多个地方需要单个和多个费用总计以及每个费用类型和总计的计数。最后,我们希望所有这些调用都有一个视图,但可以使用存储过程。

我们为此查看了多种替代方案,发现 CTE 允许我们在不使用临时表的情况下获取所需的所有数据。使用连接不起作用,因为无论我们尝试什么,我们都看到记录被复制或删除。

我附上了费用表的一个子集和包含 CTE 的查询。有没有人有比这更好的选择?更快的东西?我们是否适当地接近这种“扁平化”?

请注意,无论是 View 还是 Proc,此查询的执行计划都是相同的,并且 Proc 的运行时间似乎是原来的两倍。

下面是代码

WITH pe AS
(
SELECT 
    EventRegistrationId
    ,sum(AmountPaid)            as AmountPaidTotal
    ,sum(CommercialValueAmount) as CommercialValueAmountTotal
    ,count(1) as ExpenseCount
FROM PettyExpenses 
WHERE IsDisputed = 0 AND IsUndisputed = 0
group by EventRegistrationId
),hpe AS
(
SELECT 
    EventRegistrationId
    ,sum(AmountPaid)            as AmountPaidTotal
    ,sum(CommercialValueAmount) as CommercialValueAmountTotal
    ,count(1) as ExpenseCount
FROM HirePremisesExpenses 
WHERE IsDisputed = 0 AND IsUndisputed = 0
group by EventRegistrationId
), ae AS
(
SELECT 
    EventRegistrationId
    ,sum(AmountPaid)            as AmountPaidTotal
    ,sum(CommercialValueAmount) as CommercialValueAmountTotal
    ,count(1) as ExpenseCount
FROM AdvertisingExpenses 
WHERE IsDisputed = 0 AND IsUndisputed = 0
group by EventRegistrationId
), se AS
(
SELECT 
    EventRegistrationId
    ,sum(AmountPaid)            as AmountPaidTotal
    ,sum(CommercialValueAmount) as CommercialValueAmountTotal
    ,count(1) as ExpenseCount
FROM ServiceExpenses 
WHERE IsDisputed = 0 AND IsUndisputed = 0
group by EventRegistrationId
), gse AS
(
SELECT 
    EventRegistrationId
    ,sum(AmountPaid)            as AmountPaidTotal
    ,sum(CommercialValueAmount) as CommercialValueAmountTotal
    ,count(1) as ExpenseCount
FROM GoodsSuppliedExpenses 
WHERE IsDisputed = 0 AND IsUndisputed = 0
group by EventRegistrationId
), thve AS
(
SELECT 
    EventRegistrationId
    ,sum(AmountPaid)            as AmountPaidTotal
    ,sum(CommercialValueAmount) as CommercialValueAmountTotal
    ,count(1) as ExpenseCount
FROM TravelHireVehicleExpenses 
WHERE IsDisputed = 0 AND 
IsUndisputed = 0
group by EventRegistrationId

)
select
distinct eer.EventRegistrationId
--Petty Expense
,ISNULL(pe.AmountPaidTotal,0) as PettyExpenseAmountPaid
,ISNULL(pe.CommercialValueAmountTotal,0) as PettyExpenseCommercial
,ISNULL(pe.ExpenseCount,0) as PettyExpenseCount
--Hire On Premise Expense
,ISNULL(hpe.AmountPaidTotal,0) as HireOnPremisesExpenseAmountPaid
,ISNULL(hpe.CommercialValueAmountTotal,0) as HireOnPremisesExpenseCommercial
,ISNULL(hpe.ExpenseCount,0) as HireOnPremisesExpenseCount
--Advertising Expense
,ISNULL(ae.AmountPaidTotal,0) as AdvertisingExpenseAmountPaid
,ISNULL(ae.CommercialValueAmountTotal,0) as AdvertisingExpenseCommercial
,ISNULL(ae.ExpenseCount,0) as AdvertisingExpenseExpenseCount
--Services Expense
,ISNULL(se.AmountPaidTotal,0) as ServiceExpenseAmountPaid
,ISNULL(se.CommercialValueAmountTotal,0) as ServiceExpenseCommercial
,ISNULL(se.ExpenseCount,0) as ServiceExpenseExpenseCount
--Goods Supplied Expense
,ISNULL(gse.AmountPaidTotal,0) as GoodsSuppliedExpenseAmountPaid
,ISNULL(gse.CommercialValueAmountTotal,0) as GoodsSuppliedExpenseCommercial
,ISNULL(gse.ExpenseCount,0) as GoodsSuppliedExpenseExpenseCount
--Travel and Vehicle Expense
,ISNULL(thve.AmountPaidTotal,0) as TravelVehicleExpenseAmountPaid
,ISNULL(thve.CommercialValueAmountTotal,0) as TravelVehicleExpenseCommercial
,ISNULL(thve.ExpenseCount,0) as TravelVehicleExpenseExpenseCount
--All Expenses
,ISNULL(pe.AmountPaidTotal,0) 
    + ISNULL(hpe.AmountPaidTotal,0)
    + ISNULL(ae.AmountPaidTotal,0) 
    + ISNULL(se.AmountPaidTotal,0)
    + ISNULL(gse.AmountPaidTotal,0) 
    + ISNULL(thve.AmountPaidTotal,0) as AllExpenseAmountPaidTotal
,ISNULL(pe.CommercialValueAmountTotal,0) 
    + ISNULL(hpe.CommercialValueAmountTotal,0)
    + ISNULL(ae.CommercialValueAmountTotal,0) 
    + ISNULL(se.CommercialValueAmountTotal,0)
    + ISNULL(gse.CommercialValueAmountTotal,0) 
    + ISNULL(thve.CommercialValueAmountTotal,0) as AllExpenseCommercialValueTotal
,ISNULL(pe.ExpenseCount,0) 
    + ISNULL(hpe.ExpenseCount,0)
    + ISNULL(ae.ExpenseCount,0) 
    + ISNULL(se.ExpenseCount,0)
    + ISNULL(gse.ExpenseCount,0) 
    + ISNULL(thve.ExpenseCount,0) as AllExpenseCount
from EventRegistrations eer
left join pe on pe.EventRegistrationId = eer.EventRegistrationId
left join hpe on hpe.EventRegistrationId = eer.EventRegistrationId
left join ae on ae.EventRegistrationId = eer.EventRegistrationId 
left join se on se.EventRegistrationId = eer.EventRegistrationId
left join gse on gse.EventRegistrationId = eer.EventRegistrationId
left join thve on thve.EventRegistrationId = eer.EventRegistrationId
Run Code Online (Sandbox Code Playgroud)

更新:

这是为那些有兴趣看到它的人插入的数据库模式。

数据库架构和插入

使用 SQL Server 2014 标准,我将 db 架构/插入更改为一个文件(此处为大),其中包含更多插入以及上传的执行计划和结果(2 张图像并排显示所有返回的列)

执行计划

结果 1

结果继续

Kin*_*hah 2

\n

有人有比这更好的选择吗?有更快的东西吗?

\n
\n\n

您的原始查询将对所有 6 个表进行表扫描。

\n\n

您可以删除distinct eer.EventRegistrationId并使用GROUP BY eer.EventRegistrationId,其余一切保持不变。

\n\n

以下索引将帮助您避免TABLE SCAN并执行以下操作INDEX SEEK

\n\n
create nonclustered index [nc_PettyExpenses] on [dbo].[PettyExpenses] (\n    [IsDisputed]\n    ,[IsUndisputed]\n    ) include (\n    EventRegistrationId\n    ,AmountPaid\n    ,CommercialValueAmount\n    )\ngo\n\ncreate nonclustered index [nc_HirePremisesExpenses] on [dbo].[HirePremisesExpenses] (\n    [IsDisputed]\n    ,[IsUndisputed]\n    ) include (\n    EventRegistrationId\n    ,AmountPaid\n    ,CommercialValueAmount\n    )\ngo\n\ncreate nonclustered index [nc_AdvertisingExpenses] on [dbo].[AdvertisingExpenses] (\n    [IsDisputed]\n    ,[IsUndisputed]\n    ) include (\n    EventRegistrationId\n    ,AmountPaid\n    ,CommercialValueAmount\n    )\ngo\n\ncreate nonclustered index [nc_ServiceExpenses] on [dbo].[ServiceExpenses] (\n    [IsDisputed]\n    ,[IsUndisputed]\n    ) include (\n    EventRegistrationId\n    ,AmountPaid\n    ,CommercialValueAmount\n    )\ngo\n\ncreate nonclustered index [nc_GoodsSuppliedExpenses] on [dbo].[GoodsSuppliedExpenses] (\n    [IsDisputed]\n    ,[IsUndisputed]\n    ) include (\n    EventRegistrationId\n    ,AmountPaid\n    ,CommercialValueAmount\n    )\ngo\n\ncreate nonclustered index [nc_TravelHireVehicleExpenses] on [dbo].[TravelHireVehicleExpenses] (\n    [IsDisputed]\n    ,[IsUndisputed]\n    ) include (\n    EventRegistrationId\n    ,AmountPaid\n    ,CommercialValueAmount\n    )\ngo\n\ncreate nonclustered index [nc_EventRegistrations] on dbo.EventRegistrations (EventRegistrationId);\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

OPTION (MAXDOP 1)+以上索引

\n\n

查询计划

\n\n

在此输入图像描述

\n\n

统计IO输出

\n\n

在此输入图像描述

\n\n
\n\n

没有OPTION (MAXDOP 1)+以上索引

\n\n

查询计划

\n\n

在此输入图像描述

\n\n

统计IO输出

\n\n

在此输入图像描述

\n\n
\xe2\x95\x94\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa6\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa6\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x97\n\xe2\x95\x91             Option              \xe2\x95\x91 CPU \xe2\x95\x91 ElapsedTime \xe2\x95\x91\n\xe2\x95\xa0\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xac\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xac\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa3\n\xe2\x95\x91 Plain - As you posted your code \xe2\x95\x91  16 \xe2\x95\x91          12 \xe2\x95\x91\n\xe2\x95\x91 Without MAXDOP 1 + Indexes      \xe2\x95\x91  62 \xe2\x95\x91          11 \xe2\x95\x91\n\xe2\x95\x91 With MAXDOP 1 + Indexes         \xe2\x95\x91   0 \xe2\x95\x91           7 \xe2\x95\x91 <== Winner !!\n\xe2\x95\x9a\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa9\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa9\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x9d\n
Run Code Online (Sandbox Code Playgroud)\n\n

为了完整性,下面是代码:

\n\n
/*\ndbcc freeproccache\ndbcc dropcleanbuffers\n\n*/\nset nocount on\nset statistics io on\nset statistics time on;\n\nwith pe\nas (\n    select EventRegistrationId\n        ,sum(AmountPaid) as AmountPaidTotal\n        ,sum(CommercialValueAmount) as CommercialValueAmountTotal\n        ,count(1) as ExpenseCount\n    from PettyExpenses\n    where IsDisputed = 0\n        and IsUndisputed = 0\n    group by EventRegistrationId\n    )\n    ,hpe\nas (\n    select EventRegistrationId\n        ,sum(AmountPaid) as AmountPaidTotal\n        ,sum(CommercialValueAmount) as CommercialValueAmountTotal\n        ,count(1) as ExpenseCount\n    from HirePremisesExpenses\n    where IsDisputed = 0\n        and IsUndisputed = 0\n    group by EventRegistrationId\n    )\n    ,ae\nas (\n    select EventRegistrationId\n        ,sum(AmountPaid) as AmountPaidTotal\n        ,sum(CommercialValueAmount) as CommercialValueAmountTotal\n        ,count(1) as ExpenseCount\n    from AdvertisingExpenses\n    where IsDisputed = 0\n        and IsUndisputed = 0\n    group by EventRegistrationId\n    )\n    ,se\nas (\n    select EventRegistrationId\n        ,sum(AmountPaid) as AmountPaidTotal\n        ,sum(CommercialValueAmount) as CommercialValueAmountTotal\n        ,count(1) as ExpenseCount\n    from ServiceExpenses\n    where IsDisputed = 0\n        and IsUndisputed = 0\n    group by EventRegistrationId\n    )\n    ,gse\nas (\n    select EventRegistrationId\n        ,sum(AmountPaid) as AmountPaidTotal\n        ,sum(CommercialValueAmount) as CommercialValueAmountTotal\n        ,count(1) as ExpenseCount\n    from GoodsSuppliedExpenses\n    where IsDisputed = 0\n        and IsUndisputed = 0\n    group by EventRegistrationId\n    )\n    ,thve\nas (\n    select EventRegistrationId\n        ,sum(AmountPaid) as AmountPaidTotal\n        ,sum(CommercialValueAmount) as CommercialValueAmountTotal\n        ,count(1) as ExpenseCount\n    from TravelHireVehicleExpenses\n    where IsDisputed = 0\n        and IsUndisputed = 0\n    group by EventRegistrationId\n    )\nselect eer.EventRegistrationId\n    --Petty Expense\n    ,ISNULL(SUM(case e.src\n                when \'pe\'\n                    then e.AmountPaidTotal\n                end), 0) as PettyExpenseAmountPaid\n    ,ISNULL(SUM(case e.src\n                when \'pe\'\n                    then e.CommercialValueAmountTotal\n                end), 0) as PettyExpenseCommercial\n    ,ISNULL(SUM(case e.src\n                when \'pe\'\n                    then e.ExpenseCount\n                end), 0) as PettyExpenseCount\n    --Hire On Premise Expense\n    ,ISNULL(SUM(case e.src\n                when \'hpe\'\n                    then e.AmountPaidTotal\n                end), 0) as HireOnPremisesExpenseAmountPaid\n    ,ISNULL(SUM(case e.src\n                when \'hpe\'\n                    then e.CommercialValueAmountTotal\n                end), 0) as HireOnPremisesExpenseCommercial\n    ,ISNULL(SUM(case e.src\n                when \'hpe\'\n                    then e.ExpenseCount\n                end), 0) as HireOnPremisesExpenseCount\n    --Advertising Expense\n    ,ISNULL(SUM(case e.src\n                when \'ae\'\n                    then e.AmountPaidTotal\n                end), 0) as AdvertisingExpenseAmountPaid\n    ,ISNULL(SUM(case e.src\n                when \'ae\'\n                    then e.CommercialValueAmountTotal\n                end), 0) as AdvertisingExpenseCommercial\n    ,ISNULL(SUM(case e.src\n                when \'ae\'\n                    then e.ExpenseCount\n                end), 0) as AdvertisingExpenseExpenseCount\n    --Services Expense\n    ,ISNULL(SUM(case e.src\n                when \'se\'\n                    then e.AmountPaidTotal\n                end), 0) as ServiceExpenseAmountPaid\n    ,ISNULL(SUM(case e.src\n                when \'se\'\n                    then e.CommercialValueAmountTotal\n                end), 0) as ServiceExpenseCommercial\n    ,ISNULL(SUM(case e.src\n                when \'se\'\n                    then e.ExpenseCount\n                end), 0) as ServiceExpenseExpenseCount\n    --Goods Supplied Expense\n    ,ISNULL(SUM(case e.src\n                when \'gse\'\n                    then e.AmountPaidTotal\n                end), 0) as GoodsSuppliedExpenseAmountPaid\n    ,ISNULL(SUM(case e.src\n                when \'gse\'\n                    then e.CommercialValueAmountTotal\n                end), 0) as GoodsSuppliedExpenseCommercial\n    ,ISNULL(SUM(case e.src\n                when \'gse\'\n                    then e.ExpenseCount\n                end), 0) as GoodsSuppliedExpenseExpenseCount\n    --Travel and Vehicle Expense\n    ,ISNULL(SUM(case e.src\n                when \'thve\'\n                    then e.AmountPaidTotal\n                end), 0) as TravelVehicleExpenseAmountPaid\n    ,ISNULL(SUM(case e.src\n                when \'thve\'\n                    then e.CommercialValueAmountTotal\n                end), 0) as TravelVehicleExpenseCommercial\n    ,ISNULL(SUM(case e.src\n                when \'thve\'\n                    then e.ExpenseCount\n                end), 0) as TravelVehicleExpenseExpenseCount\n    --All Expenses\n    ,ISNULL(SUM(e.AmountPaidTotal), 0) as AllExpenseAmountPaidTotal\n    ,ISNULL(SUM(e.CommercialValueAmountTotal), 0) as AllExpenseCommercialValueTotal\n    ,ISNULL(SUM(e.ExpenseCount), 0) as AllExpenseCount\nfrom EventRegistrations eer\nleft join (\n    select \'pe\' as src\n        ,EventRegistrationId\n        ,AmountPaidTotal\n        ,CommercialValueAmountTotal\n        ,ExpenseCount\n    from pe\n\n    union all\n\n    select \'hpe\' as src\n        ,EventRegistrationId\n        ,AmountPaidTotal\n        ,CommercialValueAmountTotal\n        ,ExpenseCount\n    from hpe\n\n    union all\n\n    select \'ae\' as src\n        ,EventRegistrationId\n        ,AmountPaidTotal\n        ,CommercialValueAmountTotal\n        ,ExpenseCount\n    from ae\n\n    union all\n\n    select \'se\' as src\n        ,EventRegistrationId\n        ,AmountPaidTotal\n        ,CommercialValueAmountTotal\n        ,ExpenseCount\n    from se\n\n    union all\n\n    select \'gse\' as src\n        ,EventRegistrationId\n        ,AmountPaidTotal\n        ,CommercialValueAmountTotal\n        ,ExpenseCount\n    from gse\n\n    union all\n\n    select \'thve\' as src\n        ,EventRegistrationId\n        ,AmountPaidTotal\n        ,CommercialValueAmountTotal\n        ,ExpenseCount\n    from thve\n    ) as e on eer.EventRegistrationId = e.EventRegistrationId\ngroup by eer.EventRegistrationId --- we removed the distinct and added a group by clause \noption (maxdop 1)\n\nset statistics io off\nset statistics time off\n
Run Code Online (Sandbox Code Playgroud)\n\n

注意:您可以伪造行数和页数<--仅用于教育目的

\n\n
UPDATE STATISTICS [dbo].[PettyExpenses]             WITH ROWCOUNT = 10000000, pagecount = 10000000\nUPDATE STATISTICS [dbo].[HirePremisesExpenses]      WITH ROWCOUNT = 10000000, pagecount = 10000000\nUPDATE STATISTICS [dbo].[AdvertisingExpenses]       WITH ROWCOUNT = 10000000, pagecount = 10000000\nUPDATE STATISTICS [dbo].[ServiceExpenses]           WITH ROWCOUNT = 10000000, pagecount = 10000000\nUPDATE STATISTICS [dbo].[GoodsSuppliedExpenses]     WITH ROWCOUNT = 10000000, pagecount = 10000000\nUPDATE STATISTICS [dbo].[TravelHireVehicleExpenses] WITH ROWCOUNT = 10000000, pagecount = 10000000\n
Run Code Online (Sandbox Code Playgroud)\n