如何从双重但具有连接数据的日期中选择日期?

Tro*_*wut 7 sql oracle

我遇到了一个我无法解决的 SQL 问题。

首先,一个 SQL 摆弄它:http://sqlfiddle.com/#!4/fe7b07 /2

正如您所看到的,我在表中填充了一些日期,这些日期与某个 ID 绑定。这些日期是一天一天的。因此,对于这个例子,如果我们只看一月份,我们就会得到这样的结果:

我应该成为一名画家,而不是数据库管理员

时间线从 2020-01-01 到 2020-01-31,区块是数据库中的日期。所以这将是简单的SELECT * FROM days输出。

我现在想要的是在这个输出中填写一些天数。这些范围从timeline_beginMIN(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而不是我需要的所有日期。

因此,我正在寻找的理想解决方案是进行三个选择查询:

  1. SELECT * FROM days从数据库中选择日期
  2. SELECT * FROM days_beforeMIN(date_from)这将显示查询 1之前的日期
  3. 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.

MT0*_*MT0 2

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