【问题标题】:Multiple columns from aggregate function, but apply only to rows matching a WHERE clause来自聚合函数的多个列,但仅适用于与 WHERE 子句匹配的行
【发布时间】:2018-01-14 04:46:37
【问题描述】:

样本数据集:

+----------+---------+-----+
| order_id | prod_id | qty |
+----------+---------+-----+
|        1 |       1 |   2 |
|        1 |       1 |   4 |
|        1 |       3 |   1 |
|        2 |       1 |   1 |
|        2 |       2 |   2 |
|        3 |       4 |   1 |
|        3 |       4 |   2 |
|        3 |       2 |   3 |
|        3 |       3 |   5 |
|        4 |       1 |   2 |
|        4 |       2 |   3 |
|        4 |       3 |   3 |
|        4 |       4 |   3 |
|        4 |       6 |   2 |
+----------+---------+-----+

我要做的是构建一个“报告”视图,它将根据产品代码汇总某些总数。因此,例如,我想要一列包含产品代码 1 的订单总数,另一列包含产品 2 和 3 的总和,以及产品代码 4 和 6 的列。

请注意,产品代码的值来自产品类别表,因此我实际上希望获取第 1 类中的所有产品,即产品 1,然后是第 2 类中的所有产品,即产品2 和 3,然后是第 3 类中的所有产品,即产品 4 和 6。

+----------+--------+--------+--------+
| order_id | c1_tot | c2_tot | c3_tot |
+----------+--------+--------+--------+
|        1 |      6 |      1 |      0 |
|        2 |      1 |      2 |      0 |
|        3 |      0 |      8 |      3 |
|        4 |      2 |      6 |      5 |
+----------+--------+--------+--------+

我可以使用这样的 WHERE 子句为 一列 执行此操作:

SELECT 
    [order_id],
    SUM([qty]) AS [c2]
FROM [orders]
WHERE [prod_id] IN (SELECT [id] FROM [product_class] WHERE [class] = 2)
GROUP BY [order_id]

我想出的另一种方法是存储函数,但这似乎是一种冗长的方法:

SELECT DISTINCT
    [order_id],
    total_products_by_class([order_id],1) AS [c1_tot],
    total_products_by_class([order_id],2) AS [c2_tot],
    total_products_by_class([order_id],3) AS [c3_tot]
FROM [orders]

我怀疑理论上可以用解析函数完成一些事情?...

有没有办法在适合视图的东西中不使用存储函数来做到这一点?

更新:更清楚需要从子查询中提取产品的值。

【问题讨论】:

  • 您可以使用CASE WHEN ... END 语句来做您需要的事情。
  • 好的,这是一个好的开始,但是我应该把CASE 语句放在哪里呢?我需要做类似CASE WHEN [prod_id] = 1 THEN SUM([qty]) ELSE 0 END AS [p1_tot]... 不,我认为这不会很快。
  • 其他产品可以放SUM(CASE WHEN prod_id = 1 THEN 1 ELSE 0) AS p1_tot等。
  • 我尝试了这种方法SUM(CASE WHEN [prod_id] IN (SELECT [id] FROM [prod_class] WHERE [class] = 1) THEN [qty] ELSE 0 END) AS [p2and3_tot],但得到了这个错误:Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
  • 这行不通,因为我需要对数量值求和,而不是对记录数求和。

标签: sql sql-server tsql


【解决方案1】:

我获取了为orders 表提供的样本数据,并根据问题第二段中的描述创建了product_class 表。

样本数据:

create table #orders
    (
        order_id int
        , prod_id int
        , qty int
    )

create table #product_class
    (
        id int
        , class int
    )

insert into #orders
values (1, 1, 2)
    , (1, 1, 4)
    , (1, 3, 1)
    , (2, 1, 1)
    , (2, 2, 2)
    , (3, 4, 1)
    , (3, 4, 2)
    , (3, 2, 3)
    , (3, 3, 5)
    , (4, 1, 2)
    , (4, 2, 3)
    , (4, 3, 3)
    , (4, 4, 3)
    , (4, 6, 2)

insert into #product_class
values (1, 1)
    , (2, 2)
    , (3, 2)
    , (4, 3)
    , (6, 3)

答案:

从问题中写的第一个查询作为灵感开始,我将对product_class 表的引用更改为inner join,并将class 添加到group by。在那之后,所有需要发生的事情就是pivotclasses 跨越列而不是行。

select b.order_id
, isnull(b.[1], 0) as c1_tot
, isnull(b.[2], 0) as c2_tot
, isnull(b.[3], 0) as c3_tot
from (
    SELECT o.[order_id]
    , pc.class
    , SUM(o.[qty]) AS [c2]
    FROM [#orders] as o
    inner join #product_class as pc on o.prod_id = pc.id
    GROUP BY o.[order_id]
    , pc.class
    ) as a
pivot (max(a.c2) for a.class in ([1], [2], [3])) as b

输出与所需的输出完全匹配。

【讨论】:

    【解决方案2】:

    CASE 语句可能是您需要在简单查询中执行的操作。

    SELECT  [order_id],
          SUM(CASE WHEN prod_id = 1 THEN qty ELSE 0) AS p1_tot,
          SUM(CASE WHEN prod_id = 2 OR prod_id = 3 THEN qty ELSE 0) AS p2and3_tot,
          SUM(CASE WHEN prod_id = 4 OR prod_id = 6 THEN qty ELSE 0) AS p4and6_tot  
    FROM [orders] 
        INNER JOIN [product_class] ON [orders].[prod_id] = [product_class].[id]
    WHERE [product_class].[class] = 2
    GROUP BY [order_id];
    

    【讨论】:

    • 在您需要将 prod_id 值从子查询拉入产品类表之前,效果很好。然后你得到错误Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
    • 不确定need to pull in the prod_id values from a subquery to a product class table是什么意思
    • 如果您的 product_id 在不同的表中,那么您可以尝试加入而不是使用子查询。
    • 使关于产品 ID 来自何处的问题更加清晰。
    • 您可以使用内连接并将条件放在 where 子句中。
    猜你喜欢
    • 2022-06-15
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    • 1970-01-01
    • 1970-01-01
    • 2013-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多