在序列中查找缺失的日期

Posted

技术标签:

【中文标题】在序列中查找缺失的日期【英文标题】:Finding missing dates in a sequence 【发布时间】:2016-04-25 17:05:49 【问题描述】:

我有下表的 ID 和日期

ID   DATE
123   7/1/2015
123   6/1/2015
123   5/1/2015
123   4/1/2015
123   9/1/2014
123   8/1/2014
123   7/1/2014
123   6/1/2014
456   11/1/2014
456   10/1/2014
456   9/1/2014
456   8/1/2014
456   5/1/2014
456   4/1/2014
456   3/1/2014
789   9/1/2014
789   8/1/2014
789   7/1/2014
789   6/1/2014
789   5/1/2014
789   4/1/2014
789   3/1/2014

在此表中,我有三个客户 ID,123、456、789 和显示他们工作的月份的日期列。

我想了解哪些客户的工作存在差距。 我们的客户工作记录是每月保存的......所以,日期是每月...... 每个客户都有不同的开始和结束日期。

预期结果:

ID     First_Absent_date

123    10/01/2014
456    06/01/2014

【问题讨论】:

我认为一种可行的方法是将最大和最小日期之间的月数与记录数进行比较(假设每个客户每月只有 1 条记录)@987654321 @和docs.oracle.com/cd/B19306_01/server.102/b14200/functions032.htm 您只想查看缺少月份的ID;您不想查看缺少哪些月份吗?而且您没有要检查的日期范围? 如果你有一个日期表,那就很容易了。我没有时间写一个正确的答案,但要搜索的术语是“sql numbers table”。 【参考方案1】:

要获得一个简单的有间隔的 ID 列表,没有更多详细信息,您需要分别查看每个 ID,正如@mikey 建议的那样,您可以计算月数并查看第一个和最后一个日期以查看如果跨越多少个月。

如果您的表有一个名为 month 的列(因为 date 是不允许的,除非它是带引号的标识符),您可以从以下开始:

select id, count(month), min(month), max(month),
  months_between(max(month), min(month)) + 1 as diff
from your_table
group by id
order by id;

        ID COUNT(MONTH) MIN(MONTH) MAX(MONTH)       DIFF
---------- ------------ ---------- ---------- ----------
       123            8 01-JUN-14  01-JUL-15          14
       456            7 01-MAR-14  01-NOV-14           9
       789            7 01-MAR-14  01-SEP-14           7

然后在having 子句中将计数与月份跨度进行比较:

select id
from your_table
group by id
having count(month) != months_between(max(month), min(month)) + 1
order by id;

        ID
----------
       123
       456

如果您实际上可以在一个月内为一个 ID 记录多条记录,和/或记录的日期可能不是月初,您可以做更多的工作来规范日期:

select id,
  count(distinct trunc(month, 'MM')),
  min(trunc(month, 'MM')),
  max(trunc(month, 'MM')),
  months_between(max(trunc(month, 'MM')), min(trunc(month, 'MM'))) + 1 as diff
from your_table
group by id
order by id;

select id
from your_table
group by id
having count(distinct trunc(month, 'MM')) !=
  months_between(max(trunc(month, 'MM')), min(trunc(month, 'MM'))) + 1
order by id;

【讨论】:

我还有一个请求要添加到其中。我想在客户缺席时获得第一次约会。【参考方案2】:

Oracle 设置

