Return 如果在 UNION ALL 查询中未找到任何行,则为默认值

Return a default value if no rows are found in UNION ALL query

场景

我有一个从这个 question 开发的查询,优化的一部分是创建一个 MySql 视图,用于生成用户和销售的统计信息,问题是当有SELECT 行之一没有结果,结果 table.

中省略了它

问题

如果没有找到 SELECT 中的任何行,如何告诉 MySql 设置默认值(例如 0)?

代码

这是创建视图的代码

CREATE OR REPLACE VIEW user_events AS
    SELECT 'Complete profiles' AS type, created_at
        FROM users
        WHERE completed_registration = 1

    UNION ALL SELECT 'Incomplete profiles', created_at
        FROM users
        WHERE completed_registration = 0 AND verified_email = 1

    UNION ALL SELECT 'Unverified profiles', created_at
        FROM users
        WHERE verified_email = 0

    UNION ALL SELECT 'Onsite Teachers', created_at
        FROM onsite_teachers

    UNION ALL SELECT 'Onsite Teachers hired', created_at
        FROM purchases
            INNER JOIN purchased_profiles
                ON purchased_profiles.purchase_id = purchases.id
                 AND purchased_profiles.profile_type = 'onsite_teacher'
        WHERE purchases.transaction_status = 'completed'

    UNION ALL SELECT 'Translators', created_at
        FROM translators

    UNION ALL SELECT 'Translators hired', created_at
        FROM purchases
            INNER JOIN purchased_profiles
                ON purchased_profiles.purchase_id = purchases.id
                AND purchased_profiles.profile_type = 'translator'
        WHERE purchases.transaction_status = 'completed'

    UNION ALL SELECT 'Interpreters', created_at
        FROM interpreters

    UNION ALL SELECT 'Interpreters hired', created_at
        FROM purchases
            INNER JOIN purchased_profiles
                ON purchased_profiles.purchase_id = purchases.id
                AND purchased_profiles.profile_type = 'interpreter'
        WHERE purchases.transaction_status = 'completed';

这是查询包括当前月份在内的最近6个月总计的代码。

SELECT 
    type, 
    COUNT(CASE 
        WHEN created_at >= CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY AS DATETIME) 
        THEN 1 
    END) AS 0_month_ago, 

    COUNT(CASE 
        WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 1 MONTH AS DATETIME)
            AND CAST(LAST_DAY(CURDATE() - INTERVAL 1 MONTH) + INTERVAL 1 DAY AS DATETIME)
        THEN 1 
    END) AS 1_month_ago,

    COUNT(CASE 
        WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 2 MONTH AS DATETIME)
            AND CAST(LAST_DAY(CURDATE() - INTERVAL 2 MONTH) + INTERVAL 1 DAY AS DATETIME)
        THEN 1 
    END) AS 2_months_ago,

    COUNT(CASE 
        WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 3 MONTH AS DATETIME)
            AND CAST(LAST_DAY(CURDATE() - INTERVAL 3 MONTH) + INTERVAL 1 DAY AS DATETIME)
        THEN 1 
    END) AS 3_months_ago,

    COUNT(CASE 
        WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 4 MONTH AS DATETIME)
            AND CAST(LAST_DAY(CURDATE() - INTERVAL 4 MONTH) + INTERVAL 1 DAY AS DATETIME)
        THEN 1 
    END) AS 4_months_ago,

    COUNT(CASE 
        WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 5 MONTH AS DATETIME)
            AND CAST(LAST_DAY(CURDATE() - INTERVAL 5 MONTH) + INTERVAL 1 DAY AS DATETIME)
        THEN 1 
    END) AS 5_months_ago
FROM 
    user_events
GROUP BY 
    type;

当前输出

如果仔细观察没有 Interpreters hiredTranslators hired 行,我希望设置这些行并在它们 return null

+=========================+===============+===============+===============+==============+===============+===============+
|          type           |  0_month_ago  |  1_month_ago  |  2_month_ago  | 3_month_ago  |  4_month_ago  |  5_month_ago  |
+=========================+===============+===============+===============+==============+===============+===============+
|    Complete profiles    |       7       |      20       |      14       |      25      |      30       |       7       |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
|   Incomplete profiles   |      12       |      27       |      56       |      45      |      48       |      23       |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
|   Unverified profiles   |       3       |      16       |      23       |       5      |       0       |       9       |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
|    Onsite Teachers      |      11       |      36       |       8       |      15      |      46       |      12       |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
|  Onsite Teachers hired  |       0       |       0       |      12       |       9      |       3       |       0       |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
|      Interpreters       |       4       |      21       |      27       |      46      |      45       |      28       |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
|      Translators        |       7       |      20       |      19       |      27      |      19       |      42       |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+

user_events视图是一种日志,看起来像

type                  | created_at
======================+==============
Interpreters hired    | 2014-12-12
Interpreters hired    | 2014-12-14
Interpreters hired    | 2014-12-16
Interpreters hired    | 2015-01-02

但是,如果从未雇用过任何口译员,则不会有带有 type = 'Interpreters hired' 的行。在那种情况下,计数查询不可能凭空捏造一个 Interpreters hired 行。

一个解决方案是确保 Interpreters hired 行出现在 user_events 视图中,无论如何。您可以创建没有 created_at 日期的虚构行。这样,GROUP BY 总会有一些东西,但 COUNT().

不一定会有任何东西
CREATE OR REPLACE VIEW user_events AS
    SELECT 'Complete profiles' AS type, created_at
        FROM users
        WHERE completed_registration = 1

    UNION ALL SELECT 'Incomplete profiles', created_at
        FROM users
        WHERE completed_registration = 0 AND verified_email = 1

    UNION ALL SELECT 'Unverified profiles', created_at
        FROM users
        WHERE verified_email = 0

    UNION ALL SELECT 'Onsite Teachers', created_at
        FROM onsite_teachers

    UNION ALL SELECT 'Onsite Teachers hired', created_at
        FROM purchases
            INNER JOIN purchased_profiles
                ON purchased_profiles.purchase_id = purchases.id
                 AND purchased_profiles.profile_type = 'onsite_teacher'
        WHERE purchases.transaction_status = 'completed'
    UNION ALL SELECT 'Onsite Teachers hired', NULL

    UNION ALL SELECT 'Translators', created_at
        FROM translators

    UNION ALL SELECT 'Translators hired', created_at
        FROM purchases
            INNER JOIN purchased_profiles
                ON purchased_profiles.purchase_id = purchases.id
                AND purchased_profiles.profile_type = 'translator'
        WHERE purchases.transaction_status = 'completed'
    UNION ALL SELECT 'Translators hired', NULL

    UNION ALL SELECT 'Interpreters', created_at
        FROM interpreters

    UNION ALL SELECT 'Interpreters hired', created_at
        FROM purchases
            INNER JOIN purchased_profiles
                ON purchased_profiles.purchase_id = purchases.id
                AND purchased_profiles.profile_type = 'interpreter'
        WHERE purchases.transaction_status = 'completed'
    UNION ALL SELECT 'Interpreters hired', NULL;