我遇到了一个我无法解决的 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。
我将在下图中将这些标记为红色:
橙色跨度也没有必要添加,但如果您的解决方案也可以这样做,那也可以。
好的,到目前为止一切顺利。
为此,我创建了SELECT * FROM minmax,它将为每个选择MIN(date_from)和。仍然没有涉及魔法。MAX(date_from)id_othertable
我现在要做的就是为每个创建那些日子id_othertable,同时还要加入他们拥有的数据(在这个小提琴中,它只是字段some_info)。
我试图在查询中写下这个SELECT * FROM days_before,但我无法让它工作。我读到了有关神奇函数的信息CONNECT BY,它将自行逐行创建日期,但我无法连接前一个表中的数据。每次我加入信息时,我只得到一行,id_othertable而不是我需要的所有日期。
因此,我正在寻找的理想解决方案是进行三个选择查询:
SELECT * FROM days从数据库中选择日期SELECT * FROM days_beforeMIN(date_from)这将显示查询 1之前的日期SELECT * FROM days_afterMAX(date_from)对于查询 1之后的日期最后我将这UNION三个查询全部合并起来。
我希望我能很好地解释我的问题。如果您需要任何信息或进一步解释,请随时询问。
编辑1:我创建了一个包含一些示例数据的pastebin:https://pastebin.com/jskrStpZ
请记住,只有第一个查询具有来自数据库的实际信息,其他两个查询已创建数据。另外,此示例输出仅包含 的数据id_othertable = 1,因此实际查询也应该包含 的信息id_othertable = 2, 3。
编辑2:只是为了澄清,该字段date_to只是一个简单的date_from + 1 day.
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;
Run Code Online (Sandbox Code Playgroud)
所以对于测试数据:
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;
Run Code Online (Sandbox Code Playgroud)
这输出:
身份证 | DATE_FROM | 日期 DATE_TO -: | :--------- | :--------- 1 | 2020-01-01 | 2020-01-05 1 | 2020-01-08 | 2020-01-10 1 | 2020-01-13 | 2020-02-01 2 | 2020-01-01 | 2020-01-10 2 | 2020-01-13 | 2020-02-01 3 | 2020-01-01 | 2020-01-20 3 | 2020-01-23 | 2020-02-01 4 | 2020-01-05 | 2020-01-30 5 | 2020-01-10 | 2020-02-01 6 | 2020-01-01 | 2020-01-15
db<>在这里摆弄
如果您想要之前的日子,请过滤:
WHERE day_from = DATE '2020-01-01'
Run Code Online (Sandbox Code Playgroud)
同样,如果您想要之后的日子,则过滤:
WHERE day_to = ADD_MONTHS( DATE '2020-01-01', 1 )
Run Code Online (Sandbox Code Playgroud)
如果要指定开始日期和持续时间的月数,请使用命名绑定参数:
ID | DATE_FROM | DATE_TO -: | :--------- | :--------- 1 | 2020-01-01 | 2020-01-05 1 | 2020-01-08 | 2020-01-10 1 | 2020-01-13 | 2020-02-01 2 | 2020-01-01 | 2020-01-10 2 | 2020-01-13 | 2020-02-01 3 | 2020-01-01 | 2020-01-20 3 | 2020-01-23 | 2020-02-01 4 | 2020-01-05 | 2020-01-30 5 | 2020-01-10 | 2020-02-01 6 | 2020-01-01 | 2020-01-15