Удаление строк из рекурсивного запроса

#sql #postgresql #recursive-query

#sql #postgresql #рекурсивный запрос

Вопрос:

Когда я запускаю этот запрос в своей базе данных, я получаю следующее:

 WITH RECURSIVE series AS (
    SELECT CONCAT( a.title) as str, a.prequelID
    FROM ( Prequels NATURAL JOIN Books) AS a
    UNION
    SELECT CONCAT(t.title, ' -> ', str) as str, t.prequelID
FROM (Books NATURAL JOIN Prequels) as t 
INNER JOIN series AS s ON s.prequelID = t.bookID
)
SELECT str as series FROM series
ORDER BY series;
 

Это результат :

введите описание изображения здесь

Я не хочу никаких дубликатов, только полную строку, которая показывает всю серию. Как мне это сделать?

Обновить:

Я обновил запрос, потому что понял, что мне не хватает первой книги из серии.

 WITH RECURSIVE series AS (
    SELECT CONCAT( a.title) as str, a.prequelID
    FROM ( Prequels NATURAL JOIN Books) AS a
    UNION
    SELECT CONCAT(t.title, ' -> ', str) as str, t.prequelID
FROM (Books NATURAL JOIN Prequels) as t 
INNER JOIN series AS s ON s.prequelID = t.bookID
)
SELECT CONCAT(a.title, ' -> ',a.str) as series
FROM (
SELECT Books.title, series.str
FROM series JOIN Books ON series.prequelID = Books.bookID
WHERE NOT EXISTS(
        SELECT prequelID
        FROM Prequels
        WHERE series.prequelID = Prequels.bookID
) 
) a
ORDER BY series;
 

Результат все еще немного не так, поскольку мне нужна только полная строка серии:
введите описание изображения здесь

Как мне это исправить?

Таблицы:

 CREATE TABLE Books
(bookID integer PRIMARY KEY,
title varchar(100),
pages integer);


CREATE TABLE Prequels
(bookID INTEGER REFERENCES Books(bookID),
prequelID INTEGER REFERENCES Books(bookID),
PRIMARY KEY (bookID,prequelID));
 

Примеры данных из серии Game of Thornes:

 INSERT INTO BOOKS (bookID,title,pages) VALUES (80429,'A Game of Thrones',292);
INSERT INTO BOOKS (bookID,title,pages) VALUES (41121,'A Clash of Kings',160);
INSERT INTO BOOKS (bookID,title,pages) VALUES (29287,'A Storm of Swords',160);
INSERT INTO BOOKS (bookID,title,pages) VALUES (17696,'A Feast for Crows',292);
INSERT INTO BOOKS (bookID,title,pages) VALUES (3947,'A Dance with Dragons',101);


INSERT INTO Prequels (bookID,prequelID) VALUES (41121,80429);
INSERT INTO Prequels (bookID,prequelID) VALUES (29287,41121);
INSERT INTO Prequels (bookID,prequelID) VALUES (17696,29287);
INSERT INTO Prequels (bookID,prequelID) VALUES (3947,17696);

 

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

1. вам необходимо предоставить образцы данных и желаемый результат . (возможно, предоставьте скрипку)

2. Поскольку вы объединяете prequelID и bookID и объединяете строки, вы можете найти длину строки, а затем найти MAX(length) группировку по соответствующему идентификатору

Ответ №1:

Вы хотели бы начать с корней деревьев.
В приквелидах нет корней.

 WITH RECURSIVE series AS (
    SELECT 
      b.bookId
    , 1 as lvl
    , p.prequelID
    , CONCAT(b.title) as series
    FROM Books AS b
    LEFT JOIN Prequels AS p 
      ON p.bookId = b.bookId
    WHERE NOT EXISTS (
      SELECT 1
      FROM Prequels p2
      WHERE p2.prequelID = b.bookId
    )
    
    UNION ALL
    
    SELECT 
      s.bookId
    , s.lvl 1
    , p.prequelID
    , CONCAT(b.title, ' -> ', s.series)
    FROM series AS s
    JOIN Books AS b 
      ON b.bookId = s.prequelID
    LEFT JOIN Prequels AS p 
      ON p.bookId = s.prequelID
)
SELECT series
FROM series
WHERE prequelId is null
  AND lvl > 1
ORDER BY series;
 
Серии
Игра престолов -> Битва королей -> Буря мечей -> Пир ворон -> Танец с драконами

