Демо-данные:
CREATE TABLE category
(
"id" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY,
"name" TEXT NOT NULL,
"userId" INTEGER NULL,
"parentCategoryId" INTEGER NULL,
CONSTRAINT category_pkey PRIMARY KEY (id),
CONSTRAINT cateory_fkey_parent_id FOREIGN KEY ("parentCategoryId") REFERENCES category (id)
);
INSERT INTO category(name, "userId", "parentCategoryId")
VALUES ('ToDo', NULL, NULL);
INSERT INTO category(name, "userId", "parentCategoryId")
VALUES ('Notes', NULL, NULL);
INSERT INTO category(name, "userId", "parentCategoryId")
VALUES ('ToDo', 1, NULL);
INSERT INTO category(name, "userId", "parentCategoryId")
VALUES ('Notes', 1, NULL);
INSERT INTO category(name, "userId", "parentCategoryId")
VALUES ('my_todos', NULL, 3);
INSERT INTO category(name, "userId", "parentCategoryId")
VALUES ('ToDo', 2, NULL);
INSERT INTO category(name, "userId", "parentCategoryId")
VALUES ('Notes', 2, NULL);
Вы можете использовать рекурсивный запрос, чтобы найти родительское дерево вашей категории, затем вы можете проверить, владеет ли пользователь каким-либо из родительских узлов:
WITH RECURSIVE cte AS (
SELECT "id", "userId", "parentCategoryId"
FROM category
WHERE id = 5 --the category of interest
UNION
SELECT d."id", d."userId", d."parentCategoryId"
FROM category d
INNER JOIN cte ON d."id" = cte."parentCategoryId"
)
SELECT TRUE
FROM cte
WHERE "userId" = 1 --the user of interest
LIMIT 1;
Обратите внимание, что все имена столбцов заключены в кавычки - это означает, что они чувствительны к регистру.
Как работает запрос:
Рекурсивный запрос содержит части дерева:
База запроса (нерекурсивная), которая извлекает некоторые rows
Предложение UNION, которое объединяет результаты рекурсивной и нерекурсивной частей
Рекурсивная часть, которая выполняется для каждой строки возвращается как из рекурсивной, так и из нерекурсивной частей.
В ваших данных примера база запроса возвращает строку (id=5, user=null, parentCategoryId=3)
. Эта строка передается в качестве входных данных для рекурсивного запроса, который объединяет ее с таблицей category
. Другой запрос возвращает новую строку (id=3, userId=1, parentCategoryId=null)
. Эта строка снова передается в качестве аргумента рекурсивному запросу, но как parentCategoryId=null
, на этот раз запрос не возвращает никаких строк, и в результате выполнение прекращается. Предложение UNION
объединяет две строки в один набор результатов. Теперь внешний select
проверяет, содержит ли какая-либо из возвращенных строк userId=1
, и в этом случае возвращает TRUE.