[英]How to select dates from dual, but with joined data?
我遇到了无法解决的 SQL 问题。
首先,一个 SQL 摆弄它: http ://sqlfiddle.com/#!4/ fe7b07/2
如您所见,我用一些日期填充了表格,这些日期与某个 ID 绑定。 这些日期是一天一天的。 所以对于这个例子,如果我们只看一月,我们会有这样的事情:
从 2020-01-01 到 2020-01-31 的时间线,块是数据库中的日期。 所以这将是简单的SELECT * FROM days
输出。
我现在想要的是在几天内填写此输出。 这些将从timeline_begin
到MIN(date_from)
; 和从MAX(date_from)
到timeline_end
MAX(date_from)
。
我将在下图中将这些标记为红色:
橙色跨度也没有必要添加,但是如果您的解决方案也可以这样做,那也可以。
好的,到目前为止一切顺利。
为此,我创建了SELECT * FROM minmax
,它将为每个id_othertable
选择MIN(date_from)
和MAX(date_from)
。 仍然不涉及魔法。
我现在挣扎的是为每个id_othertable
创造那些日子,同时加入他们拥有的数据(在这个小提琴中,它只是some_info
字段)。
我试图在SELECT * FROM days_before
查询中写这个,但我无法让它工作。 我读到了神奇的函数CONNECT BY
,它将自己逐行创建日期,但我无法从前一个表中加入我的数据。 每次我加入了信息,我只能得到每行id_othertable
我需要的,不是所有的这些日期。
所以我正在寻找的理想解决方案是有三个选择查询:
SELECT * FROM days
从数据库中选择日期SELECT * FROM days_before
将显示查询 1 的MIN(date_from)
之前的日期SELECT * FROM days_after
对于查询 1 的MAX(date_from)
之后的日期最后,我将UNION
这三个查询以将它们全部合并。
我希望我能很好地解释我的问题。 如果您需要任何信息或进一步解释,请随时提出。
编辑 1:我用一些示例数据创建了一个 pastebin: https : //pastebin.com/jskrStpZ
请记住,只有第一个查询具有来自数据库的实际信息,其他两个已创建数据。 此外,此示例输出仅包含id_othertable = 1
数据,因此实际查询也应包含id_othertable = 2, 3
。
编辑2:只是为了澄清,字段date_to
只是一个简单的date_from + 1 day
。
如果你有非规范化的日期,它很简单:
with bas as (
select 1 id_other_table, to_date('2020-01-05', 'YYYY-MM-DD') date_from, to_date('2020-01-06', 'YYYY-MM-DD') date_to, 'hello' some_info from dual
union all select 1 id_other_table, to_date('2020-01-06', 'YYYY-MM-DD') date_from, to_date('2020-01-07', 'YYYY-MM-DD') date_to, 'hello' some_info from dual
union all select 1 id_other_table, to_date('2020-01-07', 'YYYY-MM-DD') date_from, to_date('2020-01-08', 'YYYY-MM-DD') date_to, 'hello' some_info from dual
union all select 1 id_other_table, to_date('2020-01-10', 'YYYY-MM-DD') date_from, to_date('2020-01-11', 'YYYY-MM-DD') date_to, 'hello' some_info from dual
union all select 1 id_other_table, to_date('2020-01-11', 'YYYY-MM-DD') date_from, to_date('2020-01-12', 'YYYY-MM-DD') date_to, 'hello' some_info from dual
union all select 1 id_other_table, to_date('2020-01-12', 'YYYY-MM-DD') date_from, to_date('2020-01-13', 'YYYY-MM-DD') date_to, 'hello' some_info from dual
union all select 2 id_other_table, to_date('2020-01-10', 'YYYY-MM-DD') date_from, to_date('2020-01-11', 'YYYY-MM-DD') date_to, 'my' some_info from dual
union all select 2 id_other_table, to_date('2020-01-11', 'YYYY-MM-DD') date_from, to_date('2020-01-12', 'YYYY-MM-DD') date_to, 'my' some_info from dual
union all select 2 id_other_table, to_date('2020-01-12', 'YYYY-MM-DD') date_from, to_date('2020-01-13', 'YYYY-MM-DD') date_to, 'my' some_info from dual
union all select 3 id_other_table, to_date('2020-01-20', 'YYYY-MM-DD') date_from, to_date('2020-01-21', 'YYYY-MM-DD') date_to, 'friend' some_info from dual
union all select 3 id_other_table, to_date('2020-01-21', 'YYYY-MM-DD') date_from, to_date('2020-01-22', 'YYYY-MM-DD') date_to, 'friend' some_info from dual
union all select 3 id_other_table, to_date('2020-01-22', 'YYYY-MM-DD') date_from, to_date('2020-01-23', 'YYYY-MM-DD') date_to, 'friend' some_info from dual)
, ad as (select trunc(sysdate,'YYYY') -1 + level all_dates from dual connect by level <= 31)
select distinct some_info,all_dates from bas,ad where (some_info,all_dates) not in (select some_info,date_from from bas)
如果您有更长的日期范围或时间查询需要另一个解决方案是有帮助的。 但这更难调试。 因为很难拿到橙色时段
使用connect by
生成器connect by
选择整个范围。 加入按 id 分区的表。
select date_from, nvl(date_to, date_from +1) date_to, id_othertable, some_info
from (
select date '2020-01-01' + level - 1 as date_from
from dual
connect by level <= date '2020-01-31' - date '2020-01-01' ) gen
natural left join some_dates partition by (id_othertable)
如果您想要数据库中没有的每个id
的日期,那么您可以使用LEAD
分析函数:
WITH dates ( id, date_from, date_to ) AS (
SELECT id_othertable,
DATE '2020-01-01',
MIN( date_from )
FROM some_dates
WHERE date_to > DATE '2020-01-01'
AND date_from < ADD_MONTHS( DATE '2020-01-01', 1 )
GROUP BY id_othertable
UNION ALL
SELECT id_othertable,
date_to,
LEAD( date_from, 1, ADD_MONTHS( DATE '2020-01-01', 1 ) )
OVER ( PARTITION BY id_othertable ORDER BY date_from )
FROM some_dates
WHERE date_to > DATE '2020-01-01'
AND date_from < ADD_MONTHS( DATE '2020-01-01', 1 )
)
SELECT id,
date_from,
date_to
FROM dates
WHERE date_from < date_to
ORDER BY id, date_from;
所以对于测试数据:
CREATE TABLE some_dates ( id_othertable, date_from, date_to, some_info ) AS
SELECT 1, DATE '2020-01-05', DATE '2020-01-06', 'hello1' FROM DUAL UNION ALL
SELECT 1, DATE '2020-01-06', DATE '2020-01-07', 'hello2' FROM DUAL UNION ALL
SELECT 1, DATE '2020-01-07', DATE '2020-01-08', 'hello3' FROM DUAL UNION ALL
SELECT 1, DATE '2020-01-10', DATE '2020-01-13', 'hello4' FROM DUAL UNION ALL
SELECT 2, DATE '2020-01-10', DATE '2020-01-13', 'my' FROM DUAL UNION ALL
SELECT 3, DATE '2020-01-20', DATE '2020-01-23', 'friend' FROM DUAL UNION ALL
SELECT 4, DATE '2019-12-31', DATE '2020-01-05', 'before' FROM DUAL UNION ALL
SELECT 4, DATE '2020-01-30', DATE '2020-02-02', 'after' FROM DUAL UNION ALL
SELECT 5, DATE '2019-12-31', DATE '2020-01-10', 'only_before' FROM DUAL UNION ALL
SELECT 6, DATE '2020-01-15', DATE '2020-02-01', 'only_after' FROM DUAL UNION ALL
SELECT 7, DATE '2019-12-31', DATE '2020-02-01', 'exlude_all' FROM DUAL;
这输出:
\n身份证 | DATE_FROM | DATE_TO \n -: | :--------- | :---------\n 1 | 2020-01-01 | 2020-01-05\n 1 | 2020-01-08 | 2020-01-10\n 1 | 2020-01-13 | 2020-02-01\n 2 | 2020-01-01 | 2020-01-10\n 2 | 2020-01-13 | 2020-02-01\n 3 | 2020-01-01 | 2020-01-20\n 3 | 2020-01-23 | 2020-02-01\n 4 | 2020-01-05 | 2020-01-30\n 5 | 2020-01-10 | 2020-02-01\n 6 | 2020-01-01 | 2020-01-15\n
db<> 在这里摆弄
如果你想要前几天然后过滤:
WHERE day_from = DATE '2020-01-01'
并且,类似地,如果您想要之后的日子,则过滤:
WHERE day_to = ADD_MONTHS( DATE '2020-01-01', 1 )
如果要指定开始日期和持续时间的月数,请使用命名绑定参数:
WITH dates ( id, date_from, date_to ) AS (
SELECT id_othertable,
:start_date,
MIN( date_from )
FROM some_dates
WHERE date_to > :start_date
AND date_from < ADD_MONTHS( :start_date, :number_months )
GROUP BY id_othertable
UNION ALL
SELECT id_othertable,
date_to,
LEAD( date_from, 1, ADD_MONTHS( :start_date, :number_months ) )
OVER ( PARTITION BY id_othertable ORDER BY date_from )
FROM some_dates
WHERE date_to > :start_date
AND date_from < ADD_MONTHS( :start_date, :number_months )
)
SELECT id,
date_from,
date_to
FROM dates
WHERE date_from < date_to
ORDER BY id, date_from;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.