Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在SQL中重新使用聚合级公式-有什么好的策略吗?_Sql_Sql Server_Database Design_Dry - Fatal编程技术网

在SQL中重新使用聚合级公式-有什么好的策略吗?

在SQL中重新使用聚合级公式-有什么好的策略吗?,sql,sql-server,database-design,dry,Sql,Sql Server,Database Design,Dry,想象一下这种情况,但是有更多的组件存储桶和更多的中间产品和输出。许多中间产品是在细节层面进行计算的,但也有一些是在聚合层面进行计算的: 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 ,Liabilit

想象一下这种情况,但是有更多的组件存储桶和更多的中间产品和输出。许多中间产品是在细节层面进行计算的,但也有一些是在聚合层面进行计算的:

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(标量UDF或带有外部应用程序的表值UDF,因为许多最终结果可能共享必须在聚合级别计算的中间结果),但根据我的经验,标量UDF和多语句表值UDF的性能非常差

还考虑了使用更动态的SQL并按名称应用公式

有没有其他技巧、技巧或策略来保持这些公式的同步和/或组织在不同层次上的应用

请注意,在不同的聚合级别上必须使用相同的公式。这会导致代码重复

如果函数更复杂,则可以从创建自定义
CLR
聚合中获益

但是,对于这样一个简单的函数,内置的
SUM
是最好的


PostgreSQL
不同,
SQL Server
不允许使用内置语言创建自定义聚合。

对于您的简化示例,我将通过返回原始数据来重构计算(
SUM(Income)
SUM(Expense)
)在每个结果集中分别计算
利润
利润

如果在实际情况下这是不可能的,你能把你的简单例子稍微复杂一点,这样我就能明白你的意思了吗


我最近参与了一个项目,该项目需要在查询中进行复杂的业务分析计算。事实证明,不可能在数据查询之外执行这些操作,因此我们最终求助于将所有内容转换为动态SQL。这允许我们构造宏函数来构建每个查询的各个部分。通过这样做,我们牺牲了可读性,但获得了可维护性。我们没有牺牲可测试性,因为我们编写了单元测试,通过宏函数执行每个可能的代码路径,并在生成查询时记录每个查询。

您可以在视图中分离部分复杂性:

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

示例查询很难复杂到足以保证视图的简化。但是,视图对于非常复杂的查询非常有帮助。

如果我理解正确,您需要类似于
CREATE VIEW myview AS SELECT SUM(Income-Expense)FROM mytable GROUP BY x
?@Quassnoi-我基本上希望能够将相同的公式一致地应用于任何具有所有适当输入列的集合,不管聚合如何。无论如何,这都需要对每个查询进行重新分类和重新优化,因此如果您的公式真的那么难,您也可以使用动态的
SQL
。对不起,我应该说明SUM(margin)没有意义。如果您运行代码,您将看到边距不能简单地求和。因此,需要在聚合后应用它(而SUM(利润)实际上很好)。@Cade Roux:对!答案已编辑,尽管视图现在解决的复杂性更低:)现在我有一个视图,由十几个细节级别的CTE组成,生成几十个中间列结果,但是,由于聚合公式中的分层条件逻辑,每个必须聚合的不同查询都有3个CTE。@Cade Roux:我们试图避免CTE,因为它增加了复杂性,但是对于第二次主计算的第七次子计算,我们有名为
vw_0207_Profit
的视图层。我发现CTE相当容易维护-更少的数据库对象,您可以在此过程中轻松添加/删除列。谢谢,很高兴知道我想做的事情已经被用作有效的方法。