递归笛卡尔积产生列名

时间:2018-08-25 20:53:00

标签: sql recursion pivot

我的问题是基于SQL的

我有一个名为SC的有效分组表,其中GROUP是char(2)

enter image description here

所以我想算出该分组的所有排列。为了做到这一点,使用了许多内部连接到SC的别名

IE

SELECT 
    ROW_NUMBER() OVER ( ORDER BY sc.Groups ) as Permutation,
    SC.GROUPS AS Group1, 
    Gr2.GROUPS as Group2,
    Gr3.GROUPS as Group3
FROM 
    SC
INNER JOIN 
    SC AS Gr2 ON 1 = 1   
INNER JOIN 
    SC AS Gr3 ON 1 = 1 

我得到了这个结果

enter image description here

这里有2个非常好的结果

  1. 我可以为新列命名,因为我为表加了别名
  2. 我可以运行结果的行号

我的问题是这不是动态的。

我想在不使用动态SQL的情况下参数化Group(有效的表联接)的数量

是否可以结合使用递归CTE和数据透视?

我尝试了以下操作,但结果不正确

;WITH cte AS 
(
    SELECT 1 AS GROUPNUMBER

    UNION ALL

    SELECT GROUPNUMBER + 1 AS GROUPNUMBER
    FROM     
        (SELECT * FROM CTE) AS CTE /*this is the recursive call which starts the recursion*/
    WHERE    
        GROUPNUMBER < 10     /* Terminating condition */
)
SELECT *
FROM
    (SELECT  
         A.GROUPNUMBER, A.GROUPS AS GROUPSA, B.GROUPS AS GROUPSB, 
         ROW_NUMBER() OVER (PARTITION BY A.GROUPS ORDER BY A.GROUPS) AS Z 
     FROM
         (SELECT * FROM SC, cte) AS A 
     INNER JOIN
         (SELECT * FROM SC, cte) AS B ON 1 = 1 
    ) AS X
PIVOT
    (MAX(X.GROUPSB) 
         FOR Z IN ([2],[3],[3],[4],[5],[6],[7],[8],[9],[10])
    ) AS P

enter image description here

谢谢

1 个答案:

答案 0 :(得分:1)

没有动态sql吗?
如果您不介意sql返回固定数量的列,则有可能。
甚至不使用PIVOT。

示例包含4个数字列,但@MaxLvl较低

测试妊娠here

DECLARE @SC TABLE ([Groups] CHAR(2) PRIMARY KEY);

INSERT INTO @SC ([Groups]) VALUES 
('AB'),('AC'),('AD'),('BC'),('BD'),('CD');

DECLARE @MaxLvl INT = 3;

;WITH CTE AS 
(
    SELECT Groups AS Base, 1 Lvl, 
    CAST (Groups AS VARCHAR(MAX)) AS ListGroups, 
    Groups AS [1],
    charnull AS [2],
    charnull AS [3],
    charnull AS [4]
    FROM @SC
    CROSS APPLY (SELECT CAST(NULL AS CHAR(2)) AS charnull) ch

    UNION ALL

    SELECT c.Base, c.Lvl+1, c.ListGroups+','+t.Groups,
    c.[1],
    IIF(c.Lvl = 1, t.Groups, c.[2]),
    IIF(c.Lvl = 2, t.Groups, c.[3]),
    IIF(c.Lvl = 3, t.Groups, c.[4])
    FROM CTE AS c
    JOIN @SC AS t ON c.Lvl < @MaxLvl
)
SELECT
ROW_NUMBER() OVER (ORDER BY ListGroups) AS rn,
 [1],[2],[3],[4]
FROM CTE
WHERE Lvl = @MaxLvl
ORDER BY ListGroups;

但是如果可以选择Dynamic Sql吗?
这是一个示例:

-- Using a temporary table for demonstration
IF OBJECT_ID('tempdb..#SC') IS NOT NULL DROP TABLE #SC;
CREATE TABLE #SC ([Groups] CHAR(2) PRIMARY KEY);

INSERT INTO #SC ([Groups]) VALUES 
('AB'),('AC'),('AD'),('BC'),('BD'),('CD');

DECLARE @MaxLvl INT = 3;

DECLARE @DynamicSql VARCHAR(max);
DECLARE @Fields VARCHAR(max) = 'SC1.[Groups]';
DECLARE @AliasedFields VARCHAR(max) = 'SC1.[Groups] AS [1]';
DECLARE @Joins VARCHAR(max) = 'FROM #SC AS SC1';

DECLARE @Lvl INT = 1;
WHILE @Lvl < @MaxLvl
BEGIN
  SET @Lvl = @Lvl + 1; 
  SET @Fields = CONCAT(@Fields,', ','SC',@Lvl,'.[Groups]');
  SET @AliasedFields = CONCAT(@AliasedFields,',',CHAR(10),'SC',@Lvl,'.[Groups] AS [',@Lvl,']');
  SET @Joins = CONCAT(@Joins,CHAR(10),'CROSS JOIN #SC AS SC', @Lvl);
END;

SET @DynamicSql = CONCAT('SELECT ', 
     CHAR(10), 'ROW_NUMBER() OVER (ORDER BY ', @Fields, ') AS RN,', 
     CHAR(10), @AliasedFields , 
     CHAR(10), @Joins,
     CHAR(10), 'ORDER BY ', @Fields);

-- select @DynamicSql AS DynamicSql;
EXEC(@DynamicSql);

测试妊娠here