Выбор внешнего ключа с самоссылкой

#mysql #sql

#mysql #sql

Вопрос:

У меня есть структура таблицы:

Регион:

  • идентификатор (ключ)
  • parent_region (внешний ключ — ссылается на другой регион)
  • Имя

Предположим, у меня есть следующие 3 строки:

 [ id, parent_region, name ]
(0, NULL, "USA")
(1, 0, "Georgia")
(2, 1, "Atlanta")
  

Я хочу иметь возможность выбирать Атланту, Джорджия, США, запрашивая идентификатор:

 SELECT ... 
  FROM region 
 WHERE id = 2
  

… и возвращает 3 строки. Я использую Java / JDBC, поэтому, очевидно, я мог бы программно выбрать 3 строки одну за другой… но мне любопытно, есть ли умный SQL-способ сделать это.

Я использую MySQL, но мне также было бы любопытно узнать о решении Oracle или Informix.

Комментарии:

1. Смотрите: dev.mysql.com/tech-resources/articles/hierarchical-data.html

2. Разве это не должно быть WHERE id = 2 ?

3. ИМХО, таблицы закрытия были бы намного лучшим решением, вместо того, чтобы пытаться втиснуть все это в одну таблицу.

4. У вас есть бесконечное количество уровней или максимум 3?

5. @BitOff — бесконечное количество уровней

Ответ №1:

В MySQL нет рекурсивных выборок, что слишком плохо для этой проблемы.

WITH RECURSIVE Предложение было придумано для этого. Это работает в PostgreSQL и что-то очень близкое или идентичное в Oracle.

 WITH RECURSIVE T(id, parent_region, name) AS
 SELECT id, parent_region, name FROM region WHERE id=2
 UNION
 SELECT id, parent_region, name FROM region JOIN T
   ON (region.parent_region = T.id) IS TRUE -- IS TRUE forces T.id NOT NULL
SELECT id, parent_region, name FROM T;
  

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

Комментарии:

1. Просто для полноты картины: это также будет работать в Firebird, DB2 и SQL Server. Но, к сожалению, это не поможет OP

2. В конечном итоге я выбрал таблицу закрытия, но я отмечу это как допустимый ответ / альтернативу.

Ответ №2:

Этот запрос:

 select a.name, b.name, c.name 
from region a, region b, region c  
where a.parent_region = b.id and b.parent_region = c.id and a.id = 2;
  

Выдал эти результаты

  --------- --------- ------ 
| name    | name    | name |
 --------- --------- ------ 
| Atlanta | Georgia | USA  |
 --------- --------- ------ 
1 row in set (0.00 sec)