С
Общие выражения с таблицей были введены в MariaDB 10.2.1.
Синтаксис
WITH [RECURSIVE] table_reference [(columns_list)] AS ( SELECT ... ) [CYCLE cycle_column_list RESTRICT] SELECT ...
Описание
Ключевое слово WITH обозначает общее выражение с таблицей (CTE). Оно позволяет вам многократно ссылаться на выражение подзапроса в запросе, как если бы у вас была временная таблица, которая существует только на время запроса.
Существует два типа CTE:
- Нерекурсивные
-
Рекурсивные (обозначается ключевым словом
RECURSIVE, поддерживается начиная с MariaDB 10.2.2)
Вы можете использовать table_reference как обычную таблицу во внешней части SELECT. Вы также можете использовать WITH в подзапросах, а также с EXPLAIN и SELECT.
Неправильно составленные рекурсивные CTE могут теоретически привести к бесконечным циклам. Системная переменная max_recursive_iterations ограничивает количество рекурсий.
CYCLE ... RESTRICT
Оператор CYCLE позволяет обнаруживать циклы в CTE, избегая чрезмерных или бесконечных циклов. MariaDB поддерживает более гибкий, нестандартный синтаксис.
Стандарт SQL допускает использование оператора CYCLE следующим образом:
WITH RECURSIVE ... ( ... ) CYCLE <cycle column list> SET <cycle mark column> TO <cycle mark value> DEFAULT <non-cycle mark value> USING <path column>
где все фразы являются обязательными.
MariaDB не поддерживает это, но начиная с 10.5.2 поддерживает более гибкий нестандартный синтаксис, как показано ниже:
WITH RECURSIVE ... ( ... ) CYCLE <cycle column list> RESTRICT
Использование CYCLE ... RESTRICT не влияет на то, использует ли CTE UNION ALL или UNION DISTINCT. UNION ALL означает "все строки, но без циклов", что и позволяет оператор CYCLE. А UNION DISTINCT означает, что все строки должны быть различными, что, опять же, произойдет - поскольку уникальность обеспечивается по подмножеству столбцов, полные строки автоматически будут различными.
Примеры
Ниже приведен пример с WITH на верхнем уровне:
WITH t AS (SELECT a FROM t1 WHERE b >= 'c') SELECT * FROM t2, t WHERE t2.c = t.a;
В примере ниже используется WITH в подзапросе:
SELECT t1.a, t1.b FROM t1, t2
WHERE t1.a > t2.c
AND t2.c IN(WITH t AS (SELECT * FROM t1 WHERE t1.a < 5)
SELECT t2.c FROM t2, t WHERE t2.c = t.a);
Ниже приведен пример рекурсивного CTE:
WITH RECURSIVE ancestors AS ( SELECT * FROM folks WHERE name="Alex" UNION SELECT f.* FROM folks AS f, ancestors AS a WHERE f.id = a.father OR f.id = a.mother ) SELECT * FROM ancestors;
Рассмотрим следующую структуру и данные:
CREATE TABLE t1 (from_ int, to_ int); INSERT INTO t1 VALUES (1,2), (1,100), (2,3), (3,4), (4,1); SELECT * FROM t1; +-------+------+ | from_ | to_ | +-------+------+ | 1 | 2 | | 1 | 100 | | 2 | 3 | | 3 | 4 | | 4 | 1 | +-------+------+
В соответствии с вышеизложенным, следующий запрос теоретически приведет к бесконечному циклу из-за последней записи в t1 (обратите внимание, что max_recursive_iterations установлен в 10 для целей этого примера, чтобы избежать чрезмерного количества циклов):
SET max_recursive_iterations=10;
WITH RECURSIVE cte (depth, from_, to_) AS (
SELECT 0,1,1 UNION DISTINCT SELECT depth+1, t1.from_, t1.to_
FROM t1, cte WHERE t1.from_ = cte.to_
)
SELECT * FROM cte;
+-------+-------+------+
| depth | from_ | to_ |
+-------+-------+------+
| 0 | 1 | 1 |
| 1 | 1 | 2 |
| 1 | 1 | 100 |
| 2 | 2 | 3 |
| 3 | 3 | 4 |
| 4 | 4 | 1 |
| 5 | 1 | 2 |
| 5 | 1 | 100 |
| 6 | 2 | 3 |
| 7 | 3 | 4 |
| 8 | 4 | 1 |
| 9 | 1 | 2 |
| 9 | 1 | 100 |
| 10 | 2 | 3 |
+-------+-------+------+
Однако, оператор CYCLE ... RESTRICT (начиная с MariaDB 10.5.2) может решить эту проблему:
WITH RECURSIVE cte (depth, from_, to_) AS (
SELECT 0,1,1 UNION SELECT depth+1, t1.from_, t1.to_
FROM t1, cte WHERE t1.from_ = cte.to_
)
CYCLE from_, to_ RESTRICT
SELECT * FROM cte;
+-------+-------+------+
| depth | from_ | to_ |
+-------+-------+------+
| 0 | 1 | 1 |
| 1 | 1 | 2 |
| 1 | 1 | 100 |
| 2 | 2 | 3 |
| 3 | 3 | 4 |
| 4 | 4 | 1 |
+-------+-------+------+
См. также
© 2023 MariaDB
Licensed under the Creative Commons Attribution 3.0 Unported License and the GNU Free Documentation License.
https://mariadb.com/kb/en/with/