【问题标题】:Building complex report with t-sql, each row has its own where condition用t-sql构建复杂报表,每一行都有自己的where条件
【发布时间】:2015-10-28 19:40:29
【问题描述】:

我正在使用 T-SQL 构建一份复杂的报表。用户给了我一个报告来源的表格,大约有 1000 万行。表包含描述性属性和数字列,如下所示:

segment product_group gmis lpt numeric_field1 numeric_field2 numeric_field3

报告有大约一千行,报告的定义逐行如下:

'第一行的名称' - 3、4、5 和 lpt = 3 和 gmis 50 中段的 numeric_field1 的总和

'第二行' - 1,2,3 和 lpt 5 和 gmis = 7 中段的 num2 之和

'Row number 3' - num2 + num3 for product_id = 7

'第 4 行' - 第 1 行 + 第 2 行

所以我最终得到了 t-sql 查询,它对每一行都有单独的选择,然后是 union all:

'Row number 1' name, (select sum(num1) from source_table where segment in (3,4,5) and lpt=3 and gmis <> 50) value
union
'Row number 2'  , (select sum(num2) from source_table where segment in (1,2,3) and lpt<> 5 and gmis = 7) 
union
'Row number 3' , (select sum(num2 + num3) from source_table where product_id = 7) 

..... …… 等等

我是否错过了一些更聪明的方法来进行这种查询?因为报告很慢....

【问题讨论】:

  • 使用适当的软件(MySQL、Oracle、DB2...)和版本标记数据库问题很有帮助,例如sql-server-2014。语法和功能的差异通常会影响答案。例如,在 SQL Server 的最新版本中,可能会应用公用表表达式 (CTE) 来避免重复某些查询。
  • 我刚刚添加了sql server 2012 有问题....
  • 不是真正的答案,但在上面的示例中,您使用“UNION”而不是“UNION ALL”;我只是想确定这是示例中的错字,而不是代码中的错字。即使在 UNION 语句上也会导致对您的查询进行 DISTINCT 搜索,这可能会对性能造成很大影响。
  • @Stuart Ainsworth:你说得对,我一直在考虑联合,但在查询中写了联合(不仅仅是在这个例子中,而是在查询本身......谢谢你,这个是向前迈出的第一步:)。但如果可能的话,我想以某种方式完全避免为每一行编写新查询......

标签: tsql optimization sql-server-2012


【解决方案1】:

假设您总是从同一个基表中进行选择并且总是在不分组的情况下进行聚合,那么以下内容应该适合您的目的。

  1. 您应该针对单个表执行一组操作,而不是组合多个选择。将您的 where 子句组合成一个 select 语句,作为一组 when 子句来计算用于聚合的基值。这意味着您只需读取一次源表:

    Select
        Case When segment in (3,4,5) and lpt = 3 and gmis <> 50 Then num1 Else 0 End As row1,
        Case When segment in (1,2,3) and lpt<> 5 and gmis = 7 Then num1 Else 0 End As row2,
        Case When product_id = 7 then (num2+num3) Else 0 End As row3
    From
        source_table
    
  2. 将其用作表表达式并从表表达式中执行聚合选择。在本例中,我使用了 CTE(通用表表达式):

    ;With Column_Values
    (
        row1,
        row2,
        row3
    )
    As
        (
            Select
                Case When segment in (3,4,5) and lpt = 3 and gmis <> 50 Then num1 Else 0 End As row1,
                Case When segment in (1,2,3) and lpt<> 5 and gmis = 7 Then num1 Else 0 End As row2,
                Case When product_id = 7 then (num2+num3) Else 0 End As row3
            From
                source_table
        )
    Select
        name,
        value
    From
        (
            Select
                Sum(row1) As [Row number 1],
                Sum(row2) As [Row number 2],
                Sum(row3) As [Row number 3],
                Sum(row1) + Sum(row2) As [Row number 4]
            From
                Column_Values
        )
    
  3. 您现在将值作为一系列列。要将它们转换为行,请使用 unpivot 命令:

    ;With Column_Values
    (
        row1,
        row2,
        row3
    )
    As
        (
            Select
                Case When segment in (3,4,5) and lpt = 3 and gmis <> 50 Then num1 Else 0 End As row1,
                Case When segment in (1,2,3) and lpt<> 5 and gmis = 7 Then num1 Else 0 End As row2,
                Case When product_id = 7 then (num2+num3) Else 0 End As row3
            From
                source_table
        )
    Select
        name,
        value
    From
        (
            Select
                Sum(row1) As [Row number 1],
                Sum(row2) As [Row number 2],
                Sum(row3) As [Row number 3],
                Sum(row1 + row2) As [Row number 4]
            From
                Column_Values
        ) pvt
    Unpivot
        (
            value For name In
                (
                    [Row number 1],
                    [Row number 2],
                    [Row number 3],
                    [Row number 4]
                )       
        ) As upvt
    

如果您仍然遇到性能问题,您可能希望考虑以下选项,具体取决于您的要求:

• 如果您不需要报告实时数据,您可以预先计算这些值,将它们存储在另一个表中并从此表中报告。

• 如果您拥有企业版并且不需要报告实时数据,您可以在源表上放置一个列存储索引 - 在 SQL Server 2012 中,这会使该表成为只读,因此必须删除并重新创建它每次将新数据加载到源表中时。聚合大型表时,列存储索引可以显着提升性能。

• 如果您需要报告实时数据,您可以使用索引视图。这可能会提高在视图中聚合数据时的性能。

【讨论】:

    猜你喜欢
    • 2014-09-25
    • 1970-01-01
    • 2015-06-13
    • 2017-12-19
    • 1970-01-01
    • 2011-05-28
    • 2015-11-19
    • 2016-12-25
    • 1970-01-01
    相关资源
    最近更新 更多