【问题标题】:Return 0 for SQL count when CASE does not exist当 CASE 不存在时,SQL 计数返回 0
【发布时间】:2016-05-02 04:01:30
【问题描述】:

我有两个表 Log 和 Player。其中 Log 存储带有 playerId 和日期的每个游戏日志。并且 Player 表的 Players 信息为 Age 和 Gender ..etc。我正在编写一个 SQL 存储过程,它需要两个日期,并将按两个日期之间的年龄范围和性别计算 LogId 和组。但是当我运行 SQL 过程时,当该期间没有玩家存在时,它不会显示所有 Age_Range/Gender 条目。我正在尝试获取所有 Age_Range/Gender 条目及其实际计数或计数 = 0,如果他们不不存在。 我什至尝试过改变

count(L.LogId) as Count, 

count(IFNULL(L.LogId, 0)) as Count,

我的SQL程序是:

CREATE PROCEDURE `SHOW_AGE_RANGE` (IN date1 CHAR(10), IN date2 CHAR(10))
 BEGIN
 SELECT
  count(L.LogId) as Count,
  CASE
     WHEN P.age BETWEEN 13 AND 18 THEN '13-18'
     WHEN P.age BETWEEN 19 AND 25 THEN '19-25'
     WHEN P.age BETWEEN 26 AND 39 THEN '26-39'
     WHEN P.age BETWEEN 40 AND 59 THEN '40-59'
     WHEN P.age > 59 THEN '60+' 
 END as Age_Range,
 CASE
    WHEN P.gender  = 0 then 'Female'
    WHEN P.gender  = 1 then 'Male'
 END  as Gender    
 FROM Log L
 LEFT JOIN Player P ON L.playerId = P.playerId 

 WHERE CAST(L.createdDate AS DATE) BETWEEN CAST(date1 AS DATE) AND CAST(date2 AS DATE)  
 GROUP BY Age_Range, Gender;    
END

SQL 过程的输出:

Count  Age_Range  Gender 
----------------------------------
'1'    '13-18'    'Male'
'1'    '19-25'    'Female'
'3'    '26-39'    'Female'
'2'    '40-59'    'Male'
'1'    '60+'      'Female'

预期输出

Count  Age_Range  Gender 
----------------------------------
'0'    '13-18'    'Female'
'1'    '13-18'    'Male'
'1'    '19-25'    'Female'
'0'    '19-25'    'Male'
'3'    '26-39'    'Female'
'0'    '26-39'    'Male'
'0'    '40-59'    'Female'
'2'    '40-59'    'Male'
'1'    '60+'      'Female'
'0'    '60+'      'Male'

【问题讨论】:

    标签: mysql sql stored-procedures


    【解决方案1】:

    您需要从您的年龄范围和性别开始。真的,您正在查询这些,而玩家数据只是您添加到它们的东西。由于您的表中还没有年龄范围(您可能应该考虑添加它),因此您需要为这些表创建一个虚拟表作为子查询。

    SELECT
        COUNT(L.playerId) AS cnt,
        AG.age_range,
        G.gender
    FROM
    (
        SELECT 13 AS min_age, 18 AS max_age, '13-18' AS age_range
        UNION ALL
        SELECT 19 AS min_age, 25 AS max_age, '19-' AS age_range
        UNION ALL
        SELECT 26 AS min_age, 39 AS max_age, '26-39' AS age_range
        UNION ALL
        SELECT 40 AS min_age, 59 AS max_age, '40-59' AS age_range
        UNION ALL
        SELECT 60 AS min_age, 999 AS max_age, '60+' AS age_range
    ) AS AG
    CROSS JOIN
    (
        SELECT 0 AS gender_value, 'Female' AS gender
        UNION ALL
        SELECT 1 AS gender_value, 'Male' AS gender
    ) AS G
    LEFT JOIN Player P ON
        P.age BETWEEN AG.min_age AND AG.max_age AND
        P.gender = G.gender_value
    LEFT OUTER JOIN Log L ON
        L.playerId = P.playerId AND
        CAST(L.createdDate AS DATE) BETWEEN CAST(date1 AS DATE) AND CAST(date2 AS DATE)
    GROUP BY
        AG.age_range, G.gender
    

    【讨论】:

    • 您可能希望选择COUNT(P.gender) 或类似名称而不是COUNT(*),否则您将无法获得没有任何代表的组所需的零计数。
    • 谢谢。更正了。
    • 不,你没有。或者至少,您的更改并不能解决问题。您需要从一个内部表(PlayerLog)中计算一列,而不是从一个外部表中计算一列。这个想法是利用COUNT( expr ) 不计入NULLs 的事实。
    • 嘿,是的。改变得太快了。
    • WHERE 子句中的谓词否定 LEFT JOIN 对log 表的“外部性”。它本质上使它成为一个内部连接操作。该谓词需要移至ON 子句。
    【解决方案2】:

    看起来您想要Log 表的外部联接。交换PlayerLog 表,并将createdDate 列上的谓词重新定位到ON 子句。

    像这样:

      FROM Player P
      LEFT 
      JOIN Log L
        ON L.playerId = P.playerId 
       AND CAST(L.createdDate AS DATE) BETWEEN CAST(date1 AS DATE) AND CAST(date2 AS DATE)
     GROUP BY Age_Range, Gender;
    

    【讨论】:

    • 我不知道这将如何解决在没有给定(年龄范围、性别)组合代表的结果中引入零计数的问题。
    • @JohnBollinger:你是对的。这将仅对存在于player 表中的行的性别/年龄范围返回零计数。这不会解决player 表中的稀疏数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 2021-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多