在Oracle sql中以T为状态的最后更改的数据

时间:2018-09-04 20:22:57

标签: sql oracle

我的数据如下

在下面的示例中,最新记录具有T,并且T的最后一次出现是在17年4月3日更新的,因此需要显示行

EMP     EFFDT     STATUS
11367   15-Apr-15   A
11367   14-Jun-15   A
11367   10-Aug-15   T
11367   2-Apr-17    A
11367   3-Apr-17    T *
11367   10-Apr-17   T

在下面的示例中,最新记录具有T,并且T的最后一次出现是在18年2月23日更新的,因此需要显示行

EMP     EFFDT     STATUS
20612   4-Sep-16    A
20612   23-Feb-18   T *
20612   20-Jul-18   T

在下面的示例中,最新记录具有T,这是唯一的出现,因此将其显示

EMP    EFFDT      STATUS
20644   12-Jul-15   A
20644   8-Aug-16    A
20644   6-Oct-16    T*

在下面的示例中,最新记录没有T,因此无需显示

EMP    EFFDT      STATUS
21155   18-May-17   T
21155   21-Jun-17   A
21155   13-Mar-18   T
21155   15-Aug-18   A

我的期望输出应为(带有*标记的记录)

EMP    EFFDT      STATUS
11367   3-Apr-17    T
20612   23-Feb-18   T
20644   6-Oct-16    T

4 个答案:

答案 0 :(得分:1)

这是一个孤岛和差距问题。

在cte中,您尝试找出哪个岛的T为最后更新(t = 0)

SQL DEMO

WITH cte as (
  SELECT "EMP", 
         "EFFDT", 
         SUM(CASE WHEN "STATUS" <> 'T' 
                  THEN 1 
                  ELSE 0 
             END) OVER (partition by "EMP" ORDER BY "EFFDT" DESC) as t
  FROM Table1
)  
SELECT "EMP", MIN("EFFDT") as "EFFDT", MAX('T') as "STATUS"
FROM cte 
WHERE t = 0
GROUP BY "EMP"

输出

|   EMP |                 EFFDT | STATUS |
|-------|-----------------------|--------|
| 11367 | 2017-04-03 00:00:00.0 |      T |
| 20612 | 2018-02-23 00:00:00.0 |      T |
| 20644 | 2016-10-06 00:00:00.0 |      T |

对于调试,您可以尝试

SELECT *
FROM cte 

查看如何创建t

答案 1 :(得分:0)

WITH cte1
AS (
    SELECT A.*
        ,lag(STATUS, 1, 0) OVER (
            PARTITION BY EMP ORDER BY EFFDT
            ) AS PRIOR_STATUS
    FROM Table1 A
    )
SELECT EMP
    ,STATUS
    ,MAX(EFFDT) AS EFFDT
FROM cte1 A
WHERE A.STATUS = 'T'
    AND A.PRIOR_STATUS <> 'T'
GROUP BY EMP
    ,STATUS

SQL小提琴在这里:http://sqlfiddle.com/#!4/458733/18

答案 2 :(得分:0)

alter session set nls_date_format = 'dd-Mon-rr';

解决方案(包括with子句中的模拟数据)

with
  simulated_data (EMP, EFFDT, STATUS) as (
    select 11367, to_date('15-Apr-15'), 'A' from dual union all
    select 11367, to_date('14-Jun-15'), 'A' from dual union all
    select 11367, to_date('10-Aug-15'), 'T' from dual union all
    select 11367, to_date( '2-Apr-17'), 'A' from dual union all
    select 11367, to_date( '3-Apr-17'), 'T' from dual union all
    select 11367, to_date('10-Apr-17'), 'T' from dual union all
    select 20612, to_date( '4-Sep-16'), 'A' from dual union all
    select 20612, to_date('23-Feb-18'), 'T' from dual union all
    select 20612, to_date('20-Jul-18'), 'T' from dual union all
    select 20644, to_date('12-Jul-15'), 'A' from dual union all
    select 20644, to_date( '8-Aug-16'), 'A' from dual union all
    select 20644, to_date( '6-Oct-16'), 'T' from dual union all
    select 21155, to_date('18-May-17'), 'T' from dual union all
    select 21155, to_date('21-Jun-17'), 'A' from dual union all
    select 21155, to_date('13-Mar-18'), 'T' from dual union all
    select 21155, to_date('15-Aug-18'), 'A' from dual
  )
-- End of simulated data (for testing only).
-- SQL query (solution) begins BELOW THIS LINE.
select emp, min(effdt) as eff_dt, 'T' as status
from   (
         select emp, effdt, status,
                row_number() over (partition by emp, status 
                                   order by effdt desc)           as rn,
                min(status) keep (dense_rank last order by effdt)
                             over (partition by emp)              as last_status
         from   simulated_data
       )
