Я создаю прототип в Google Cloud Spanner, и мне нужен способ выполнять рекурсивные запросы, как в PostgreSQL / SQLite, для обработки иерархических или графических данных. Я ищу синтаксис вроде https://sqlite.org/lang_with.html. Есть ли у Spanner способ выполнять подобные рекурсивные запросы или, может быть, хранимая процедура, такая как возможность выполнять там рекурсию? Я не могу найти синтаксис для общих табличных выражений.
Подробнее:
Ну, я моделирую ориентированные ациклические графы (DAG) в Spanner. Мне нужно будет поискать по графикам. Пример запроса: «найти все источники, подключенные к определенному узлу». Первая попытка моделирования графика в Spanner следующая:
CREATE TABLE nodes (
node_id INT64 NOT NULL,
node_label String(1024) NOT NULL,
name String(1024)
) PRIMARY KEY (node_id);
CREATE TABLE out_edges (
node_id INT64 NOT NULL,
to_node_id INT64 NOT NULL,
edge_label String(1024) NOT NULL,
) PRIMARY KEY (node_id, to_node_id),
INTERLEAVE IN PARENT nodes ON DELETE CASCADE;
CREATE TABLE in_edges (
node_id INT64 NOT NULL,
from_node_id INT64 NOT NULL,
edge_label String(1024) NOT NULL,
) PRIMARY KEY (node_id, from_node_id),
INTERLEAVE IN PARENT nodes ON DELETE CASCADE;
Использование чередующихся таблиц состоит в том, чтобы попытаться получить быстрый доступ к краям узла. Каждое уникальное ребро существует как в таблицах out_edges
, так и in_edges
.
Один из способов найти все источники, подключенные к определенному узлу в графе, - это выполнить DFS или BFS, начиная с этого узла и следуя по ребрам в обратном порядке, отслеживая узлы, у которых нет ребер, по мере их прохождения. . С этим представлением я могу думать о том, чтобы сделать это двумя способами: использовать рекурсивное общее табличное выражение (например, в SQLite или PostgreSQL или CONNECT BY в Oracle) или поддерживать транзитивное закрытие графа и соединять узлы без внутренних краев. с этими узлами в транзитивном замыкании, смежными с входным узлом. Последний будет работать в Spanner, но требует всего этого дополнительного хранилища и обслуживания переходного замыкания. И без какого-либо рекурсивного запроса я был бы ограничен поиском источников на фиксированном количестве ребер от входного узла. Я предполагаю, что другой способ сделать это в Spanner - это выполнить алгоритм поиска в клиентском коде. Проблема с этим может заключаться в потенциально большом количестве запросов к Spanner, чтобы удовлетворить исходный запрос.