Использование CTE для отображения транзитивных отношений в данных

#sql #postgresql #common-table-expression

#sql #postgresql #common-table-expression

Вопрос:

Я работаю над археологической базой данных, которая включает в себя пару таблиц, описывающих пространственные отношения между стратиграфическими единицами. Это довольно просто — единица находится либо выше, либо ниже другой единицы. Для этого у меня есть таблица, в которой записаны unit_1, unit_2 и тип пространственных отношений между ними (выше или ниже). Я также хочу сгенерировать представление, в котором также записывается транзитивный аналог. Другими словами, если блок A находится выше блока B, я также хочу временную строку, указывающую, что блок B находится ниже блока A.

Вот как выглядит мой CTE в настоящее время. Я получаю ошибку «ОШИБКА: отношение «matrix_cte» не существует», так что, вероятно, это не тот способ сделать это. Но идея здесь в том, что когда отношение «выше» (которое совпадает с 1), команда INSERT должна добавить новую строку в таблицу, созданную, где две единицы поменялись местами, а отношение находится ниже (или 2). Любая помощь с благодарностью, заранее спасибо.

 WITH matrix_cte  (unit, related_unit, relationship)
AS (SELECT lookup_unit,
lookup_unit_2,
lookup_unit_relationship

FROM register_unit_matrix)
INSERT INTO matrix_cte(unit, related_unit, relationship)
SELECT lookup_unit_2, lookup_unit, 2
FROM (register_unit_matrix

INNER JOIN matrix_cte ON ((register_unit_matrix.lookup_unit = matrix_cte.unit)))
WHERE relationship = 1;
  

Ответ №1:

Вы не можете INSERT перейти в CTE. CTE — это логическая таблица, это псевдоним результирующего набора. Вы можете SELECT из CTE. Не совсем уверен, чего вы пытаетесь там достичь.

ОШИБКА: отношение «matrix_cte» не существует

Эти сообщения об ошибках означают, что вы можете INSERT использовать только отношения (таблицы). CTE — это не таблица, это не постоянный объект в базе данных, и в вашей базе данных нет таблицы с именем matrix_cte .


Чтобы сгенерировать все связи, как прямые, так и обратные, вы можете UNION объединить два результирующих набора. Если ваша исходная таблица имеет связи только в одном направлении, тогда вы можете использовать UNION ALL , и запрос будет быстрее. Я имею в виду, если в исходной таблице никогда не бывает двух строк для одной и той же пары единиц:

 unit1, unit2, 1
unit2, unit1, 2
  

тогда вы можете использовать UNION ALL приведенное ниже. Если в исходной таблице могут быть такие дубликаты, вам следует использовать UNION для удаления дополнительных дубликатов.

 -- all direct relationships as they are
SELECT 
    lookup_unit,
    lookup_unit_2,
    lookup_unit_relationship
FROM register_unit_matrix

UNION

-- inverse all relationships 
SELECT 
    lookup_unit_2,
    lookup_unit,
    CASE WHEN lookup_unit_relationship = 1 THEN 2 ELSE 1 END AS lookup_unit_relationship
FROM register_unit_matrix
  

Вы можете поместить этот запрос выше в представление или использовать как есть.