SQL: соединение (внутреннее соединение) с соответствующим предком из другой таблицы - PullRequest
1 голос
/ 06 апреля 2020

Так что я совершенно тупик, пытаясь выяснить этот запрос SQL.

У меня есть таблица категорий товаров, которые существуют в древовидной структуре. Для упрощения скажем, есть 3 категории высшего уровня: A, B, C. Над ними есть еще одна категория («Все») - root. Никакие продукты не могут быть отнесены к этой категории. Чтобы различать категории guish, которые нельзя назначить продуктам, они имеют тип «Абстрактный», а не «Конкретный»

Каждая категория может иметь любое количество и глубину подкатегорий. , В настоящее время я храню их с идентификатором родителя для непосредственного родителя (список смежности).

Categories

Category   Parent    Type
All        None      Abstract
A          All       Concrete
B          All       Concrete
C          All       Concrete
D          A         Concrete
E          D         Concrete
F          B         Concrete
G          F         Concrete
H          C         Concrete
I          C         Concrete

У меня есть еще одна таблица продуктов с полем категории. В этой таблице отображаются только категории верхнего уровня. ie. Либо A, B, либо C.

Products

Part Number       Category
XXXX-XXXX         A
XXXX-YYYY         A
XXXX-ZZZZ         B
YYYY-XXXX         C

Я хотел бы создать запрос, объединяющий две таблицы, чтобы создать строки, в которых категория заменяется дочерней категорией. ie. С точки зрения псевдокода в основном присоединение к категории = при условии, что категория либо равна, либо является потомком категории.

Так что-то вроде:

select * from products
inner join categories
on products.category = descendent of category

приведет к:

Part Number       Category
XXXX-XXXX         E (E's top level concrete parent is A)
XXXX-YYYY         E (E's top level concrete parent is A)
YYYY-XXXX         H (H's top level concrete parent is C)
YYYY-XXXX         I (I's top level concrete parent is C)

У меня есть это, которое извлекает все конкретные типы до верхнего уровня:

with recursive
concrete_parents as (
  select category, parent, type
  from categories
  where category in ('E', 'H', 'I')
  UNION ALL
    select t2.category, t2.parent, t2.type
    from categories as t2
    inner join concrete_parents t1
    on t1.parent = t2.category
    where t2.type = 'Concrete'
)

select distinct * from concrete_parents
order by parent;

Я не могу понять, как совместить это с внутренним соединением на главном столе?

Другая альтернатива, которую я рассматриваю, - это использование Postgres дерева, но я не очень знаком с ним.

Есть мысли?

Ответы [ 2 ]

1 голос
/ 07 апреля 2020

... было бы здорово динамически фиксировать конкретные категории верхнего уровня.

Это кажется выполнимым, поскольку вы заявили:

Единственные категории, которые в этой таблице (Products) указаны верхние уровни ie. A, B или C.

Таким образом, эти категории верхнего уровня автоматически фильтруются в финальном JOIN. И так как эти (и только те) имеют parent = 'All' в соответствии с вашими примерами данных, мы можем сократить один уровень рекурсии и сделать его немного быстрее, но:

WITH RECURSIVE parent_cat AS (
   SELECT category AS original, category, parent -- no need for type
   FROM   categories      c
   WHERE  category in ('A', 'D', 'H', 'I')

   UNION ALL
   SELECT pc.original, c.category, c.parent
   FROM   parent_cat pc
   JOIN   categories c ON c.category = pc.parent
   WHERE  pc.parent <> 'All'  -- stop at top level, save 1 recursion
   )
SELECT p.part_number, pc.category, pc.original 
FROM   parent_cat pc
JOIN   products   p USING (category)
WHERE  pc.parent = 'All'      -- redundant, but a bit faster
ORDER  BY pc.original;

Кроме того, нет необходимости фильтровать с type = 'Concrete', поскольку другие типы фильтруются объединением, а также pc.parent = 'All'.

db <> fiddle здесь

Кстати, если производительность критична и категории не слишком сильно меняются, рассмотрите вариант MATERIALIZED VIEW, заменяющий rCTE parent_cat в запросе, - и установите соответствующий режим, чтобы поддерживать его актуальность.

0 голосов
/ 07 апреля 2020

Так что я считаю, что это работает:

WITH RECURSIVE parent_categories AS (
SELECT category, parent, type, category AS original
FROM categories
WHERE category in ('E', 'H', 'I')

UNION ALL

SELECT cat.category, cat.parent, cat.type, pc.original
FROM categories cat, parent_categories pc
WHERE cat.category = pc.parent
)
SELECT b.part_number, a.category, a.original 
FROM parent_categories a
INNER JOIN products b
ON a.category = b.category
WHERE a.type = 'Concrete' AND a.category IN ('A', 'B', 'C')

Fiddle

Мне не нравится это, так как было бы здорово динамически фиксировать конкретные категории верхнего уровня , Хотя в этой системе они очень стабильны. Если я заменю («E», «H», «I») на («D», «H», «I»), я получу:

part_number category    original
XXXX-XXXX   A           D
XXXX-YYYY   A           D
YYYY-XXXX   C           H
YYYY-XXXX   C           I

Или («A», «D» ',' H ',' I ') Я получаю:

part_number category    original
XXXX-XXXX   A           A
XXXX-YYYY   A           A
XXXX-XXXX   A           D
XXXX-YYYY   A           D
YYYY-XXXX   C           H
YYYY-XXXX   C           I 

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

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

...