繁体   English   中英

如何从双重中选择日期,但加入数据?

[英]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_beginMIN(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我需要的,不是所有的这些日期。

所以我正在寻找的理想解决方案是有三个选择查询:

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

sqlfiddle

如果您想要数据库中没有的每个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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM