递归CTE问题

Chr*_*ris 6 sql-server recursive-cte

我试图在SQL Server中使用递归CTE从包含底层树结构的表中构建谓词公式.例如,我的表看起来像:

Id  |  Operator/Val |  ParentId
--------------------------
1   | 'OR'          |  NULL 
2   | 'AND'         |  1
3   | 'AND'         |  1
4   | '>'           |  2
5   | 'a'           |  4
6   | 'alpha'       |  4
...
Run Code Online (Sandbox Code Playgroud)

...表示((a>α)AND(b>β))OR((c>γ)AND(a <delta)).

ParentId是对父节点的同一表中的Id的引用.

我想编写一个查询,它将从表中构建此字符串.可能吗?

谢谢

Luc*_*ero 5

对于生产环境,如果性能和递归深度限制(32级)不是问题,您可能希望使用递归函数以简化.

但是,这是一个非常干净且非常有效的CTE解决方案(请注意,它将接受任意数量的"树"并为每个没有父项的项返回一个结果):

DECLARE @tbl TABLE
  (
   id int PRIMARY KEY
          NOT NULL,
   op nvarchar(max) NOT NULL,
   parent int
  ) ;
INSERT INTO @tbl
    SELECT 1, 'OR', NULL    UNION ALL
    SELECT 2, 'AND', 1    UNION ALL
    SELECT 3, 'AND', 1    UNION ALL
    SELECT 4, '>', 2    UNION ALL
    SELECT 5, 'a', 4    UNION ALL
    SELECT 6, 'alpha', 4    UNION ALL
    SELECT 7, '>', 2    UNION ALL
    SELECT 8, 'b', 7    UNION ALL
    SELECT 9, 'beta', 7    UNION ALL
    SELECT 10, '>', 3    UNION ALL
    SELECT 11, 'c', 10    UNION ALL
    SELECT 12, 'gamma', 10    UNION ALL
    SELECT 13, '>', 3    UNION ALL
    SELECT 14, 'd', 13    UNION ALL
    SELECT 15, 'delta', 13 ;

WITH  nodes -- A CTE which sets a flag to 1 for non-leaf nodes
        AS (
            SELECT t.*, CASE WHEN p.parent IS NULL THEN 0
                             ELSE 1
                        END node
              FROM @tbl t 
              LEFT JOIN (
                         SELECT DISTINCT parent
                          FROM @tbl
                        ) p ON p.parent = T.id
           ),
      rec -- the main recursive run to determine the sort order and add meta information
        AS (
            SELECT id rootId, node lvl, CAST(0 AS float) sort, CAST(0.5 AS float) offset, *
              FROM nodes
              WHERE parent IS NULL
            UNION ALL
            SELECT r.rootId, r.lvl+t.node, r.sort+r.offset*CAST((ROW_NUMBER() OVER (ORDER BY t.id)-1)*2-1 AS float),
                r.offset/2, t.*
              FROM rec r 
              JOIN 
                nodes t ON r.id = t.parent
           ),
      ranked -- ranking of the result to sort and find the last item
        AS (
            SELECT rootId, ROW_NUMBER() OVER (PARTITION BY rootId ORDER BY sort) ix,
                COUNT(1) OVER (PARTITION BY rootId) cnt, lvl, op
              FROM rec
           ),
      concatenated -- concatenate the string, adding ( and ) as needed
        AS (
            SELECT rootId, ix, cnt, lvl, CAST(REPLICATE('(', lvl)+op AS nvarchar(max)) txt
              FROM ranked
              WHERE ix = 1
            UNION ALL
            SELECT r.rootId, r.ix, r.cnt, r.lvl,
                c.txt+COALESCE(REPLICATE(')', c.lvl-r.lvl), '')+' '+COALESCE(REPLICATE('(', r.lvl-c.lvl), '')+r.op
                +CASE WHEN r.ix = r.cnt THEN REPLICATE(')', r.lvl)
                      ELSE ''
                 END
              FROM ranked r 
              JOIN 
                concatenated c ON (r.rootId = c.rootId)
                                  AND (r.ix = c.ix+1)
           )
  SELECT rootId id, txt
    FROM concatenated
    WHERE ix = cnt
    OPTION (MAXRECURSION 0);
Run Code Online (Sandbox Code Playgroud)


Kro*_*ass 0

是的,可以这样做,但问题不在于 CTE,请使用 PIVOT 检查它,从此链接中了解更多信息

http://msdn.microsoft.com/en-us/library/ms177410.aspx

本文档中的一些示例与您的问题类似