没有 CTE 的分层查询
Hierarchical query without CTE
由于 VistaDB 上缺少 CTEs/recursive 查询,我试图制定一个具有一定深度的可行查询来查询 PARENT/ID 分层自引用 table。我有几个想法(SQL 来自 Firebird,因为我们在服务器端使用它):
做几个连接,像这样:
SELECT
"A"."ID",
"B"."ID",
"C"."ID",
"D"."ID"
FROM "NAVIGATION" AS A
LEFT JOIN "NAVIGATION" AS B ON (B.PARENT = A.ID)
LEFT JOIN "NAVIGATION" AS C ON (C.PARENT = B.ID)
LEFT JOIN "NAVIGATION" AS D ON (D.PARENT = C.ID)
WHERE "A"."ID" = CHAR_TO_UUID('00000000-0000-0000-0000-000000000000');
然后对 A、B、C、D "ID" 列进行 COALESCE,并将其用作实际行的子查询或连接源以获取所需的内容。但是,由于第一层的第一行可能会连接到其他几行,所以这不起作用 - 我需要的是:
A B C D
0 NULL NULL NULL
0 1 NULL NULL
0 1 2 NULL
0 1 2 3
0 1 2 4
0 1 2 5
相反 - 正如预期的那样 - 我得到这个:
A B C D
0 1 2 3
0 1 2 4
0 1 2 5
有什么方法可以获取额外的 NULL
行?
对子查询使用 UNION
。但是,我想不出一个可行的语法来完成这项工作。
也许是另一种语法。我们只需要几个层次的深度。从技术上讲,我们可以在应用程序中评估 (1.) 的结果,但我更喜欢一种更优雅的方法,尽管它不必非常快。我们通常只会在客户端深入查询两三层,有时只会一层。尽管如此,最好不要按程序进行。
请求的一些示例数据:
ID PARENT TITLE
0 NULL 'Root Node'
1 0 '1st Level Node'
2 1 '2nd Level Node'
3 2 '3nd Level Node 1'
4 2 '3nd Level Node 2'
5 2 '3nd Level Node 3'
如果您有示例数据,将会有所帮助。但是,如果其他表中有匹配的行,则无法查询 return A
/NULL
/NULL
/NULL
。
获取所有层次结构的一种方法是为每个联接添加一个 NULL
值:
SELECT "A"."ID", "B"."ID", "C"."ID", "D"."ID"
FROM "NAVIGATION" AS A LEFT JOIN
(SELECT N.PARENT, N.ID
FROM "NAVIGATION"
UNION ALL
SELECT NULL, NULL
) B
ON B.PARENT = A.ID
(SELECT N.PARENT, N.ID
FROM "NAVIGATION"
UNION ALL
SELECT NULL, NULL
) C
ON C.PARENT = B.ID LEFT JOIN
(SELECT N.PARENT, N.ID
FROM "NAVIGATION"
UNION ALL
SELECT NULL, NULL
) D
ON D.PARENT = C.ID
WHERE "A"."ID" = CHAR_TO_UUID('00000000-0000-0000-0000-000000000000');
这里是为好奇的人准备的完整代码。当然可以扩展到更多级别。
SELECT NAVIGATION.* FROM (
SELECT
COALESCE("E"."ID", "D"."ID", "C"."ID", "B"."ID", "A"."ID") AS FINAL_ID
FROM "NAVIGATION" AS A LEFT JOIN
(
SELECT NULL AS "ID", NULL AS "PARENT", NULL AS "TITLE"
UNION ALL
SELECT "NAVIGATION"."ID", "NAVIGATION"."PARENT", "NAVIGATION"."TITLE"
FROM "NAVIGATION"
) AS B
ON ("B"."PARENT" = "A"."ID") OR ("B"."ID" IS NULL) LEFT JOIN
(
SELECT NULL AS "ID", NULL AS "PARENT", NULL AS "TITLE"
UNION ALL
SELECT "NAVIGATION"."ID", "NAVIGATION"."PARENT", "NAVIGATION"."TITLE"
FROM "NAVIGATION"
) AS C
ON ("C"."PARENT" = "B"."ID") OR ("C"."ID" IS NULL) LEFT JOIN
(
SELECT NULL AS "ID", NULL AS "PARENT", NULL AS "TITLE"
UNION ALL
SELECT "NAVIGATION"."ID", "NAVIGATION"."PARENT", "NAVIGATION"."TITLE"
FROM "NAVIGATION"
) AS D
ON ("D"."PARENT" = "C"."ID") OR ("D"."ID" IS NULL) LEFT JOIN
(
SELECT NULL AS "ID", NULL AS "PARENT", NULL AS "TITLE"
UNION ALL
SELECT "NAVIGATION"."ID", "NAVIGATION"."PARENT", "NAVIGATION"."TITLE"
FROM "NAVIGATION"
) AS E
ON ("E"."PARENT" = "D"."ID") OR ("E"."ID" IS NULL)
WHERE "A"."ID" = '00000000-0000-0000-0000-000000000000'
)
LEFT JOIN NAVIGATION ON NAVIGATION.ID = FINAL_ID;
通常临时 tables 是 CTE 的良好替代品。事实上,有时使用临时 tables 而不是 CTE 以获得性能更好,特别是如果您计划多次加入 CTE。
运行这个
INSERT INTO #MyCTEReplacement
select ID, ParentID, 0 as Level
from Navigation
where ParentID is null
循环此操作,直到您测量到 0 个受影响的记录 - 您从 SqlCommand.ExecuteNonQuery()
获得的 return int
INSERT INTO #MyCTEReplacement
select ID, ParentID, (X.Level + 1) as Level
from Navigation N
join #MyCTEReplacement X
on X.ID = N.ParentID
where ParentID is null
您也可以在带有循环的存储过程中执行此操作。但至少你没有使用 CURSOR (ew),你可以为临时文件添加一个索引 table.
注意:这是上面的伪代码:不完美的 VistaDB 语法,也不完全符合您的架构需求
由于 VistaDB 上缺少 CTEs/recursive 查询,我试图制定一个具有一定深度的可行查询来查询 PARENT/ID 分层自引用 table。我有几个想法(SQL 来自 Firebird,因为我们在服务器端使用它):
做几个连接,像这样:
SELECT "A"."ID", "B"."ID", "C"."ID", "D"."ID" FROM "NAVIGATION" AS A LEFT JOIN "NAVIGATION" AS B ON (B.PARENT = A.ID) LEFT JOIN "NAVIGATION" AS C ON (C.PARENT = B.ID) LEFT JOIN "NAVIGATION" AS D ON (D.PARENT = C.ID) WHERE "A"."ID" = CHAR_TO_UUID('00000000-0000-0000-0000-000000000000');
然后对 A、B、C、D "ID" 列进行 COALESCE,并将其用作实际行的子查询或连接源以获取所需的内容。但是,由于第一层的第一行可能会连接到其他几行,所以这不起作用 - 我需要的是:
A B C D 0 NULL NULL NULL 0 1 NULL NULL 0 1 2 NULL 0 1 2 3 0 1 2 4 0 1 2 5
相反 - 正如预期的那样 - 我得到这个:
A B C D 0 1 2 3 0 1 2 4 0 1 2 5
有什么方法可以获取额外的
NULL
行?对子查询使用
UNION
。但是,我想不出一个可行的语法来完成这项工作。也许是另一种语法。我们只需要几个层次的深度。从技术上讲,我们可以在应用程序中评估 (1.) 的结果,但我更喜欢一种更优雅的方法,尽管它不必非常快。我们通常只会在客户端深入查询两三层,有时只会一层。尽管如此,最好不要按程序进行。
请求的一些示例数据:
ID PARENT TITLE
0 NULL 'Root Node'
1 0 '1st Level Node'
2 1 '2nd Level Node'
3 2 '3nd Level Node 1'
4 2 '3nd Level Node 2'
5 2 '3nd Level Node 3'
如果您有示例数据,将会有所帮助。但是,如果其他表中有匹配的行,则无法查询 return A
/NULL
/NULL
/NULL
。
获取所有层次结构的一种方法是为每个联接添加一个 NULL
值:
SELECT "A"."ID", "B"."ID", "C"."ID", "D"."ID"
FROM "NAVIGATION" AS A LEFT JOIN
(SELECT N.PARENT, N.ID
FROM "NAVIGATION"
UNION ALL
SELECT NULL, NULL
) B
ON B.PARENT = A.ID
(SELECT N.PARENT, N.ID
FROM "NAVIGATION"
UNION ALL
SELECT NULL, NULL
) C
ON C.PARENT = B.ID LEFT JOIN
(SELECT N.PARENT, N.ID
FROM "NAVIGATION"
UNION ALL
SELECT NULL, NULL
) D
ON D.PARENT = C.ID
WHERE "A"."ID" = CHAR_TO_UUID('00000000-0000-0000-0000-000000000000');
这里是为好奇的人准备的完整代码。当然可以扩展到更多级别。
SELECT NAVIGATION.* FROM (
SELECT
COALESCE("E"."ID", "D"."ID", "C"."ID", "B"."ID", "A"."ID") AS FINAL_ID
FROM "NAVIGATION" AS A LEFT JOIN
(
SELECT NULL AS "ID", NULL AS "PARENT", NULL AS "TITLE"
UNION ALL
SELECT "NAVIGATION"."ID", "NAVIGATION"."PARENT", "NAVIGATION"."TITLE"
FROM "NAVIGATION"
) AS B
ON ("B"."PARENT" = "A"."ID") OR ("B"."ID" IS NULL) LEFT JOIN
(
SELECT NULL AS "ID", NULL AS "PARENT", NULL AS "TITLE"
UNION ALL
SELECT "NAVIGATION"."ID", "NAVIGATION"."PARENT", "NAVIGATION"."TITLE"
FROM "NAVIGATION"
) AS C
ON ("C"."PARENT" = "B"."ID") OR ("C"."ID" IS NULL) LEFT JOIN
(
SELECT NULL AS "ID", NULL AS "PARENT", NULL AS "TITLE"
UNION ALL
SELECT "NAVIGATION"."ID", "NAVIGATION"."PARENT", "NAVIGATION"."TITLE"
FROM "NAVIGATION"
) AS D
ON ("D"."PARENT" = "C"."ID") OR ("D"."ID" IS NULL) LEFT JOIN
(
SELECT NULL AS "ID", NULL AS "PARENT", NULL AS "TITLE"
UNION ALL
SELECT "NAVIGATION"."ID", "NAVIGATION"."PARENT", "NAVIGATION"."TITLE"
FROM "NAVIGATION"
) AS E
ON ("E"."PARENT" = "D"."ID") OR ("E"."ID" IS NULL)
WHERE "A"."ID" = '00000000-0000-0000-0000-000000000000'
)
LEFT JOIN NAVIGATION ON NAVIGATION.ID = FINAL_ID;
通常临时 tables 是 CTE 的良好替代品。事实上,有时使用临时 tables 而不是 CTE 以获得性能更好,特别是如果您计划多次加入 CTE。
运行这个
INSERT INTO #MyCTEReplacement
select ID, ParentID, 0 as Level
from Navigation
where ParentID is null
循环此操作,直到您测量到 0 个受影响的记录 - 您从 SqlCommand.ExecuteNonQuery()
获得的 return intINSERT INTO #MyCTEReplacement
select ID, ParentID, (X.Level + 1) as Level
from Navigation N
join #MyCTEReplacement X
on X.ID = N.ParentID
where ParentID is null
您也可以在带有循环的存储过程中执行此操作。但至少你没有使用 CURSOR (ew),你可以为临时文件添加一个索引 table.
注意:这是上面的伪代码:不完美的 VistaDB 语法,也不完全符合您的架构需求