Рекурсивный запрос SQLite для возврата идентификатора заданного пути - PullRequest
0 голосов
/ 18 июня 2020

Я храню иерархию папок в базе данных Sqlite следующим образом:

+----------+---------+----------+
| ParentID | ChildID |   Name   |
+----------+---------+----------+
|        1 |       2 | Folder A |
|        2 |       3 | Folder B |
|        3 |       4 | Folder C |
+----------+---------+----------+

Где Folder A - это root, а структура: Folder A/Folder B/Folder C.

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

Однако в настоящее время у меня НЕТ способа получить идентификатор папки с учетом ее пути . Это определенно немного сложнее, потому что мне нужно разбить строку на /, если я хочу иметь возможность запрашивать таблицу.

Пока что у меня есть начало рекурсивного CTE:

SELECT Path, ChildID FROM DirectoryStructure, (
    SELECT SUBSTR('Folder A/Folder B/Folder C', 0, Position) as Root, 
           SUBSTR('Folder A/Folder B/Folder C', Position+1) as Path 
    FROM (SELECT INSTR('Folder A/Folder B/Folder C', '/') as Position)
) WHERE Name = Root

Это вернет:

+-------------------+---------+
|       Path        | ChildID |
+-------------------+---------+
| Folder B/Folder C |       2 |
+-------------------+---------+

Это идеально, потому что у меня есть ChildID Folder A, а также есть следующий кусок пути для обработки. Теперь нужно просто рекурсивно выполнить следующий шаг, где я выдергиваю Folder B (как я сделал Folder A) и нахожу запись в DirectoryStructure, где ParentID = 2 AND Name = 'Folder B', чтобы получить ChildID и так далее.

Однако здесь у меня проблемы. Я могу представить, что мне нужно что-то вроде этого:

WITH RECURSIVE GetId(Path, LastChild) AS (
    SELECT Path, ChildID FROM DirectoryStructure, (
        SELECT SUBSTR('Folder A/Folder B/Folder C', 0, Position) as Root, 
               SUBSTR('Folder A/Folder B/Folder C', Position+1) as Path 
        FROM (SELECT INSTR('Folder A/Folder B/Folder C', '/') as Position)
    ) WHERE Name = Root
    UNION ALL
    SELECT Path, ChildID FROM DirectoryStructure, (
        SELECT SUBSTR(GetId.Path, 0, Position) as Root, 
               SUBSTR(GetId.Path, Position+1) as Path
        FROM GetId, (SELECT INSTR(GetId.Path, '/') as Position FROM GetId)
    ) WHERE (ParentID = GetId.LastChild, Name = Root)
) SELECT * from GetId;

Играя с этим, я обычно получаю сообщение об ошибке: Result: recursive reference in a subquery: GetId

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

Любая помощь будет принята с благодарностью!

1 Ответ

1 голос
/ 18 июня 2020

Альтернативный способ приблизиться к этому - найти путь к каждому дочернему элементу, а затем просто сравнить его с путем поиска:

WITH RECURSIVE CTE AS (
  SELECT ChildId, Name AS Path
  FROM DirectoryStructure
  WHERE ParentID = 1
  UNION ALL
  SELECT d.ChildId, Path || '/' || Name
  FROM DirectoryStructure d
  JOIN CTE ON d.ParentId = CTE.ChildId
)
SELECT *
FROM CTE
WHERE Path = 'Folder A/Folder B/Folder C'

Вывод:

ChildId     Path
4           Folder A/Folder B/Folder C

Демо on dbfiddle

Если ваша таблица большая, а приведенный выше запрос слишком медленный, вы можете обрезать записи, как вы go, чтобы убедиться, что они соответствуют желаемому пути до этого уровня:

WITH RECURSIVE CTE AS (
  SELECT ChildId, Name AS Path
  FROM DirectoryStructure
  WHERE ParentID = 1
    AND 'Folder A/Folder B/Folder C' LIKE Name || '%'
  UNION ALL
  SELECT d.ChildId, Path || '/' || Name
  FROM DirectoryStructure d
  JOIN CTE ON d.ParentId = CTE.ChildId
  WHERE 'Folder A/Folder B/Folder C' LIKE Path || '/' || Name || '%'
)
SELECT *
FROM CTE
WHERE Path = 'Folder A/Folder B/Folder C'

Демонстрация на dbfiddle

...