按出现天数对结果进行分组

Group results by number of days appearing

我想获取某人在一个月内登录的天数。使用此查询:

select id,
       to_char(date_on, 'MM-DD') as mon_dd
from 
       logs
group by 
       id, to_char(date_on, 'MM-DD')

我得到一个 table,看起来像这样:

  id    | mon_dd
    0   | 01-27
    3   | 02-23
    1   | 01-05
    0   | 01-31
    2   | 02-01
    3   | 02-05
    1   | 02-09

我想得到一个结果,将 id 按它们在一个月中出现的天数分组,如下所示:

      id | month | days_appeared
       0 | jan   | 2
       0 | feb   | 0
       1 | jan   | 1
       1 | feb   | 1
       2 | jan   | 0
       2 | feb   | 1
       3 | jan   | 0
       3 | feb   | 2
  
 

您可以生成 table 中不同月份和用户的笛卡尔积,然后将 table 与 left join:

select 
    i.id, 
    d.date_month, 
    count(distinct trunc(l.date_on)) days_appeared
from (select distinct trunc(date_on, 'month') date_month from logs) d
cross join (select distinct id from logs) i
left join logs l 
    on  l.date_on >= d.date_month 
    and l.date_on < add_months(d.date_month, 1)
    and l.id = i.id
group by i.id, d.date_month

如果你想得到所有的月份,即使是那些有零的月份,那么:

select l.id, m.mon, count(distinct trunc(date_on)) as num_days
from (select distinct id from logs) i cross join
     (select distinct trunc(date_on, 'month') as mon) m left join
     logs l
     on l.id = i.id and trunc(date_on, 'month') = m.mon
group by l.id, m.mon;

注意:与在 logs table.

上使用 select distinct 相比,您可能拥有更有效的月份和 ID 来源

我喜欢使用 WITH 子句以模块化方式构建查询。

with vals as (                                      -- all information needed is here
  select id
  , to_char(mon_dd, 'mon') as month
  , to_char(mon_dd,'mm') as mm 
  from logs
),
months as (                                         -- the distinct months, 
select distinct month, mm                           -- including the month numbers 
from vals                                           -- for ordering the main query
),
ids as (                                            -- the distinct ids
select distinct id
from vals)
select i.id, m.month, (select count(id) from vals   -- for every combination of id
                       where month=m.month          -- and month
                       and  id = i.id) as count     -- count the number of ids
from ids i cross join months m
order by i.id, m.mm;