Oracle SQL: объединяет не более одной связанной сущности - PullRequest
1 голос
/ 25 февраля 2010

У меня есть таблицы Building и Address, где каждый Building связан с 0..n Addresses.

Я хотел бы перечислить Buildings с ассоциированным Address. Если у Building есть несколько входов и, следовательно, несколько Addresses, мне все равно, какой из них отображается. Если у Building нет известных адресов, поля адреса должны быть null.

Это - что-то вроде левого соединения, которое объединяет каждую строку не более одного раза.

Как я могу выразить это в Oracle SQL?

PS: Мой запрос будет включать довольно сложные ограничения для обеих таблиц. Поэтому я бы хотел избежать повторения этих ограничений в тексте запроса.

Ответы [ 5 ]

4 голосов
/ 25 февраля 2010

Я хотел бы рассмотреть вопрос об адресе в предложении SELECT, например ::10000

SELECT b.*
      ,(SELECT a.text
        FROM   addresses a
        WHERE  a.buildingid = b.id
        AND    ROWNUM=1) as atext
FROM   building b;

ROWNUM=1 означает «просто получите один, если есть, все равно, какой».

Преимущество этого подхода состоит в том, что он, вероятно, будет работать лучше, чем большинство альтернатив, при условии наличия подходящего индекса по адресам .buildingid. Он перестанет искать другие адреса, как только найдет один для каждого запрашиваемого здания.

Недостатком этого подхода является то, что если вам нужно несколько столбцов из таблицы адресов, вы не можете - хотя вы можете объединить их вместе в одну строку.

2 голосов
/ 25 февраля 2010

Поскольку вам все равно, какой из многих адресов отображается:

Oracle 9i +:

WITH summary AS (
      SELECT b.*,
             a.*,
             ROW_NUMBER() OVER (PARTITION BY b.building_id) rn
        FROM BUILDINGS b
   LEFT JOIN ADDRESSES a ON a.building_id = b.building_id)
SELECT s.*
  FROM summary s
 WHERE s.rn = 1

Эквивалент факторинга без подзапроса:

SELECT s.*
  FROM (SELECT b.*,
               a.*,
               ROW_NUMBER() OVER (PARTITION BY b.building_id) rn
           FROM BUILDINGS b
      LEFT JOIN ADDRESSES a ON a.building_id = b.building_id) s
 WHERE s.rn = 1
0 голосов
/ 25 февраля 2010

Meriton,

Этот подход использует вложенные встроенные представления. Я доказал этот подход на больших наборах данных, он работает очень хорошо.

Лучший способ понять запрос - начать с самого внутреннего "М" встроенного представления. Я добавил счет ради отладки и ясности. Это идентифицирует максимальный (т.е. самый последний ???) идентификатор адреса для каждого здания:

   select maxa.b_id, max(maxa.a_id) a_id, count(*) c
   from address maxa
   group by maxa.b_id;

Следующее встроенное представление «A» использует вышеуказанное встроенное представление «M», чтобы решить, какой адрес получить, затем присоединяется к этому идентификатору адреса для возврата набора полей адреса:

  select ma.b_id, ma.a_id, ma.addr1, ma.addr2, ma.addr3, m.c
  from address ma, 
     ( select maxa.b_id, max(maxa.a_id) a_id, count(*) c
       from address maxa
       group by maxa.b_id ) m
  where ma.a_id = m.a_id;

Приведенное выше встроенное представление «A» доставляет преобразованный набор адресов к окончательному запросу. Принимая во внимание, что отношение между BUILDING и ADDRESS составляет от 1 до 0..n, отношение между BUILDING и "A" составляет от 1 до 0..1, основное внешнее соединение:

select b.b_id, b.b_code, b.b_name, a.*
 from building b, 
    ( select ma.b_id, ma.a_id, ma.addr1, ma.addr2, ma.addr3, m.c
      from address ma, 
         ( select maxa.b_id, max(maxa.a_id) a_id, count(*) c
           from address maxa
           group by maxa.b_id ) m
      where ma.a_id = m.a_id ) a
 where b.b_id = a.b_id (+);

Ключевые преимущества этого подхода:

  1. Доставляет любое количество адресных столбцов.
  2. Детерминистический, возвращает одинаковые результаты при каждом запуске.
  3. Не накладывает чрезмерных сложностей на ваш последний запрос, который, безусловно, будет более сложным, чем этот.
  4. Встроенное представление "A" может быть легко инкапсулировано в представление базы данных, возможно, назовите его представлением LATEST_ADDRESS:
create view latest_address (b_id, a_id, addr1, addr2, addr3, c) as
select ma.b_id, ma.a_id, ma.addr1, ma.addr2, ma.addr3, m.c
 from address ma, 
    ( select maxa.b_id, max(maxa.a_id) a_id, count(*) c
      from address maxa
      group by maxa.b_id ) m
 where ma.a_id = m.a_id;

select b.b_id, b.b_code, b.b_name, a.*
 from building b, latest_address a
 where b.b_id = a.b_id (+);

Наслаждайтесь!
Matthew

0 голосов
/ 25 февраля 2010
select b.*, max(a.id) as aid 
from building b 
left outer join addresses a on (a.buildingid = b.id) 
group by a.buildingid 

или

select b.*, maxid
from building b 
left outer join 
(
 select buildingid, max(id) as maxid
 from addresses
 group by buildingid 
) a on (a.buildingid = b.id) 
0 голосов
/ 25 февраля 2010

то, что вы можете сделать, это ограничение на адреса, к которым вы присоединяетесь. Например, требуя, чтобы не было адреса с более низким идентификатором:

select *
from building b
left join addresses a on (a.buildingid = b.id)
where not exists (select 1 from addresses a2
                  where a2.buildingid = b.id and a2.id < a.id)

в этом случае вы получите максимум 1 адрес на здание.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...