如何在 SELECT 查询中使用聚合函数和 CASE WHEN THEN

Posted

技术标签:

【中文标题】如何在 SELECT 查询中使用聚合函数和 CASE WHEN THEN【英文标题】:How do I use aggregate function and CASE WHEN THEN in SELECT query 【发布时间】:2021-09-01 05:45:25 【问题描述】:

我有 3 张桌子:

    sales_fact(fact_date、prod_id、fact_sum) sales_pred(pred_date、prod_id、pred_sum) 产品(prod_id、名称、价格)

我需要制作一份报告,其中包含以下: 年 月 产品名称 事实销售 预计销售 偏差

如果实际销售为空,则偏差 = 预测销售; 如果 Predicted sales 为空,则偏差 = -Fact sales

我在最后一个条件下遇到了困难。帮助我理解我的错误

我的代码:

SELECT year(fact_date) AS Year, 
        month(fact_date) AS Month,  
        product.name AS Name, 
        ROUND(SUM(fact_sum), 2) AS Fact, 
        ROUND(SUM(pred_sum), 2) AS Prediction, 
        (CASE 
             WHEN SUM(pred_sum) IS NULL AND SUM(fact_sum) IS NOT NULL 
             THEN ROUND(-(SUM(fact_sum)), 2)
             WHEN SUM(fact_sum) IS NULL AND SUM(pred_sum) IS NOT NULL
             THEN ROUND(SUM(pred_sum)), 2)
             WHEN SUM(pred_sum) IS NOT NULL AND SUM(fact_sum) IS NOT NULL
             THEN ROUND(SUM(pred_sum) - (SUM(fact_sum)), 2) 
             ELSE 0 END AS Deviation        
FROM sales_fact
LEFT JOIN product ON sales_fact.prod_id = product.prod_id
LEFT JOIN sales_pred ON sales_pred.prod_id = product.prod_id AND sales_pred.pred_date = sales_fact.fact_date
GROUP BY product.name, month(fact_date), year(fact_date);

创建表格的代码:

create table sales_fact (fact_date date, prod_id varchar(30), fact_sum float);
    insert into sales_fact (fact_date, prod_id, fact_sum) values ('2016-03-25', '2271145', 93.31);
    insert into sales_fact (fact_date, prod_id, fact_sum) values ('2016-04-25', '2271146', 13.31);
    insert into sales_fact (fact_date, prod_id, fact_sum) values ('2016-05-25', '2271147', 83.31);
    insert into sales_fact (fact_date, prod_id, fact_sum) values ('2016-03-25', '2271145', 43.31);
    insert into sales_fact (fact_date, prod_id, fact_sum) values ('2016-07-25', '2271148', 73.31);
    insert into sales_fact (fact_date, prod_id, fact_sum) values ('2016-07-25', '2271147', 13.31);
    insert into sales_fact (fact_date, prod_id, fact_sum) values ('2016-08-25', '2271149', 43.31);
    insert into sales_fact (fact_date, prod_id, fact_sum) values ('2016-09-25', '2271148', 33.31);
create table sales_pred (pred_date date, prod_id varchar(30), pred_sum float);
    insert into sales_pred (pred_date, prod_id, pred_sum) values ('2016-03-25', '2271145', 100);
    insert into sales_pred (pred_date, prod_id, pred_sum) values ('2016-04-25', '2271146', 93.31);
    insert into sales_pred (pred_date, prod_id, pred_sum) values ('2016-05-25', '2271147', 103.31);
    insert into sales_pred (pred_date, prod_id, pred_sum) values ('2016-07-25', '2271148', 73.31);
create table product (prod_id varchar(30), name varchar(30), price float);
    insert into product (prod_id, name, price) values ('2271145', 'phone', 3);   
    insert into product (prod_id, name, price) values ('2271147', 'laptop', 4);
    insert into product (prod_id, name, price) values ('2271148', 'PC', 5);
    insert into product (prod_id, name, price) values ('2271146', 'mouse', 2);
    insert into product (prod_id, name, price) values ('2271149', 'airpods', 7);

【问题讨论】:

我有 3 个表您必须为这些表提供完整的 CREATE TABLE - 这对问题至关重要。 请提供少量样本数据。 看起来您将 NULL 视为零,这通常是不正确的。如果您有应该为零的空值,则最好将它们固定为零。如果它们不应该为零,那么如果计算中存在空值,则 Deviation 可能应该返回 NULL。 附言。 ROUND(SUM(pred_sum) - SUM(fact_sum), 2) ROUND(SUM(pred_sum), 2) - ROUND(SUM(fact_sum), 2). 只要使用COALESCE,就不需要CASE,例如COALESCE(SUM(pred_sum),0) - COALESCE(SUM(fact_sum),0) 【参考方案1】:

根据您的条件偏差可能为负,因此只需使用减法并使用 colaesce() 将 NULL 值转换为 0(零)。

-- mysql
SELECT YEAR(sf.fact_date) year
     , MONTH(sf.fact_date) month
     , p.name
     , COALESCE(ROUND(SUM(sf.fact_sum), 2), 0) fact
     , COALESCE(ROUND(SUM(slp.pred_sum), 2), 0) Prediction
     , ROUND(COALESCE(SUM(slp.pred_sum), 0) - COALESCE(SUM(sf.fact_sum), 0), 2) deviation
FROM sales_fact sf
LEFT JOIN product p 
       ON sf.prod_id = p.prod_id
LEFT JOIN sales_pred slp
       ON slp.prod_id = p.prod_id 
      AND slp.pred_date = sf.fact_date
GROUP BY YEAR(sf.fact_date), MONTH(sf.fact_date), p.name;

请查看网址https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=a9e9f21dd36400d8830a4734aa7ffb29

【讨论】:

如果偏差必须为正,那么ABS() 更简单。正如您所指出的,OP 对此并不明确/不合逻辑。根据 OP 代码,如果两个总和都有值,则可能出现负数。如果预测为空,那么显示正数就没有逻辑意义。总是将偏差作为正数会产生一个毫无意义的数字,因为你不知道它是盈余还是赤字。 顺便说一句,如果您写的答案基本上详细说明了别人的 cmets,承认这一事实通常被认为是礼貌的。 感谢@JonathanWillcock 的观察。是的 ABS() 更好。如果提问者提供预期的输出会更好。需要提问者的回答。

以上是关于如何在 SELECT 查询中使用聚合函数和 CASE WHEN THEN的主要内容,如果未能解决你的问题,请参考以下文章

sql聚合函数的应用

尝试使用 linq 聚合函数

MySQL聚合函数

jOOQ - 在查询中简洁地表示列和聚合/窗口函数

MarkLogic:在 SPARQL 查询中使用聚合函数

在单个外部 SELECT 查询中使用来自 SELECT 子查询的两个聚合值