文章目錄
- 一、with用法系列文章
- 二、前言
- 三、MySQL 普通CTE與遞歸CTE混合使用的嚴格規則
- 四、解決方案
- 4.1、方法1:嵌套查詢
- 4.2、方法2:使用臨時表
- 4.3、方法3:分開執行(應用層處理)
本文主要探討mysql
中with普通cte
與遞歸cte
如何混合使用。
一、with用法系列文章
關于with用法與with RECURSIVE的用法可以參考本人的另外兩篇博文。
- 《sql中with as用法/with-as 性能調優/with用法》
- 《MYSQL的(WITH RECURSIVE)遞歸查詢》
二、前言
在使用with RECURSIVE
遞歸查詢的過程中,發現有一段sql是公共的,因此想把這部分sql提取出去,當做臨時表。 with as
子查詢就可以當做臨時表,所以我就在想能不能先用with as
把公共部分查詢成臨時表,后面再跟著with RECURSIVE
遞歸查詢。即with as
與with RECURSIVE
混合使用。
經測試后發現先普通CTE再遞歸CTE時sql報錯 ,所以我想知道是否能混合使用,本文就是來討論這個問題。
sql示例如下:
-- 查詢任務2子節點
WITH RECURSIVE cte AS (SELECT r.id,r.project_code, r.code, r.name,r.parent_project_code,r.parent_codeFROM t_ds_process_dependent_relation rinner join t_ds_process_definition d on r.project_code = d.project_code and r.code = d.code and r.version = d.versionWHERE r.code = 18418446171042 -- 任務2UNION ALLSELECT t.id,t.project_code, t.code, t.name,t.parent_project_code,t.parent_codeFROM t_ds_process_dependent_relation tinner join t_ds_process_definition d on t.project_code = d.project_code and t.code = d.code and t.version = d.versionINNER JOIN cte c ON t.parent_project_code = c.project_code and t.parent_code = c.code
)
SELECT * FROM cte;
在我的設想里,我想把公共部分提取成普通CTE, 然后在遞歸CTE中引用,但是這種語法在mysql中是錯誤的
錯誤SQL如下:
with relation as ( -- 普通CTESELECT r.id,r.project_code, r.code, r.name,r.parent_project_code,r.parent_codeFROM t_ds_process_dependent_relation rinner join t_ds_process_definition d on r.project_code = d.project_code and r.code = d.code and r.version = d.version),RECURSIVE cte AS ( -- 遞歸CTESELECT id,project_code, code, name,parent_project_code,parent_codeFROM relationWHERE code = 18418446171042 -- 任務2UNION ALLSELECT t.id,t.project_code, t.code, t.name,t.parent_project_code,t.parent_codeFROM relation tINNER JOIN cte c ON t.parent_project_code = c.project_code and t.parent_code = c.code
)
SELECT * FROM cte;
三、MySQL 普通CTE與遞歸CTE混合使用的嚴格規則
在 MySQL 中,不可以 先定義普通 CTE 再定義遞歸 CTE。這是 MySQL 與某些其他數據庫(如 PostgreSQL)的一個重要語法差異。
MySQL 的嚴格規則
-
必須將
RECURSIVE
關鍵字緊跟在WITH
之后 -
第一個 CTE 必須是遞歸 CTE(如果使用了 RECURSIVE 關鍵字)
-
所有 CTE(包括普通 CTE)都必須放在同一個 WITH RECURSIVE 塊中
正確寫法示例:
WITH RECURSIVE-- 必須先定義遞歸CTErecursive_cte AS (-- 基礎部分SELECT ...UNION ALL-- 遞歸部分SELECT ... FROM recursive_cte ...),-- 然后才能定義普通CTEnormal_cte AS (SELECT ... FROM ...)-- 主查詢
SELECT ... FROM recursive_cte JOIN normal_cte ...
錯誤寫法示例:
-- 這樣寫會報錯!
WITHnormal_cte AS (SELECT ...), -- 先普通CTERECURSIVE -- 后RECURSIVErecursive_cte AS (SELECT ...)
SELECT ...
四、解決方案
如果確實需要先處理普通 CTE 再處理遞歸 CTE,可以考慮以下方法:
4.1、方法1:嵌套查詢
WITH RECURSIVE-- 將普通CTE的邏輯嵌入到遞歸CTE的基礎部分recursive_cte AS (-- 基礎部分包含普通CTE邏輯WITH normal_cte AS (SELECT ...)SELECT ... FROM normal_cte WHERE ...UNION ALL-- 遞歸部分SELECT ... FROM recursive_cte ...)
SELECT ... FROM recursive_cte;
4.2、方法2:使用臨時表
-- 先創建臨時表存儲普通CTE結果
CREATE TEMPORARY TABLE temp_normal AS
SELECT ... FROM ...;-- 然后使用遞歸CTE
WITH RECURSIVE recursive_cte AS (SELECT ... FROM temp_normal ...
)
SELECT ... FROM recursive_cte;-- 最后刪除臨時表
DROP TEMPORARY TABLE temp_normal;
4.3、方法3:分開執行(應用層處理)
-- 第一個查詢:執行普通CTE
SET @var = (SELECT ... FROM ...);-- 第二個查詢:執行遞歸CTE
WITH RECURSIVE recursive_cte AS (SELECT ... WHERE ... = @var
)
SELECT ... FROM recursive_cte;