where  last_status = 'T' and status = 'T' and rn <= 2
group by emp
;

输出:

       EMP EFF_DT    STATUS
---------- --------- ------
     11367 03-Apr-17 T
     20612 23-Feb-18 T
     20644 06-Oct-16 T

说明:

在子查询中,我们将两列添加到输入数据中。 RN列按EMPNOSTATUS在每个分区中按EFFDT降序排列。 LAST_STATUS使用了LAST()函数的解析版本,将TA分配为每个EMP的最后状态(并将此值附加到EVERY EMP的一行,而不考虑每一行自己的STATUS)。

在外部查询中,我们仅保留最后一个状态为EMP的{​​{1}}。对于这些行,我们只希望保留实际上其实际状态为T的行(顺便说一句,我们知道这将始终包括该T的最后一行,并且将有EMP)。此外,我们只对RN = 1为1或可能为2的那些行感兴趣(如果该RN至少有两行的状态为T)。对于给定的EMP,这些状态为T的一两行中,我们希望获得最早的日期。如果该分区不存在带有EMP的行,则为唯一日期;否则,它将是前一行的日期,并带有RN = 2

在外部RN = 2中,我们选择SELECT,最早的日期以及我们已经知道的状态,即EMP(因此,我们不需要任何工作-实际上,目前尚不清楚为什么甚至需要第三列,因为事先知道它在所有行中都是T

答案 3 :(得分:0)

假设A和T是唯一的状态,这应该可以工作。

WITH cte1
        AS (
            SELECT A.EMP, A.EFFDT, A.STATUS
                ,min(STATUS) OVER (
                    PARTITION BY EMP ORDER BY EFFDT RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING 
                    ) AS MIN_STATUS
            FROM Table1 A
            )
    SELECT
       cte1.EMP
      ,MIN(cte1.EFFDT) AS EFFDT
      ,MIN(cte1.STATUS) as STATUS
    FROM cte1
    WHERE cte1.MIN_STATUS = 'T'
    GROUP BY EMP

编辑:好吧,如果您还有其他雕像,让我们使其更坚固。实际上,它与juan-carlos-oropeza提出的提议几乎相同,但是他错过了“电流行和无边界跟随之间的距离”部分。

糟糕,它是相同的解决方案:juan-carlos-oropeza使用了DESC的订单,而不是无限制的关注。

    with emp_status_log (EMP, EFFDT, STATUS) as
(
    select 11367, to_date('15-Apr-15', 'dd-Mon-yy'), 'A' from dual union all
    select 11367, to_date('14-Jun-15', 'dd-Mon-yy'), 'A' from dual union all
    select 11367, to_date('10-Aug-15', 'dd-Mon-yy'), 'T' from dual union all
    select 11367, to_date( '2-Apr-17', 'dd-Mon-yy'), 'A' from dual union all
    select 11367, to_date( '3-Apr-17', 'dd-Mon-yy'), 'T' from dual union all
    select 11367, to_date('10-Apr-17', 'dd-Mon-yy'), 'T' from dual union all

    select 20612, to_date( '4-Sep-16', 'dd-Mon-yy'), 'A' from dual union all
    select 20612, to_date('23-Feb-18', 'dd-Mon-yy'), 'T' from dual union all
    select 20612, to_date('20-Jul-18', 'dd-Mon-yy'), 'T' from dual union all

    select 20644, to_date('12-Jul-15', 'dd-Mon-yy'), 'A' from dual union all
    select 20644, to_date( '8-Aug-16', 'dd-Mon-yy'), 'A' from dual union all
    select 20644, to_date( '6-Oct-16', 'dd-Mon-yy'), 'T' from dual union all

    select 21155, to_date('18-May-17', 'dd-Mon-yy'), 'T' from dual union all
    select 21155, to_date('21-Jun-17', 'dd-Mon-yy'), 'A' from dual union all
    select 21155, to_date('13-Mar-18', 'dd-Mon-yy'), 'T' from dual union all
    select 21155, to_date('15-Aug-18', 'dd-Mon-yy'), 'A' from dual
  )
,
-- End of simulated data (for testing only).
/* SQL query (solution) begins BELOW THIS LINE.
with--*/
cte1 as
( 
   select sl.*
     ,sum(decode(sl.STATUS, 'T', 0, 1)) OVER (
                       PARTITION BY sl.EMP ORDER BY sl.EFFDT RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING 
                       ) AS non_t_count
   from emp_status_log sl
)
select
   cte1.emp
 , min(cte1.effdt) as effdt
 , min(cte1.status) as status
from cte1
where cte1.non_t_count = 0
group by cte1.emp