可以使用 sql 在月历中显示事件吗?
Posted
技术标签:
【中文标题】可以使用 sql 在月历中显示事件吗?【英文标题】:Can you display events in a monthly calendar using sql? 【发布时间】:2020-09-01 14:29:08 【问题描述】:我有一张包含事件(日期和描述)的表格。 我想以当月的日历格式显示即将发生的事件。 使用 SQL 以日历格式显示当前事件的最佳方式是什么? 我的目标是 Oracle,但也许有比其他解决方案更通用的解决方案。
输出应该类似于这个:
WEEK | MON | TUE ! WED | THU | FRI | SAT | SUN
--------- ------------------- ----------- ----------- ----------------- ----------- ----------- -----------
36 | | 01 | 02 | 03 stay at home | 04 | 05 | 06
37 | 07 | 08 | 09 | 10 | 11 | 12 | 13
38 | 14 work from home | 15 | 16 | 17 | 18 | 19 | 20
39 | 21 | 22 | 23 | 24 | 25 | 26 | 27
40 | 28 | 29 | 30 | | | |
【问题讨论】:
您的源数据是否每天都有一个条目,或者您是否需要报告来填补空白?每天可以举办多个活动吗? 好问题@WilliamRobertson,报告将填补空白,但如果每天能有多个事件就好了,所以我猜想列出一个列表。 【参考方案1】:样本数据(包括有两个事件的一天):
create table calendar
( cal_date date
, cal_event varchar2(50) );
insert all
into calendar values (date '2020-09-03', 'stay at home')
into calendar values (date '2020-09-03', 'play Fallout')
into calendar values (date '2020-09-14', 'work from home')
select * from dual;
报告:
select cal_week as week
, mon_events as mon
, tue_events as tue
, wed_events as wed
, thu_events as thu
, fri_events as fri
from ( select to_char(d.dt,'Dy') as cal_day
, to_number(to_char(d.dt,'iw')) as cal_week
, to_char(d.dt,'DD')||' '||listagg(cal_event,', ') within group (order by c.cal_date) as cal_event
from ( select date '2020-09-01' -1 + rownum as dt
from xmltable('1 to 30' ) ) d
left join calendar c on c.cal_date = d.dt
group by d.dt
)
pivot (max(cal_event) as events
for (cal_day) in ('Mon' as mon, 'Tue' as tue, 'Wed' as wed, 'Thu' as thu, 'Fri' as fri))
order by cal_week;
结果:
+------+-------------------+-----+-----+-------------------------------+-----+
| WEEK | MON | TUE | WED | THU | FRI |
+------+-------------------+-----+-----+-------------------------------+-----+
| 36 | | 01 | 02 | 03 play Fallout, stay at home | 04 |
| 37 | 07 | 08 | 09 | 10 | 11 |
| 38 | 14 work from home | 15 | 16 | 17 | 18 |
| 39 | 21 | 22 | 23 | 24 | 25 |
| 40 | 28 | 29 | 30 | | |
+------+-------------------+-----+-----+-------------------------------+-----+
我必须在pivot
之前将日期编号与事件列表连接起来,这意味着聚合必须在子查询中,而max(cal_event)
只是为了满足需要聚合函数的pivot
语法。
【讨论】:
哇,@William,这看起来很棒!我想我更喜欢更通用的方式来生成当月的所有日期,但是 xmltable 函数和 pivot 是不错的技巧!而且我不知道 insert all ... select * from dual!非常感谢您的帮助! 这项技术(xmltable('1 to 30' )
)真的很棒,罗伯逊先生!【参考方案2】:
您可以将条件聚合与GROUP
ing BY
周数一起使用:
WITH events AS
(
SELECT date'2020-09-03' AS dy, 'stay at home' AS event
FROM dual
UNION ALL
SELECT date'2020-09-14' AS dy
, 'work FROM home' AS event
FROM dual
UNION ALL
SELECT date'2020-10-14' AS dy
, 'work FROM home' AS event
FROM dual
) , month(dy) AS (
SELECT trunc(sysdate, 'month') + level - 1 AS dy
FROM dual
CONNECT BY level <= last_day(sysdate)-trunc(sysdate, 'month') + 1
)
SELECT TO_CHAR( m.dy, 'iw' ) AS week,
MAX(CASE WHEN TO_CHAR( m.dy, 'DY' ) = 'MON' THEN TO_CHAR( m.dy, 'DD ' )||e.event END) AS "MON",
MAX(CASE WHEN TO_CHAR( m.dy, 'DY' ) = 'TUE' THEN TO_CHAR( m.dy, 'DD ' )||e.event END) AS "TUE",
MAX(CASE WHEN TO_CHAR( m.dy, 'DY' ) = 'WED' THEN TO_CHAR( m.dy, 'DD ' )||e.event END) AS "WED",
MAX(CASE WHEN TO_CHAR( m.dy, 'DY' ) = 'THU' THEN TO_CHAR( m.dy, 'DD ' )||e.event END) AS "THU",
MAX(CASE WHEN TO_CHAR( m.dy, 'DY' ) = 'FRI' THEN TO_CHAR( m.dy, 'DD ' )||e.event END) AS "FRI",
MAX(CASE WHEN TO_CHAR( m.dy, 'DY' ) = 'SAT' THEN TO_CHAR( m.dy, 'DD ' )||e.event END) AS "SAT",
MAX(CASE WHEN TO_CHAR( m.dy, 'DY' ) = 'SUN' THEN TO_CHAR( m.dy, 'DD ' )||e.event END) AS "SUN"
FROM month m
LEFT JOIN events e
ON e.dy = m.dy
GROUP BY TO_CHAR( m.dy, 'iw' )
Demo
【讨论】:
谢谢@Rabaros,这是降低复杂性的好方法! 顺便说一下,@Barbaros,我想我更喜欢 with-clause 方式来生成天数,因为按级别连接只能在 Oracle 中使用。我不知道其中一个是否有更好的性能。 不客气@Marko。是的,应该测试性能。顺便说一句,也可以使用 Pivot 子句。【参考方案3】:我想出了这个解决方案。也许有人会得到帮助,但我也对其他解决方案感到好奇。
经过编辑以包括同一天的多个事件。
with events as (
select date'2020-09-03' as dy
, 'stay at home' as event
from dual
union all
select date'2020-09-03' as dy
, 'stay at home 2' as event
from dual
union all
select date'2020-09-14' as dy
, 'work from home' as event
from dual
union all
select date'2020-10-14' as dy
, 'work from home' as event
from dual
) ,
day_events as (
select dy, listagg(event,', ') within group (order by event) as event
from events
group by dy
)
, month(dy) as (
select trunc(sysdate, 'month') as dy from dual
union all
select dy + 1 from month
where dy < last_day(sysdate)-1
)
, days as (
select to_char(dy, 'iw') as week,
case to_char(dy, 'dy') when 'mon' then dy end as mon,
case to_char(dy, 'dy') when 'tue' then dy end as tue,
case to_char(dy, 'dy') when 'wed' then dy end as wed,
case to_char(dy, 'dy') when 'thu' then dy end as thu,
case to_char(dy, 'dy') when 'fri' then dy end as fri,
case to_char(dy, 'dy') when 'sat' then dy end as sat,
case to_char(dy, 'dy') when 'sun' then dy end as sun
from month
), calendar as (
select week
, max(mon) as mon
, max(tue) as tue
, max(wed) as wed
, max(thu) as thu
, max(fri) as fri
, max(sat) as sat
, max(sun) as sun
from days
group by week
order by week)
select week
, '| '||decode(mon, e.dy, to_char(mon,'dd')||' '||e.event, to_char(mon, 'dd')) as "| MON"
, '| '||decode(tue, e.dy, to_char(tue,'dd')||' '||e.event, to_char(tue, 'dd')) as "| TUE"
, '| '||decode(wed, e.dy, to_char(wed,'dd')||' '||e.event, to_char(wed, 'dd')) as "! WED"
, '| '||decode(thu, e.dy, to_char(thu,'dd')||' '||e.event, to_char(thu, 'dd')) as "| THU"
, '| '||decode(fri, e.dy, to_char(fri,'dd')||' '||e.event, to_char(fri, 'dd')) as "| FRI"
, '| '||decode(sat, e.dy, to_char(sat,'dd')||' '||e.event, to_char(sat, 'dd')) as "| SAT"
, '| '||decode(sun, e.dy, to_char(sun,'dd')||' '||e.event, to_char(sun, 'dd')) as "| SUN"
from calendar c left join day_events e
on c.week = to_char(e.dy, 'iw')
order by c.week;
【讨论】:
以上是关于可以使用 sql 在月历中显示事件吗?的主要内容,如果未能解决你的问题,请参考以下文章