Rownum в состоянии соединения - PullRequest
4 голосов
/ 07 июля 2011

Недавно я исправил некоторую ошибку: в состоянии соединения был rownum.

Примерно так: левое соединение t1 на t1.id = t2.id и rownum <2.Так что предполагалось вернуть только одну строку независимо от «левого соединения». </p>

Когда я углубился в это, я понял, что не понимаю, как Oracle оценивает rownum в состоянии «левого соединения».Давайте создадим две таблицы sampe: master и detail.

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» и снова получил тот же результат!</p>

select * from master t
left join detail d on d.ref_master_id=t.id and rownum<-666.

Из-за результирующего набора мы можем сказать, что это условие было оценено как «Истина» для 3 строк в таблице сведений.Но если я использую «внутреннее соединение», все идет так, как должно быть.

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?

Ответы [ 3 ]

8 голосов
/ 07 июля 2011

ROWNUM - это псевдоатрибут наборов результатов, а не базовых таблиц. ROWNUM определяется после выбора строк, но до того, как они будут отсортированы с помощью предложения ORDER BY.

edit: Я ошибся в своей предыдущей записи ROWNUM, поэтому вот новая информация:

Вы можете использовать ROWNUM ограниченным образом в предложении WHERE, чтобы проверить, является ли оно меньше только положительным целым числом. Подробнее см. Псевдостолбец ROWNUM .

SELECT ... WHERE ROWNUM < 10

Непонятно, какое значение имеет ROWNUM в контексте предложения JOIN, поэтому результаты могут быть неопределенными. Кажется, что есть некоторая особая обработка выражений с ROWNUM, например WHERE ROWNUM > 10 всегда возвращает false. Я не знаю, как ROWNUM<-666 работает в вашем предложении JOIN, но это не имеет смысла, поэтому я бы не рекомендовал его использовать.

В любом случае это не поможет вам извлечь первую строку подробностей для каждой данной главной строки.

Чтобы решить эту проблему, вы можете использовать аналитические функции и PARTITION и объединить их с Общими выражениями таблиц , чтобы вы могли получить доступ к столбцу номера строки в дальнейшем 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;
2 голосов
/ 07 июля 2011

Если вы хотите получить первые три значения из условия соединения, измените оператор выбора следующим образом.

    select * 
    from (select * 
          from master t left join detail d on d.ref_master_id=t.id)
    where rownum<3;

Вы получите требуемый результат.Будьте осторожны с однозначно определенными именами столбцов при использовании *

Позвольте мне дать абсолютный ответ, который вы можете запустить напрямую, не внося никаких изменений в код.

1 голос
/ 07 июля 2011

Фильтр 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);

Вы получаете что-то совершенно странное.

Вероятно, это следует отклонить как ошибку, поэтому ожидайте, что произойдут бессмысленные вещи

...