【问题标题】:Creating a loop to compare time between dates (oracle SQL)创建一个循环来比较日期之间的时间(oracle SQL)
【发布时间】:2021-09-24 23:58:22
【问题描述】:

这是一个数据示例(我只是分享一个人的示例以帮助说明我的问题):

ID DATE
1 January 1, 2021
1 February 1, 2021
1 March 1, 2021
1 April 1, 2021
1 May 1, 2021
1 June 1, 2021

ID = 个人的唯一标识符

日期 = 该人注册计划的时间

一个人最多可以注册一个计划 40 天。在该期限结束后,他们再次有资格注册。但是,有时(出于各种管理目的)某人可能会在某人有资格之前为其注册。我正在尝试找出标记哪些注册实际符合条件的最佳方法。

例如,一月份的注册很好,因为那是会员的第一次注册(因此应该标记)。但是,他们的 2 月注册并未发生在之前符合条件的注册(从 1 月开始)的 40 天之后,因此不应被标记。 3 月注册确实发生在符合条件的 1 月注册后 40 多天,因此应该标记它。之前的合格注册(3 月)的 40 天后没有发生 4 月注册,因此不应标记......

我怀疑我需要创建某种循环(我知道如何在其他编程语言中做到这一点,但不知道 SQL。我的想法是:

  1. 一月是第一次注册,因此应标记并移至下一行(一月存储为日期参考)。

  2. 2 月未在 1 月参考后 40 天后出现,因此不应标记,移至下一行(1 月仍为参考)。

  3. 3 月确实发生在 1 月参考之后 40 多天,因此应该标记它,移动到下一行(3 月现在是参考)

.....

谁能帮我弄清楚我该如何做到这一点?

【问题讨论】:

  • 您的 Oracle 版本是多少?如果您不知道,请运行 select banner from v$version 并告诉我们它报告的内容 - 类似于 Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 的行(这是我运行该查询时系统报告的内容)。我的版本是 12.2.0.1.0。你的是什么?
  • " 怀疑我需要创建某种循环(我知道如何在其他编程语言中做到这一点” 您需要记住的重要一点是,那些“其他语言”都是过程性的。你编写如何来做某事,一步一步。但是SQL(不仅仅是oracle)是一个描述性的 i> 语言。您编写 what 您想要的代码,然后让数据库找出 how。此外,SQL 正在使用 sets。它不是逐行语言。

标签: oracle loops


【解决方案1】:

这是一个使用 match_recognize 的简短演示(自 Oracle 12.1 起提供)。如果您还不知道它,则需要一些时间来学习它;如果你熟悉分析函数和正则表达式(字符串),学习会更容易。

首先是一些测试数据(创建一个小表):

create table enrolments (id, enrolment_date) as
  select 1, date '2021-01-01' from dual union all
  select 1, date '2021-02-01' from dual union all
  select 1, date '2021-03-01' from dual union all
  select 1, date '2021-04-01' from dual union all
  select 1, date '2021-05-01' from dual union all
  select 1, date '2021-06-01' from dual union all
  select 8, date '2021-01-05' from dual union all
  select 8, date '2021-03-10' from dual union all
  select 8, date '2021-03-15' from dual union all
  select 8, date '2021-04-10' from dual
;

这里有一些注意事项 - DATE 是保留关键字,所以它不能是列名(我希望它不在您的实际数据中);在我的示例中,日期列是 date 数据类型(我希望它也在您的真实数据中,而不是字符串数据类型);我使用 date literal 语法来插入日期。

然后是查询和输出(以及更改输出中日期格式以匹配您的命令):

alter session set nls_date_format = 'fmMonth dd, yyyy';

select id, enrolment_date, flag
from   enrolments
match_recognize(
  partition by id
  order     by enrolment_date
  measures  nullif(classifier(), 'NOT_ELIGIBLE') as flag
  all rows per match
  pattern   ( ELIGIBLE NOT_ELIGIBLE* )
  define    NOT_ELIGIBLE as enrolment_date < ELIGIBLE.enrolment_date + 40
);

ID  ENROLMENT_DATE     FLAG           
--  -----------------  -----------
 1  January 1, 2021    ELIGIBLE       
 1  February 1, 2021                  
 1  March 1, 2021      ELIGIBLE       
 1  April 1, 2021                     
 1  May 1, 2021        ELIGIBLE       
 1  June 1, 2021                      
 8  January 5, 2021    ELIGIBLE       
 8  March 10, 2021     ELIGIBLE       
 8  March 15, 2021                    
 8  April 10, 2021      

【讨论】:

  • 非常感谢!这似乎工作得很好! :) 是的,我在这里分享的数据不是真实的,而且我的列有“真实”的名称,它们在这里不会有太大的意义,所以我只是将它们标记为 ID 和 DATE(即使你说 DATE 不能是列名)。
猜你喜欢
  • 1970-01-01
  • 2017-04-26
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
  • 2014-09-11
  • 2021-02-14
  • 1970-01-01
相关资源
最近更新 更多