【问题标题】:Return zero value when no row data exists不存在行数据时返回零值
【发布时间】:2012-08-26 21:50:15
【问题描述】:

我的问题之前已得到解决,但我似乎无法对我的查询应用任何解决方案以使其正常工作。非常感谢一些指导。

下面我当前的查询返回这个数据集:

  |  Age      |   Count   |
     0-1 day      300
     2-3 days     6000
     3-4 days     100 
SELECT(CASE WHEN time_dtm > SYSDATE -1 THEN '0-1 day'
        WHEN time_dtm > SYSDATE -2 AND time_dtm < SYSDATE -1 THEN '1-2 days'
        WHEN time_dtm > SYSDATE -3 AND time_dtm < SYSDATE -2 THEN '2-3 days'
        WHEN time_dtm > SYSDATE -4 AND time_dtm < SYSDATE -3 THEN '3-4 days'
        WHEN time_dtm > SYSDATE -5 AND time_dtm < SYSDATE -4 THEN 'Closed'
        END) AS Age, 
    COUNT( * ) AS "Count" 
FROM table_1 
WHERE id IN (1,2,3)
GROUP BY (CASE WHEN time_dtm > SYSDATE -1 THEN '0-1 day'
        WHEN time_dtm > SYSDATE -2 AND time_dtm < SYSDATE -1 THEN '1-2 days'
        WHEN time_dtm > SYSDATE -3 AND time_dtm < SYSDATE -2 THEN '2-3 days'
        WHEN time_dtm > SYSDATE -4 AND time_dtm < SYSDATE -3 THEN '3-4 days'
        WHEN time_dtm > SYSDATE -5 AND time_dtm < SYSDATE -4 THEN 'Closed'
        END)
ORDER BY (CASE WHEN time_dtm > SYSDATE -1 THEN '0-1 day'
        WHEN time_dtm > SYSDATE -2 AND time_dtm < SYSDATE -1 THEN '1-2 days'
        WHEN time_dtm > SYSDATE -3 AND time_dtm < SYSDATE -2 THEN '2-3 days'
        WHEN time_dtm > SYSDATE -4 AND time_dtm < SYSDATE -3 THEN '3-4 days'
        WHEN time_dtm > SYSDATE -5 AND time_dtm < SYSDATE -4 THEN 'Closed'
        END)

但是,我希望它像这样将零/空行显示为零:

  |  Age      |   Count   |
     0-1 day      300
     1-2 days     0
     2-3 days     6000
     3-4 days     100
     Closed       0

我已经阅读了过去几天的各种回复:NVL、COALESCE、FULL/LEFT/RIGHT OUTER JOIN、LEFT/RIGHT JOINS、UNION ALL 等等,这些都没有 CASE 语句,并试图自己解决它但!你必须知道什么时候停下来问路。

