[英]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.