Как оптимизировать JOIN в Oracle, если нас интересует только первая совпадающая дочерняя строка? - PullRequest
0 голосов
/ 04 сентября 2018

У меня есть родительская таблица, скажем, P, с Id, Name и Type.

P(
  id (pk),
  name,
  type (type in (1..10) )
)

И у меня есть дочерняя таблица, которая ссылается на P, скажем, таблица называется B:

B (
  id,
  date,
  other columns,
  parent_id
)

Теперь вот проблема: таблица P имеет не так много записей, но таблица B действительно огромна. В случае если Type = 3, дата в B всегда будет одинаковой для родительской записи.

Итак, я хотел бы получить все записи из P с type = 3 и соответствующую дату из таблицы B. Каков наилучший способ сделать это, учитывая, что B очень большой?

Я думал написать что-то вроде

Select b.parent_id, p.name, max(b.date)
from B b
join P p on p.id = b.parent_id
group by b.parent_id, p.name
where p.type = 3

Здесь max(b.date) или min(b.date) доза не имеет значения, потому что дата совпадает. Но таблица B очень большая. Не лучше ли выбрать только из P и присоединиться к B, например, «дочерняя строка с минимальным идентификатором» или, в основном, любая дочерняя строка?

Select p.id, p.name, b.date
from P p
join B b on (p.id = b.parent_id and "take only first matching row")
where p.type = 3

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

По запросу и при условии, что у вас есть индекс на b (parent_id). Если у вас есть данные:

create table p (
  id number(6) primary key not null,
  name varchar2(20),
  type number(2)
);

insert into p (id, name, type) values (10, 'John', 2);
insert into p (id, name, type) values (11, 'Peter', 3);
insert into p (id, name, type) values (12, 'Albert', 3);
insert into p (id, name, type) values (13, 'Mary', 4);
insert into p (id, name, type) values (14, 'Diego', 3);

create table b (
  id number(6),
  date1 date,
  parent_id number(6),
  constraint fk1_b foreign key (parent_id) references p (id)
);

create index ix1_b on b (parent_id);

insert into b (id, date1, parent_id) values (101, timestamp '2018-01-01 12:34:56', 10);
insert into b (id, date1, parent_id) values (102, timestamp '2018-01-02 12:34:57', 11);
insert into b (id, date1, parent_id) values (103, timestamp '2018-01-03 12:34:58', 11);
insert into b (id, date1, parent_id) values (104, timestamp '2018-01-04 12:34:59', 11);
insert into b (id, date1, parent_id) values (105, timestamp '2018-01-04 12:55:10', 12);
insert into b (id, date1, parent_id) values (106, timestamp '2018-01-04 12:55:11', 12);
insert into b (id, date1, parent_id) values (107, timestamp '2018-01-04 12:55:12', 12);

Запрос будет:

select p.*, bl.date1
  from p,
  lateral (
    select * from b where b.parent_id = p.id fetch next 1 rows only
  ) bl
  where p.type = 3;

Результат:

ID  NAME    TYPE  DATE1
--  ------  ----  ---------------------
11  Peter   3     2018-01-02 12:34:57.0
12  Albert  3     2018-01-04 12:55:10.0

Теперь, если в p есть строки, а в b нет строк (в моем примере это "Diego"), вам понадобится внешнее соединение:

select p.*, bl.date1
  from p
  outer apply (
    select * from b where b.parent_id = p.id fetch next 1 rows only
  ) bl
  where p.type = 3;

Результат:

ID  NAME    TYPE  DATE1
--  ------  ----  ---------------------
11  Peter   3     2018-01-02 12:34:57.0
12  Albert  3     2018-01-04 12:55:10.0
14  Diego   3     <null>

Ура!

0 голосов
/ 04 сентября 2018

попробуй:

Select p.id, p.name, (select b.date from B where p.id = b.parent_id and rownum = 1) date
from P p
where p.type = 3
...