Select 并为没有任何数据的行分配 0

Select and assign 0 for the rows which does not have any data

我们有客户要求,如果对于特定条件,没有可用数据,则在从数据库中选择列时分配 0。

下面是table结构。我们需要从今天提取过去 6 个月的数据,其中性别为 'M'。如果某个月没有 'Male'/'Female'(根据 where 条件)参与者,则显示该月有 0 个参与者。

  yearMonth participants gender
    202101     1            F
    202102     0            M  
    202103     0            F
    202003     0            M
    202104     0            F
    202105     0            F
    202105     30           F
    202105     5            M
    202106     22           F
    202106     20           M
    202107     14           F
    202108     29           M 

没有任何 where 条件,查询如下所示 -

select yearMonth, sum(participants) as participants 
from table_name 
where yearMonth
between 202104 and 202108  group by yearMonth;

这个查询 returns -

  yearMonth participants 
    202103     0
    202104     0            
    202105     35           
    202106     42           
    202107     14           
    202108     29     

当我们添加 where 条件时 -

select yearMonth, sum(participants) as participants 
from table_name 
where yearMonth
between 202104 and 202108 
where gender = 'M' group by yearMonth;

它returns

  yearMonth participants           
    202105     5           
    202106     20           
    202107     14           
    202108     29  

但要求是,还要return202103和202104,其中0为参与者。

  yearMonth participants 
    202103     0
    202104     0            
    202105     5           
    202106     20           
    202107     14           
    202108     29   

我们可以使用 Postgre SQL 来完成吗?如果可以,请帮助我。

使用日历table方法:

WITH dates AS (
    SELECT CAST('2021-01-01' AS DATE) + (n || ' month')::INTERVAL dt
    FROM generate_series(0, 11) n
)

SELECT
    TO_CHAR(d.dt, 'YYYYMM')::int AS yearMonth,
    SUM(t.participants) FILTER (WHERE t.gender = 'M') AS male_participants
FROM dates d
LEFT JOIN table_name t
    ON t.yearMonth = d.yearMonth
WHERE
    TO_CHAR(d.dt, 'YYYYMM')::int BETWEEN 202101 AND 202108 AND        
GROUP BY
    TO_CHAR(d.dt, 'YYYYMM')::int;

您可以使用条件聚合

select yearMonth, sum(case when gender = 'M' then participants else 0 end) as participants 
from table_name 
where yearMonth
between 202104 and 202108 
group by yearMonth;

仅当今年月份的原始 table 中根本没有任何参与者 raw 时,才会缺少 yearMonth raw。

一种方法是条件聚合,在 Postgres 中看起来像这样:

select yearMonth,
       sum(participants) filter (where gender = 'M') as male_participants 
from table_name 
where yearMonth between 202104 and 202108 
group by yearMonth;

但是,您的条件是:

We need to pull last 6 months data from today where gender is 'M'.

为此,我建议 generate_series() 以便自动进行日期比较:

select v.yearMonth, 
       coalesce(sum(participants), 0) as male_participants
from generate_series(0, 5, 1) gs(n) cross join lateral
     (values (to_char(date_trunc(month, current_date) - gs.n * interval '1 month', 'YYYYMM')::int)
     ) v(yearMonth) left join
     table_name t
     on t.yearMonth = v.yearMonth::int and
        t.gender = 'M'
group by v.yearMonth
order by v.yearmonth;