最近我修复了一些错误:连接条件中存在rownum。
这样的事情:在t1.id = t2.id和rownum< 2上左连接t1。因此无论“左连接”如何,它都应该只返回一行。
当我进一步研究这个时,我意识到我不明白Oracle如何在“左连接”条件下评估rownum。 让我们创建两个样本表:主表和详细信息。
create table MASTER
(
ID NUMBER not null,
NAME VARCHAR2(100)
)
;
alter table MASTER
add constraint PK_MASTER primary key (ID);
prompt Creating DETAIL...
create table DETAIL
(
ID NUMBER not null,
REF_MASTER_ID NUMBER,
NAME VARCHAR2(100)
)
;
alter table DETAIL
add constraint PK_DETAIL primary key (ID);
alter table DETAIL
add constraint FK_DETAIL_MASTER foreign key (REF_MASTER_ID)
references MASTER (ID);
prompt Disabling foreign key constraints for DETAIL...
alter table DETAIL disable constraint FK_DETAIL_MASTER;
prompt Loading MASTER...
insert into MASTER (ID, NAME)
values (1, 'First');
insert into MASTER (ID, NAME)
values (2, 'Second');
commit;
prompt 2 records loaded
prompt Loading DETAIL...
insert into DETAIL (ID, REF_MASTER_ID, NAME)
values (1, 1, 'REF_FIRST1');
insert into DETAIL (ID, REF_MASTER_ID, NAME)
values (2, 1, 'REF_FIRST2');
insert into DETAIL (ID, REF_MASTER_ID, NAME)
values (3, 1, 'REF_FIRST3');
commit;
prompt 3 records loaded
prompt Enabling foreign key constraints for DETAIL...
alter table DETAIL enable constraint FK_DETAIL_MASTER;
set feedback on
set define on
prompt Done.
然后我们有了这个问题:
select * from master t
left join detail d on d.ref_master_id=t.id
结果集是可预测的:我们拥有主表中的所有行和详细信息表中符合此条件的3行d.ref_master_id = t.id。
然后我将“rownum = 1”添加到连接条件,结果是相同的
select * from master t
left join detail d on d.ref_master_id=t.id and rownum=1
最有趣的是我设置了“rownum< -666”并再次得到了相同的结果!
select * from master t
left join detail d on d.ref_master_id=t.id and rownum<-666.
由于结果集,我们可以说这个条件在详细信息表中被评估为3行的“True”。但是,如果我使用“内部联接”,一切都应该如此。
select * from master t
join detail d on d.ref_master_id=t.id and rownum<-666.
此查询不会返回任何行,因为我无法想象rownum会少于-666: - )
此外,如果我使用oracle语法进行外连接,使用“(+)”一切也顺利。
select * from master m ,detail t
where m.id=t.ref_master_id(+) and rownum<-666.
此查询也不会返回任何行。
有谁能告诉我,我误解了外连接和rownum?
答案 0 :(得分:8)
ROWNUM是结果集的伪属性,而不是基表的伪属性。 ROWNUM是在选择行之后定义的,但是在它们按ORDER BY子句排序之前定义。
编辑:我在之前的ROWNUM写作中错了,所以这里有新的信息:
您可以在WHERE
子句中以有限的方式使用ROWNUM,以测试它是否仅小于正整数。有关详细信息,请参阅ROWNUM Pseudocolumn。
SELECT ... WHERE ROWNUM < 10
目前尚不清楚ROWNUM在JOIN子句的上下文中具有什么值,因此结果可能未定义。似乎有一些特殊情况下使用ROWNUM处理表达式,例如WHERE ROWNUM > 10
总是返回false。我不知道你的JOIN子句中ROWNUM<-666
是如何工作的,但它没有意义所以我不推荐使用它。
在任何情况下,这都无法帮助您获取每个给定主行的第一个详细信息行。
要解决此问题,您可以使用analytic functions and PARTITION
,并将其与Common Table Expressions结合使用,这样您就可以在WHERE
条件下访问行号列。
WITH numbered_cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY d.something) AS rn
FROM master t LEFT OUTER JOIN detail d ON d.ref_master_id = t.id
)
SELECT *
FROM numbered_cte
WHERE rn = 1;
答案 1 :(得分:2)
如果要从连接条件中获取前三个值,请更改这样的select语句。
select *
from (select *
from master t left join detail d on d.ref_master_id=t.id)
where rownum<3;
您将获得所需的输出。使用*
时,请注意明确定义的列名称让我给出一个绝对的答案,你可以直接运行而不对代码进行任何更改。
select *
from (select t.id,t.name,d.id,d.ref_master_id,d.name
from master t left join detail d on d.ref_master_id=t.id)
where rownum<3;
答案 2 :(得分:1)
ROWNUM过滤器在连接中没有任何意义,但它不会被拒绝为无效。
解释计划将包括ROWNUM过滤器或将其排除。如果它包含它,它将在应用其他连接条件后将过滤器应用于详细信息表。因此,如果您输入ROWNUM = 100(永远不会满足),则排除所有细节行,然后外部联接开始。
如果你输入ROWNUM = 1,它似乎会丢弃过滤器。
如果你查询
with
a as (select rownum a_val from dual connect by level < 10),
b as (select rownum*2 b_val from dual connect by level < 10)
select * from a left join b on a_val < b_val and rownum in (1,3);
你得到一些非常奇怪的东西。
它可能应该被拒绝作为错误,所以期待无意义的事情发生