2

由于 VistaDB 上缺乏 CTE/递归查询,我试图制定一个具有一定深度的可行查询来查询 PARENT/ID 分层自引用表。我有几个想法(SQL 来自 Firebird,因为我们在服务器端使用它):

  1. 做几个连接,像这样:

    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行吗?

  2. 与子查询一起使用UNION。但是,我想不出一个可行的语法来完成这项工作。

  3. 也许是另一种语法。我们只需要几个层次的深度。从技术上讲,我们可以在应用程序中评估 (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'
4

3 回答 3

2

如果您有样本数据会有所帮助。但是,如果其他表中有匹配的行,则查询无法返回A/// 。NULLNULLNULL

获取所有层次结构的一种方法是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');
于 2015-11-26T16:41:45.920 回答
1

这是好奇的完整代码。当然,它可以扩展到更多级别。

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;
于 2015-11-27T12:40:19.473 回答
0

通常临时表是 CTE 的良好替代品。事实上,有时最好使用临时表而不是 CTE 来提高性能,尤其是在您计划多次加入 CTE 的情况下。

运行这个

INSERT INTO #MyCTEReplacement
select ID, ParentID, 0 as Level
from Navigation
where ParentID is null

这个循环,直到您测量 0 条受影响的记录 - 您从 SqlCommand.ExecuteNonQuery() 获得的返回 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),并且您可以向临时表添加索引。

注意:这是上面的伪代码:不是完美的 VistaDB 语法,也不完全符合您的架构需求

于 2017-10-03T04:54:05.227 回答