检查一个表是否应该在3个月的范围内连续3个月连续3个记录?

时间:2015-11-03 05:59:06

标签: sql oracle

在我的表中,我有YMonth列,YYONY格式的YMONTH值(如201510)。目前我正在这样做,

SELECT COUNT(*) FROM MyTable WHERE XXX AND YMONTH 
in(TO_CHAR(add_months(SYSDATE,0),'YYYYMM'),
TO_CHAR(add_months(SYSDATE,-1),'YYYYMM'),
TO_CHAR(add_months(SYSDATE,-2),'YYYYMM'))

如果count = 3则OK。否则,我会执行,

SELECT COUNT(*) FROM MyTable WHERE XXX AND YMONTH 
in(TO_CHAR(add_months(SYSDATE,-1),'YYYYMM'),
TO_CHAR(add_months(SYSDATE,-2),'YYYYMM'),
TO_CHAR(add_months(SYSDATE,-3),'YYYYMM'))

如果count = 3则OK。否则,我会执行,

SELECT COUNT(*) FROM MyTable WHERE XXX AND YMONTH 
in(TO_CHAR(add_months(SYSDATE,-2),'YYYYMM'),
TO_CHAR(add_months(SYSDATE,-3),'YYYYMM'),
TO_CHAR(add_months(SYSDATE,-4),'YYYYMM'))

这很有效。但我想让它变得简单,只想执行一个sql。

4 个答案:

答案 0 :(得分:2)

您可以使用分层查询来查找连续的月份:

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE MyTable ( YMONTH ) AS
          SELECT '201511' FROM DUAL
UNION ALL SELECT '201510' FROM DUAL
UNION ALL SELECT '201509' FROM DUAL
UNION ALL SELECT '201508' FROM DUAL
UNION ALL SELECT '201507' FROM DUAL

查询1

此查询将在3个月内为每个不同的连续条目集返回一行。在上表中有三个3个月的期间(7月至9月,8月至10月和9月至11月),因此结果集返回这三行。

SELECT YMONTH AS FROM_YMONTH,
       CONNECT_BY_ROOT( YMONTH ) AS TO_YMONTH
FROM   MyTable
WHERE  LEVEL = 3
START WITH TO_DATE( YMONTH, 'YYYYMM' ) BETWEEN TRUNC( SYSDATE, 'MM' ) - INTERVAL '2' MONTH AND TRUNC( SYSDATE, 'MM' )
CONNECT BY PRIOR TO_DATE( YMONTH, 'YYYYMM' ) - INTERVAL '1' MONTH = TO_DATE( YMONTH, 'YYYYMM' )

<强> Results

| FROM_YMONTH | TO_YMONTH |
|-------------|-----------|
|      201509 |    201511 |
|      201508 |    201510 |
|      201507 |    201509 |

查询2

如果您只想在此期间内连续最多(最多3个)月份的数字值,那么您可以使用此值(虽然它不会告诉您该期间的哪个月份):

SELECT MAX( LEVEL ) AS Max_Consecutive_Months
FROM   MyTable
START WITH TO_DATE( YMONTH, 'YYYYMM' ) BETWEEN TRUNC( SYSDATE, 'MM' ) - INTERVAL '2' MONTH AND TRUNC( SYSDATE, 'MM' )
CONNECT BY PRIOR TO_DATE( YMONTH, 'YYYYMM' ) - INTERVAL '1' MONTH = TO_DATE( YMONTH, 'YYYYMM' )
AND        LEVEL <= 3

<强> Results

| MAX_CONSECUTIVE_MONTHS |
|------------------------|
|                      3 |

答案 1 :(得分:0)

是的,您可以在单个查询中执行此操作。在一个查询中组合这些条件,然后对YMonth DESC 进行排序,并按 ROWNUM 进行限制

SELECT COUNT(*) FROM (
    SELECT XXX,YMONTH 
    FROM MyTable 
    WHERE XXX 
    AND YMONTH IN(
        TO_CHAR(add_months(SYSDATE,0),'YYYYMM'),
        TO_CHAR(add_months(SYSDATE,-1),'YYYYMM'),
        TO_CHAR(add_months(SYSDATE,-2),'YYYYMM'),
        TO_CHAR(add_months(SYSDATE,-3),'YYYYMM'),
        TO_CHAR(add_months(SYSDATE,-4),'YYYYMM')
    )
    ORDER BY YMONTH DESC
) WHERE ROWNUM <= 3

请尝试上面的查询,看看它是否有效。

