繁体   English   中英

从Oracle中选择最新的两个不同记录

[英]Selecting the latest two distinct records from Oracle

我有一个由INT(CUSTOMER)类型的多个记录组成的表,需要为帐户的NAME获取最后两个不同的事务。 所以,在我的SQL中,我有以下内容提供最新的4个事务都是正确的(我必须选择最新的4条记录,因为,如你所见,选择最新的两条记录会导致两条记录都是NAME'JOHNSON') :

SQL> SELECT ID, NAME, DATE, CUSTOMER
    FROM (select * from ORDER_TABLE ORDER BY DATE DESC) ORDER_TABLE
    WHERE rownum <= 4
    and (CUSTOMER = 1002) 
    ORDER BY rownum DESC;

        ID NAME                DATE         CUSTOMER
---------- ------------------- ---------    ----------
        90 SMITH               26-DEC-17    1002
       135 JOHNSON             09-DEC-17    1002
       235 JOHNSON             01-JAN-18    1002
       322 JOHNSON             04-JAN-18    1002

但是,我需要返回的只是最新的DISTINCT NAME命令,所以不过上面的输出,我只想看到:

        90 SMITH               26-DEC-17    1002
       322 JOHNSON             04-JAN-18    1002

我是否可以在单个语句中执行查询以获得所需的输出? 任何帮助将不胜感激!!!!

这是一种方法 - 使用分析函数。 这有点复杂; 遗憾的是, count(distinct ...)可以用作分析函数,但只能使用partition by子句 - 而不是order by子句。 因此,我们必须使用组启动技术手动创建自己。

请注意,WITH子句中的第一个子查询仅用于生成测试数据; 如果对实际表使用它,请从代码中删除它,并确保第二个子查询中的表名(和列名!)是正确的。 我用dt替换了列名date ; date是保留字,不应用作列名。

我正在展示一个通用解决方案 - 如果需要,您当然可以过滤单个客户编号。 (或者,如果您一次只需要一个客户,则可以稍微简化查询。)

with
  tbl ( id, name, dt, customer ) as (
    select  90, 'SMITH'  , to_date('26-DEC-17', 'dd-MON-rr'), 1002 from dual union all
    select 135, 'JOHNSON', to_date('09-DEC-17', 'dd-MON-rr'), 1002 from dual union all
    select 235, 'JOHNSON', to_date('01-JAN-18', 'dd-MON-rr'), 1002 from dual union all
    select 322, 'JOHNSON', to_date('04-JAN-18', 'dd-MON-rr'), 1002 from dual
  ),
  flg ( id, name, dt, customer, flag ) as (
    select id, name, dt, customer,
           case when lag(name) over (partition by customer order by dt desc) = name
                then null else 1 end
    from   tbl
  ),
  prep ( id, name, dt, customer, sm ) as (
    select id, name, dt, customer,
           sum(flag) over (partition by customer order by dt desc)
    from   flg
  )
select max(id) keep (dense_rank first order by dt desc) as id,
       name, max(dt) as dt, customer
from   prep
where  sm <= 2
group by customer, name
;

        ID NAME    DT          CUSTOMER
---------- ------- --------- ----------
        90 SMITH   26-DEC-17       1002
       322 JOHNSON 04-JAN-18       1002

在Oracle 12.1及更高版本中, match_recognize可以快速完成这些要求(并且通常比分析函数方法快得多)。

我不会在本答案中写一篇关于match_recognize的完整教程; 我只想指出PATTERN语法中的{- ... -}意味着这些行是匹配定义的一部分,但这些行是从输出中排除的。 其余的是match_recognize的基本应用。

我正在展示一个通用解决方案 - 如果需要,您当然可以过滤单个客户编号。 (或者,如果您一次只需要一个客户,则可以稍微简化查询。)

with
  tbl ( id, name, dt, customer ) as (
    select  90, 'SMITH'  , to_date('26-DEC-17', 'dd-MON-rr'), 1002 from dual union all
    select 135, 'JOHNSON', to_date('09-DEC-17', 'dd-MON-rr'), 1002 from dual union all
    select 235, 'JOHNSON', to_date('01-JAN-18', 'dd-MON-rr'), 1002 from dual union all
    select 322, 'JOHNSON', to_date('04-JAN-18', 'dd-MON-rr'), 1002 from dual
  )
select *
from   tbl
match_recognize(
  partition by customer
  order by dt desc
  all rows per match
  pattern ( ^ a {- b* -} c )
  define b as name = a.name
);

  CUSTOMER DT                ID NAME  
---------- --------- ---------- -------
      1002 04-JAN-18        322 JOHNSON
      1002 26-DEC-17         90 SMITH  
CREATE TABLE SANORD ( ID INT, NAME VARCHAR2(20), ODATE DATE, CUST INT);
Table created.

INSERT INTO SANORD VALUES (90, 'SMITH','26-DEC-17',1002);
1 row created.

INSERT INTO SANORD VALUES (135, 'JOHNSON','09-DEC-17',1002);
1 row created.

INSERT INTO SANORD VALUES (235, 'JOHNSON','01-JAN-18',1002);
1 row created.

INSERT INTO SANORD VALUES (322, 'JOHNSON','04-JAN-18',1002);
1 row created.

COMMIT;

SELECT id, name, odate, cust
FROM   sanord
WHERE  (name, odate) IN
       ( select name, max(odate) from sanord where cust = 1002 group by name)
;

        ID NAME                 ODATE           CUST
---------- -------------------- --------- ----------
        90 SMITH                26-DEC-17       1002
       322 JOHNSON              04-JAN-18       1002
SELECT id, name, date, customer
FROM   order_table
WHERE  (name, date) IN 
       (SELECT name, max(date)
        FROM   order_table
        WHERE  customer = 1002
        GROUP BY name)
;

当然使用IN并不是大表中最好的,你可以使用内连接。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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