Oracle中select子句取值先于order by子句执行而导致rownum伪列未按预期取值导致的问题

有这么一张表:

create table hytb(
    id number(4,0) not null primary key,
    padid nvarchar2(20) not null,
    inputdate date not null,
    dosid integer not null
)

可以这样给它充值:

insert into hytb(id,padid,inputdate,dosid) values('1','001',to_date('2020-01-23','yyyy-MM-dd'),'398');
insert into hytb(id,padid,inputdate,dosid) values('2','001',to_date('2020-01-24','yyyy-MM-dd'),'442');
insert into hytb(id,padid,inputdate,dosid) values('3','001',to_date('2020-01-25','yyyy-MM-dd'),'90');
insert into hytb(id,padid,inputdate,dosid) values('4','001',to_date('2020-01-26','yyyy-MM-dd'),'178');
insert into hytb(id,padid,inputdate,dosid) values('5','002',to_date('2020-01-23','yyyy-MM-dd'),'444');
insert into hytb(id,padid,inputdate,dosid) values('6','002',to_date('2020-01-24','yyyy-MM-dd'),'15');
insert into hytb(id,padid,inputdate,dosid) values('7','002',to_date('2020-01-25','yyyy-MM-dd'),'145');
insert into hytb(id,padid,inputdate,dosid) values('8','002',to_date('2020-01-27','yyyy-MM-dd'),'196');
insert into hytb(id,padid,inputdate,dosid) values('9','003',to_date('2020-01-24','yyyy-MM-dd'),'22');
insert into hytb(id,padid,inputdate,dosid) values('10','003',to_date('2020-01-25','yyyy-MM-dd'),'22');
insert into hytb(id,padid,inputdate,dosid) values('11','004',to_date('2020-01-24','yyyy-MM-dd'),'33');
insert into hytb(id,padid,inputdate,dosid) values('12','004',to_date('2020-01-25','yyyy-MM-dd'),'33');
insert into hytb(id,padid,inputdate,dosid) values('13','004',to_date('2020-01-28','yyyy-MM-dd'),'33');

充完值是这样的结果集:

现在需要从中找出inputdate最近的三条记录(从图中目测是28-JAN-20,27-JAN-20,26-JAN-20),请问该怎么做?

首先可以利用row_number()函数:

select * from (select row_number() over (order by inputdate desc) as rn,hytb.* from hytb) a  where a.rn<4

其次可以利用rownum伪列:

select * from (select rownum as rn,a.* from (select * from hytb order by inputdate desc) a) b where b.rn<4

这两种SQL都是正确的,可以互相验证。

但是,下面的sql却是错误的:

select * from (select rownum as rn,hytb.* from hytb order by  inputdate desc) a where a.rn<4

执行结果如下:

为什么会出现这样的结果呢,让我们先看看其中子句select rownum as rn,hytb.* from hytb order by  inputdate desc的执行结果:

可以发现,伪列和id是一样的,并没有按时间排。

究其原因,是因为select子句的执行顺序在order by之前,在sql:

select rownum as rn,hytb.* from hytb order by  inputdate desc

里,首先执行的是

select rownum as rn,hytb.* from hytb

其次才是

order by  inputdate desc

也就是说,伪列先于order by执行,或者说 select子句先于order by子句执行,由于rownum在select子句中,所以rownum伪列先于order by执行。

关于SQL子句的执行顺序,可以参考:https://database.51cto.com/art/202001/609727.htm

这就是问题所在。

突破藩篱的感觉真好。

--2020-03-10--

以下是上面实验用到的所有sql,贴出来以供参考:

create table hytb(
    id number(4,0) not null primary key,
    padid nvarchar2(20) not null,
    inputdate date not null,
    dosid integer not null
)

insert into hytb(id,padid,inputdate,dosid) values('1','001',to_date('2020-01-23','yyyy-MM-dd'),'398');
insert into hytb(id,padid,inputdate,dosid) values('2','001',to_date('2020-01-24','yyyy-MM-dd'),'442');
insert into hytb(id,padid,inputdate,dosid) values('3','001',to_date('2020-01-25','yyyy-MM-dd'),'90');
insert into hytb(id,padid,inputdate,dosid) values('4','001',to_date('2020-01-26','yyyy-MM-dd'),'178');
insert into hytb(id,padid,inputdate,dosid) values('5','002',to_date('2020-01-23','yyyy-MM-dd'),'444');
insert into hytb(id,padid,inputdate,dosid) values('6','002',to_date('2020-01-24','yyyy-MM-dd'),'15');
insert into hytb(id,padid,inputdate,dosid) values('7','002',to_date('2020-01-25','yyyy-MM-dd'),'145');
insert into hytb(id,padid,inputdate,dosid) values('8','002',to_date('2020-01-27','yyyy-MM-dd'),'196');
insert into hytb(id,padid,inputdate,dosid) values('9','003',to_date('2020-01-24','yyyy-MM-dd'),'22');
insert into hytb(id,padid,inputdate,dosid) values('10','003',to_date('2020-01-25','yyyy-MM-dd'),'22');
insert into hytb(id,padid,inputdate,dosid) values('11','004',to_date('2020-01-24','yyyy-MM-dd'),'33');
insert into hytb(id,padid,inputdate,dosid) values('12','004',to_date('2020-01-25','yyyy-MM-dd'),'33');
insert into hytb(id,padid,inputdate,dosid) values('13','004',to_date('2020-01-28','yyyy-MM-dd'),'33');

commit;

truncate table hytb

select * from hytb

--Correct
select * from (select rownum as rn,a.* from (select * from hytb order by inputdate desc) a) b where b.rn<4

select * from (select row_number() over (order by inputdate desc) as rn,hytb.* from hytb) a  where a.rn<4

--Wrong
select * from (select rownum as rn,hytb.* from hytb order by  inputdate desc) a where a.rn<4
原文地址:https://www.cnblogs.com/heyang78/p/12455930.html