答案 2 :(得分:0)

通过YMONTH订购您的桌子并添加一个rownum列:

-- test data
with mytable as
 (select '201501' as ymonth
    from dual
  union
  select '201503' as ymonth
    from dual
  union
  select '201504' as ymonth
    from dual
  union
  select '201505' as ymonth
    from dual
  union
  select '201507' as ymonth
    from dual
  union
  select '201508' as ymonth
    from dual
  union
  select '201510' as ymonth
    from dual
  union
  select '201511' as ymonth from dual),

ordered_data as
 (select ymonth, row_number() over(order by ymonth desc) as r
    from mytable)

select * from ordered_data;

结果:

ymonth  r
201511  1
201510  2
201508  3
201507  4
201505  5
201504  6
201503  7
201501  8

通过此结果,您可以按add_months(ymonth,r)进行分组。具有相同值的行属于一起,即

ymonth  r add_months(ymonth,r)
201511  1 201512
201510  2 201512
201508  3 201511
201507  4 201511
201505  5 201510 -
201504  6 201510 - first group of tree
201503  7 201510 -
201501  8 201509

以下sql提供了您三个月范围的最后一个月,根据您的需要进行调整:

-- test data
with mytable as
 (select '201501' as ymonth
    from dual
  union
  select '201503' as ymonth
    from dual
  union
  select '201504' as ymonth
    from dual
  union
  select '201505' as ymonth
    from dual
  union
  select '201507' as ymonth
    from dual
  union
  select '201508' as ymonth
    from dual
  union
  select '201510' as ymonth
    from dual
  union
  select '201511' as ymonth from dual),

ordered_data as
 (select ymonth, row_number() over(order by ymonth desc) as r
    from mytable)

select max(d.ymonth)
  from ordered_data d
 where add_months(to_date(d.ymonth || '01', 'YYYYMMDD'), r) =
       (select max(add_months(to_date(dd.ymonth || '01', 'YYYYMMDD'), r))
          from ordered_data dd
         group by add_months(to_date(dd.ymonth || '01', 'YYYYMMDD'), r)
        having count(*) >= 3);

sqlfiddle

答案 3 :(得分:0)

我不太确定你期望单个查询的输出是什么,但也许你会追求像:

with mytable as (select 1 xxx, 201511 ymonth from dual union all
                 select 1 xxx, 201510 ymonth from dual union all
                 select 1 xxx, 201509 ymonth from dual union all
                 select 1 xxx, 201508 ymonth from dual union all
                 select 2 xxx, 201510 ymonth from dual union all
                 select 2 xxx, 201509 ymonth from dual union all
                 select 2 xxx, 201508 ymonth from dual union all
                 select 3 xxx, 201511 ymonth from dual union all
                 select 3 xxx, 201509 ymonth from dual union all
                 select 3 xxx, 201508 ymonth from dual union all
                 select 3 xxx, 201507 ymonth from dual)
-- end of mimicking the mytable table with data in it
select xxx,
       ymonth,
       case when count(case when to_date(ymonth||'01', 'yyyymmdd') between add_months(trunc(sysdate, 'mm'), -2) and add_months(trunc(sysdate, 'mm'), 0) then 1 end) over (partition by xxx) = 3 then 'last_3_months'
            when count(case when to_date(ymonth||'01', 'yyyymmdd') between add_months(trunc(sysdate, 'mm'), -3) and add_months(trunc(sysdate, 'mm'), -1) then 1 end) over (partition by xxx) = 3 then 'last_3_months_but_1'
            when count(case when to_date(ymonth||'01', 'yyyymmdd') between add_months(trunc(sysdate, 'mm'), -4) and add_months(trunc(sysdate, 'mm'), -2) then 1 end) over (partition by xxx) = 3 then 'last_3_months_but_2'
       end month_range
from   mytable
where  to_date(ymonth||'01', 'yyyymmdd') between add_months(trunc(sysdate, 'mm'), -4) and add_months(trunc(sysdate, 'mm'), 0);            


       XXX     YMONTH MONTH_RANGE                     
---------- ---------- --------------------------------
         1     201511 last_3_months                   
         1     201510 last_3_months                   
         1     201509 last_3_months                   
         1     201508 last_3_months                   
         2     201510 last_3_months_but_1             
         2     201509 last_3_months_but_1             
         2     201508 last_3_months_but_1             
         3     201511 last_3_months_but_2             
         3     201509 last_3_months_but_2             
         3     201508 last_3_months_but_2             
         3     201507 last_3_months_but_2