CREATE TABLE your_table ( ID, "DATE" ) AS
SELECT 123, DATE '2015-07-01' FROM DUAL UNION ALL
SELECT 123, DATE '2015-06-01' FROM DUAL UNION ALL
SELECT 123, DATE '2015-05-01' FROM DUAL UNION ALL
SELECT 123, DATE '2015-04-01' FROM DUAL UNION ALL
SELECT 123, DATE '2014-09-01' FROM DUAL UNION ALL
SELECT 123, DATE '2014-08-01' FROM DUAL UNION ALL
SELECT 123, DATE '2014-07-01' FROM DUAL UNION ALL
SELECT 123, DATE '2014-06-01' FROM DUAL UNION ALL
SELECT 456, DATE '2014-11-01' FROM DUAL UNION ALL
SELECT 456, DATE '2014-10-01' FROM DUAL UNION ALL
SELECT 456, DATE '2014-09-01' FROM DUAL UNION ALL
SELECT 456, DATE '2014-08-01' FROM DUAL UNION ALL
SELECT 456, DATE '2014-05-01' FROM DUAL UNION ALL
SELECT 456, DATE '2014-04-01' FROM DUAL UNION ALL
SELECT 456, DATE '2014-03-01' FROM DUAL UNION ALL
SELECT 789, DATE '2014-09-01' FROM DUAL UNION ALL
SELECT 789, DATE '2014-08-01' FROM DUAL UNION ALL
SELECT 789, DATE '2014-07-01' FROM DUAL UNION ALL
SELECT 789, DATE '2014-06-01' FROM DUAL UNION ALL
SELECT 789, DATE '2014-05-01' FROM DUAL UNION ALL
SELECT 789, DATE '2014-04-01' FROM DUAL UNION ALL
SELECT 789, DATE '2014-03-01' FROM DUAL;

查询

SELECT ID,
       MIN( missing_date )
FROM   (
  SELECT ID,
         CASE WHEN LEAD( "DATE" ) OVER ( PARTITION BY ID ORDER BY "DATE" )
                     = ADD_MONTHS( "DATE", 1 ) THEN NULL
              WHEN LEAD( "DATE" ) OVER ( PARTITION BY ID ORDER BY "DATE" )
                     IS NULL THEN NULL
              ELSE ADD_MONTHS( "DATE", 1 )
              END AS missing_date
  FROM   your_table
)
GROUP BY ID
HAVING COUNT( missing_date ) > 0;

输出

        ID MIN(MISSING_DATE) 
---------- -------------------
       123 2014-10-01 00:00:00 
       456 2014-06-01 00:00:00 

【讨论】:

【参考方案3】:

您可以使用 Lag() 函数来查看是否已跳过特定日期的记录。Lag() 基本上有助于将当前行中的数据与前一行中的数据进行比较。因此,如果我们按 DATE 订购,我们可以轻松比较并找到任何差距。

select * from 
   (
    select ID,DATE_, case when DATE_DIFF>1 then 1  else 0 end comparison from
        (
          select ID, DATE_ ,DATE_-LAG(DATE_, 1) OVER (PARTITION BY ID ORDER BY DATE_)  date_diff from trial
        )
    )
    where comparison=1 order by ID,DATE_;

这会按 id 对所有条目进行分组,然后按日期排列记录。如果客户总是在场,那么他的约会就不会出现空档。因此,日期差大于 1 的任何人都有一个差距。您可以根据自己的要求进行调整。

编辑:当我仔细观察上述答案时,刚刚观察到您正在以 mm/dd/yyyy 格式存储数据。您只存储每个月的第一个日期。因此,上述查询可以调整为:

select * from 
   (
    select ID,DATE_,PREV_DATE,last_day(PREV_DATE)+1 ABSENT_DATE, case when DATE_DIFF>31 then 1  else 0 end comparison from
        (
          select ID, DATE_ ,LAG(DATE_,1)  OVER (PARTITION BY ID ORDER BY DATE_)  PREV_DATE,DATE_-LAG(DATE_, 1) OVER (PARTITION BY ID ORDER BY DATE_)  date_diff from trial
        )
    )
    where comparison=1 order by ID,DATE_;

【讨论】:

以上是关于在序列中查找缺失的日期的主要内容,如果未能解决你的问题,请参考以下文章

查找 ID 列中的空白 + 选择上一个/下一个日期列

获取日期序列中所有缺失的日期

如何将缺失的日期添加到不带NA的列中?

R:在时间序列中填充缺失的日期?

在表中查找缺失的序列

查找序列中的缺失值