SQL: Сохранение root_id в трехуровневом дереве? - PullRequest
1 голос
/ 10 ноября 2011

У меня есть простое трехуровневое дерево, в настоящее время сохраненное в виде списка смежности:

CREATE TABLE categories (
  id int,
  name text,
  parent_id int);

Это только для чтения, и мне часто нужно знать корневую категорию любой данной категории. Поэтому я хочу добавить столбец root_id и сохранить его, чтобы избежать беспорядочных предложений WHERE, CTE и т. Д.

Моя первая попытка была:

update categories set root_id = parent.id
from categories as child
join categories as parent
on child.parent_id = parent.id
and parent.parent_id is null;

Но UPDATE не может работать с соединениями , создающими несколько строк . (Этот запрос был только для категорий уровня 2; я бы сделал аналогичный запрос для третьего уровня.)

Я знаю, что ответ включает превращение объединения в подзапрос, но я не могу сразу думать о самостоятельных соединениях и подзапросах. Мы находимся на PostgreSQL 9.0, поэтому у меня нет записываемых CTE (хотя мне любопытно, как этот будет выглядеть).

Какой правильный способ сделать это?

1 Ответ

1 голос
/ 10 ноября 2011

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

Небольшая демонстрация преследования дедушки.(здесь соглашение состоит в том, что любой корень имеет parent_id = NULL; другое соглашение может быть parent_id = id; YMMV) * ​​1003 *

set search_path='tmp';

DROP TABLE categories;
CREATE TABLE categories
  ( id INTEGER NOT NULL PRIMARY KEY
  , parent_id INTEGER references categories(id)
  , zname text
  );

-- Generate some data
INSERT INTO categories (id) SELECT * FROM generate_series (1,33);
UPDATE categories SET parent_id = 1 + id/4 WHERE id > 1;
-- uncomment the next line (and comment the previous) if you want a forest.
-- UPDATE categories SET parent_id = 1 + id/4 WHERE id > 3;
UPDATE categories SET zname = 'Zzz'::text || id::text;


-- SELECT * FROM categories;

WITH RECURSIVE tr AS (
    SELECT ca0.id as root
    , ca0.parent_id AS parent_id
    , ca0.id AS id
    , ca0.zname AS zname
    FROM categories ca0
    WHERE ca0.parent_id IS NULL
    UNION
    SELECT tr.root as root
    , ca1.parent_id AS parent_id
    , ca1.id AS id
    , ca1.zname AS zname
    FROM categories ca1, tr
    WHERE ca1.parent_id = tr.id
    )
SELECT * FROM tr;

Результат:

 root | parent_id | id | zname
------+-----------+----+-------
    1 |           |  1 | Zzz1
    1 |         1 |  2 | Zzz2
    1 |         1 |  3 | Zzz3
    1 |         2 |  4 | Zzz4
    1 |         2 |  5 | Zzz5
    1 |         2 |  6 | Zzz6
    1 |         2 |  7 | Zzz7
    1 |         3 |  8 | Zzz8
    1 |         3 |  9 | Zzz9
    1 |         3 | 10 | Zzz10
    1 |         3 | 11 | Zzz11
    1 |         4 | 12 | Zzz12
    1 |         4 | 13 | Zzz13
    1 |         4 | 14 | Zzz14
    1 |         4 | 15 | Zzz15
    1 |         5 | 16 | Zzz16
    1 |         5 | 17 | Zzz17
    1 |         5 | 18 | Zzz18
    1 |         5 | 19 | Zzz19
    1 |         6 | 20 | Zzz20
    1 |         6 | 21 | Zzz21
    1 |         6 | 22 | Zzz22
    1 |         6 | 23 | Zzz23
    1 |         7 | 24 | Zzz24
    1 |         7 | 25 | Zzz25
    1 |         7 | 26 | Zzz26
    1 |         7 | 27 | Zzz27
    1 |         8 | 28 | Zzz28
    1 |         8 | 29 | Zzz29
    1 |         8 | 30 | Zzz30
    1 |         8 | 31 | Zzz31
    1 |         9 | 32 | Zzz32
    1 |         9 | 33 | Zzz33
(33 rows)
...