【问题讨论】:

    标签: sql oracle11g toad


    【解决方案1】:

    首先,重新编写您的查询。使用视图或公用表表达式来避免在 SELECTGROUP BYORDER BY 子句中重复三次。您的查询变为:

    WITH data AS (
        SELECT(CASE WHEN time_dtm > SYSDATE -1 THEN '0-1 day'
                    WHEN time_dtm > SYSDATE -2 AND 
                         time_dtm < SYSDATE -1 THEN '1-2 days'
                    WHEN time_dtm > SYSDATE -3 AND 
                         time_dtm < SYSDATE -2 THEN '2-3 days'
                    WHEN time_dtm > SYSDATE -4 AND 
                         time_dtm < SYSDATE -3 THEN '3-4 days'
                    WHEN time_dtm > SYSDATE -5 AND 
                         time_dtm < SYSDATE -4 THEN 'Closed'
               END) AS Age 
        FROM table_1 
        WHERE id IN (1,2,3)
    )
    SELECT Age, COUNT(*)
    FROM data
    GROUP BY Age
    ORDER BY Age
    

    然后,为了确保您想要的任何组都可以在结果中使用,您有很多选择。

    你可以使用UNION ALL:

    WITH data AS (
        SELECT(CASE WHEN time_dtm > SYSDATE -1 THEN '0-1 day'
                    WHEN time_dtm > SYSDATE -2 AND 
                         time_dtm < SYSDATE -1 THEN '1-2 days'
                    WHEN time_dtm > SYSDATE -3 AND 
                         time_dtm < SYSDATE -2 THEN '2-3 days'
                    WHEN time_dtm > SYSDATE -4 AND 
                         time_dtm < SYSDATE -3 THEN '3-4 days'
                    WHEN time_dtm > SYSDATE -5 AND 
                         time_dtm < SYSDATE -4 THEN 'Closed'
               END) AS Age 
        FROM table_1 
        WHERE id IN (1,2,3)
    
        -- The below will add one record for every desired Age group
        UNION ALL
        SELECT '0-1 day'  FROM DUAL UNION ALL
        SELECT '1-2 days' FROM DUAL UNION ALL
        SELECT '2-3 days' FROM DUAL UNION ALL
        SELECT '3-4 days' FROM DUAL UNION ALL
        SELECT 'Closed'   FROM DUAL
    )
    SELECT Age, COUNT(*) - 1 -- Subtract the extra record again
    FROM data
    GROUP BY Age
    ORDER BY Age
    

    一个完全不同的解决方案将涉及LEFT OUTER JOINs

    -- Groups is a dynamic table that contains the date ranges and their "Age" label
    WITH groups AS (
        SELECT SYSDATE -1 lower, SYSDATE upper, '0-1 day'  Age FROM DUAL UNION ALL
        SELECT SYSDATE -2      , SYSDATE -1   , '1-2 days'     FROM DUAL UNION ALL
        SELECT SYSDATE -3      , SYSDATE -2   , '2-3 days'     FROM DUAL UNION ALL
        SELECT SYSDATE -4      , SYSDATE -3   , '3-4 days'     FROM DUAL UNION ALL
        SELECT SYSDATE -5      , SYSDATE -4   , 'Closed'       FROM DUAL
    )
    SELECT g.Age, NVL(SUM(t.counter), 0)
    FROM groups g
    
    -- LEFT OUTER JOINing "table_1" to "groups" will ensure that every group
    -- appears at least once in the result
    LEFT OUTER JOIN (
      SELECT 1 counter, t.* FROM table_1 t WHERE t.id IN (1,2,3)
    ) t
    ON  t.time_dtm >= g.lower
    AND t.time_dtm <  g.upper
    GROUP BY g.Age
    ORDER BY g.Age
    

    在第二个示例中,您也可以不使用 CTE,并为 groups 表使用嵌套 SELECT。如果您的需求发生变化,很容易看出第二个示例在未来如何更简单地发展。

    【讨论】:

    • 感谢@Lukas,回答我的问题。我尝试了 WITH DATA AS & UNION ALL,但它没有将“年龄”识别为标识符。我还尝试了动态组和 LEFT OUTER JOIN,它返回了与我的原始查询相同的数据集(没有零行)。你还有什么建议吗?如果相关的话,我正在使用 Toad for Oracle 11.5。附言。动态组很酷!
    • @ltran:我在第一个查询中出现语法错误(忘记了FROM 子句)...此外,在第二个查询中,我已将t.id IN (1,2,3) 谓词移动到子选择中。这就是它不起作用的原因。让我知道这是否有帮助
    • 超级棒,他们俩都很棒!感谢您进一步简化了我的查询。我还没有足够的积分来投票,但是当他们累积@Lukas 时我会的。干杯。
    • 很高兴知道。我认为,您始终可以“接受”,而无需投票的必要声誉
    • @ltran:为什么是-1?没有记录添加到 table_1,计数应该正确...
    猜你喜欢
    • 1970-01-01
    • 2019-02-15
    • 2021-08-12
    • 1970-01-01
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    相关资源
    最近更新 更多