Демонстрация в db<>fiddle здесь

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

1. Не будет включать книги, у которых нет приквелов (отдельные названия, не входящие в серию). Понятия не имею, требуется ли это для op или нет.

2. Кроме того, останавливает одну книгу слишком рано. В серии должно быть 5 книг, а не четыре. Рекурсивная часть должна быть series INNER books LEFT prequels (ошибка, унаследованная от OP).

3. Действительно. Допущена та же ошибка, что и в OP. Спасибо, что указали на это. Исправлено. Чтобы получить строки без приквелов, в конце просто должно быть ЛЕВОЕ СОЕДИНЕНИЕ.

4. @TomLefher Спасибо, но мне нужно было улучшить его после того, как Мат повозился. 2-й НЕ СУЩЕСТВУЕТ, и CONCAT в конце концов не потребовался. И теперь вы можете включать книги, которые есть только в таблице книг, удалив критерии для lvl.

Ответ №2:

Модифицированная версия LukStorms теперь удалила ответ…

https://dbfiddle.uk/?rdbms=postgres_14amp;fiddle=4e26beed430aefb60f1dc91766db13d3

 WITH RECURSIVE series AS (
   SELECT
      b.bookId,
      1 as lvl,
      p.prequelID,
      CONCAT(b.title)   AS series
    FROM
      books      b
    LEFT JOIN
      prequels   p
        ON p.bookid = b.bookid
    WHERE NOT EXISTS (
      SELECT 1
      FROM Prequels p
      WHERE p.prequelID = b.bookId
    )

    UNION ALL

    SELECT
      b.bookid,
      lvl 1,
      p.prequelID,
      CONCAT(b.title, ' -> ', s.series)
    FROM
      series     s
    INNER JOIN
      books      b
        ON b.bookid = s.prequelid
    LEFT JOIN
      prequels   p
        ON p.bookid = b.bookid
)
SELECT
  s.series
FROM
  series s
WHERE
  s.prequelid IS NULL
ORDER BY
  s.series;
 

Нерекурсивная часть CTE имеет ЛЕВОЕ СОЕДИНЕНИЕ, чтобы включать книги, которые не являются частью серии, просто отдельную книгу. Верните это к ЕСТЕСТВЕННОМУ или ВНУТРЕННЕМУ СОЕДИНЕНИЮ, если вы этого не хотите.

Нерекурсивная часть CTE содержит предложение WHERE, гарантирующее, что она начинается только с последней книги в серии.

Рекурсивная часть CTE имеет ЛЕВОЕ СОЕДИНЕНИЕ в таблице приквелов, так что первая книга в серии не теряется (из-за отсутствия строки приквела), и поэтому записывает приквел этой книги как NULL.

Затем внешний запрос ищет это значение NULL, поэтому выводятся только строки для завершенных рядов.

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

1. 1 за усилия. Кстати, нередко кто-то временно удаляет свой ответ, чтобы избежать отрицательных голосов, пока они пытаются исправить свое ошибочное решение.

Ответ №3:

После рекурсивного запроса вы можете сравнить все строки str вместе с помощью самосоединения и сохранить те s1.str , которые не являются подстрокой any s2.str благодаря WHERE s2.str IS NULL предложению.

 WITH RECURSIVE series AS (
    SELECT CONCAT( a.title) as str, a.prequelID
    FROM ( Prequels NATURAL JOIN Books) AS a
    UNION
    SELECT CONCAT(t.title, ' -> ', str) as str, t.prequelID
FROM (Books NATURAL JOIN Prequels) as t 
INNER JOIN series AS s ON s.prequelID = t.bookID
)
SELECT s1.str as series 
  FROM series AS s1
  LEFT JOIN series AS s2
    ON s2.str ~ s1.str
   AND s2.str <> s1.str
 WHERE s2.str IS NULL
ORDER BY series;
 

результат теста в dbfiddle

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

1. Когда я запускаю этот запрос, он выдает нулевые строки : ( @Edouard

2. Извините, в AND s2.str <> s1.str LEFT JOIN ... ON предложении не хватало. Ответ был обновлен и должен работать должным образом, см. Результат в dbfiddle .

3. Останавливает одну книгу раньше. Должен включать Game of Thrones , но не включает, из-за ошибки, скопированной из OP. Рекурсивная часть CTE должна присоединяться слева к приквелам.