想象一下这种情况,但是有更多的组件桶和更多的中间产品和输出。许多中间体是在详细程度上计算的,但是在总体水平上计算了一些东西:
DECLARE @Profitability AS TABLE
(
Cust INT NOT NULL
,Category VARCHAR(10) NOT NULL
,Income DECIMAL(10, 2) NOT NULL
,Expense DECIMAL(10, 2) NOT NULL
,Liability DECIMAL(10, 2) NOT NULL
,AllocatedCapital DECIMAL(10, 2) NOT NULL
) ;
INSERT INTO @Profitability
VALUES ( 1, 'Software', 100, 50, 0, 0 ) ;
INSERT INTO @Profitability
VALUES ( 2, 'Software', 100, 20, 0, 0 ) ;
INSERT INTO @Profitability
VALUES ( 3, 'Software', 100, 60, 0, 0 ) ;
INSERT INTO @Profitability
VALUES ( 4, 'Software', 500, 400, 0, 0 ) ;
INSERT INTO @Profitability
VALUES (
5
,'Hardware'
,1000
,550
,0
,0
) ;
INSERT INTO @Profitability
VALUES (
6
,'Hardware'
,1000
,250
,500
,200
) ;
INSERT INTO @Profitability
VALUES (
7
,'Hardware'
,1000
,700
,500
,600
) ;
INSERT INTO @Profitability
VALUES (
8
,'Hardware'
,5000
,4500
,2500
,800
) ;
WITH ProfitView
AS ( SELECT Cust
,Category
,Income
,Expense
,Profit = Income - Expense
,NetProfit = Income - Expense
- CASE WHEN Liability - AllocatedCapital > 0
THEN Liability - AllocatedCapital
ELSE 0
END
FROM @Profitability
)
SELECT Cust
,Category
,Income
,Expense
,Profit
,NetProfit
,Margin = Profit / Income
,NetMargin = NetProfit / Income
FROM ProfitView ; -- NOTE I've left off the AFTER grouping formulas on this one.
WITH ProfitView
AS ( SELECT Cust
,Category
,Income
,Expense
,Profit = Income - Expense
,NetProfit = Income - Expense
- CASE WHEN Liability - AllocatedCapital > 0
THEN Liability - AllocatedCapital
ELSE 0
END
FROM @Profitability
),
GROUP1
AS ( SELECT Category
,SUM(Profit) AS Profit
,SUM(NetProfit) AS NetProfit
,SUM(Income) AS Income
,SUM(Profit) / SUM(Income) AS Margin
,SUM(NetProfit) / SUM(Income) AS NetMargin
FROM ProfitView
GROUP BY Category
),
GROUP2
AS ( SELECT GROUP1.*
,NetProfit - Profit AS Exposure
FROM GROUP1
)
SELECT *
,Exposure / Income AS ExposureRatio
FROM GROUP2 ;
WITH ProfitView
AS ( SELECT Cust
,Category
,Income
,Expense
,Profit = Income - Expense
,NetProfit = Income - Expense
- CASE WHEN Liability - AllocatedCapital > 0
THEN Liability - AllocatedCapital
ELSE 0
END
FROM @Profitability
),
GROUP1
AS ( SELECT SUM(Profit) AS Profit
,SUM(NetProfit) AS NetProfit
,SUM(Income) AS Income
,SUM(Profit) / SUM(Income) AS Margin
,SUM(NetProfit) / SUM(Income) AS NetMargin
FROM ProfitView
),
GROUP2
AS ( SELECT GROUP1.*
,NetProfit - Profit AS Exposure
FROM GROUP1
)
SELECT *
,Exposure / Income AS ExposureRatio
FROM GROUP2 ;
请注意必须在不同的聚合级别使用相同的公式。这导致代码重复。
我曾考虑使用UDF(标量或表值为OUTER APPLY,因为许多最终结果可能共享必须在聚合级别计算的中间体),但根据我的经验,标量和多语句表值UDF的表现非常糟糕。
还考虑使用更多动态SQL并按名称应用公式。
保持这些公式需要在不同级别同步和/或组织的任何其他技巧,技巧或策略?
答案 0 :(得分:1)
请注意必须在不同的聚合级别使用相同的公式。这导致代码重复。
如果您的功能更复杂,您可以从创建自定义CLR
聚合中受益。
但是,对于这样一个简单的函数,内置的SUM
是最好的。
与PostgreSQL
不同,SQL Server
不允许以内置语言创建自定义聚合。
答案 1 :(得分:1)
对于简化示例,我将通过在每个结果集中单独返回原始数据(SUM(Income)
和SUM(Expense)
)来重构计算,并计算Profit
和Margin
业务层。
如果在实际案例中这是不可能的,你能否让你的简单例子变得更复杂,以便我能看到你得到的是什么?
我最近参与了一个项目,需要在查询中完成复杂的业务分析计算。事实证明,在数据查询之外不可能这样做,所以我们最终将所有内容都转换为动态SQL。这允许我们构造宏函数来构建每个查询的各个部分。通过这样做,我们牺牲了可读性,但获得了可维护性。我们没有牺牲可测试性,因为我们编写了单元测试,通过宏函数运行了每个可能的代码路径,并在生成时记录每个查询。
答案 2 :(得分:1)
您可以在视图中分离部分复杂性:
create view dbo.vw_Profit
as
SELECT
Cust
, Income,
, Expense
, Income - Expense as Profit
FROM dbo.Profitability
这允许稍微简单的查询:
SELECT cust, SUM(profit), SUM(Income) / SUM(Expense)
FROM dbo.vw_Profit
GROUP BY cust
示例查询几乎不够复杂,无法通过视图进行简化。但是对于非常复杂的查询,视图可以提供很大帮助。