9

看:通過SQL Server中的遞歸CTE創建排列?

;WITH cte AS(
    SELECT 1 AS x UNION 
    SELECT 2 AS x UNION  
    SELECT 3 AS x 
) 

我可以爲所有3個值置換表:

SELECT T1.x , y=T2.x , z=t3.x 
FROM cte T1 
JOIN cte T2 
ON T1.x != T2.x 
JOIN cte T3 
ON T2.x != T3.x AND T1.x != T3.x 

這將使用SQL的笛卡爾乘積再加上消除平等價值觀的力量。

http://i.imgur.com/uJUPtVH.png

好的。

但有可能進一步提高這一遞歸僞CTE:

;WITH cte AS(
    SELECT 1 AS x , 2 AS y , 3 AS z 
    UNION ALL 
    ... 
) 

SELECT * FROM cte 

enter image description here

,使其產生的結果相同:

enter image description here

NB還有其他在使用遞歸CTE的SO中的解決方案,但它不擴展到列,而是字符串repr排列

+0

只有一行數據? –

+0

@RaduGheorghiu是的。這是種子。 –

+0

對於3個元素,它可以相對容易地完成,但其特定的3個元素。你只需要3個元素,或者你正在尋找一個可擴展的解決方案? –

回答

2

我試圖做一個CTE的很多。

然而,試圖動態「重新定義」一個行集是有點棘手。雖然使用動態SQL執行該任務相對容易,但不會帶來一些問題。

雖然這個答案可能不是最有效或最直接的,或者甚至是正確的,因爲它不是所有的CTE,它都可能爲其他人提供工作的基礎。

爲了更好地理解我的方法,請閱讀註釋,但是可能需要依次查看每個CTE表達式,然後在主塊中更改下面的代碼位,並註釋掉下面的部分。

SELECT * FROM <CTE NAME> 

祝你好運。

IF OBJECT_ID('tempdb..#cteSchema') IS NOT NULL 
    DROP Table #cteSchema 
GO 

-- BASE CTE 
;WITH cte AS(SELECT 1 AS x, 2 AS y, 3 AS z), 

    -- So we know what columns we have from the CTE we extract it to XML 
    Xml_Schema AS (SELECT CONVERT(XML,(SELECT * FROM cte FOR XML PATH(''))) AS MySchema), 

    -- Next we need to get a list of the columns from the CTE, by querying the XML, getting the values and assigning a num to the column 
    MyColumns AS (SELECT D.ROWS.value('fn:local-name(.)','SYSNAME') AS ColumnName, 
         D.ROWS.value('.','SYSNAME') as Value, 
         ROW_NUMBER() OVER (ORDER BY D.ROWS.value('fn:local-name(.)','SYSNAME')) AS Num 
        FROM Xml_Schema 
         CROSS APPLY Xml_Schema.MySchema.nodes('/*') AS D(ROWS)), 
    -- How many columns we have in the CTE, used a coupld of times below 
    ColumnStats AS (SELECT MAX(NUM) AS ColumnCount FROM MyColumns), 

    -- create a cartesian product of the column names and values, so now we get each column with it's possible values, 
    -- so {x=1, x =2, x=3, y=1, y=2, y=3, z=1, z=2, z=3} -- you get the idea. 
    PossibleValues AS (SELECT MyC.ColumnName, MyC.Num AS ColumnNum, MyColumns.Value, MyColumns.Num, 
       ROW_NUMBER() OVER (ORDER BY MyC.ColumnName, MyColumns.Value, MyColumns.Num) AS ID 
       FROM MyColumns 
        CROSS APPLY MyColumns MyC 
       ), 

    -- Now we have the possibly values of each "column" we now have to concat the values together using this recursive CTE. 
    AllRawXmlRows AS (SELECT CONVERT(VARCHAR(MAX),'<'+ISNULL((SELECT ColumnName FROM MyColumns WHERE MyColumns.Num = 1),'')+'>'+Value) as ConcatedValue, Value,ID, Counterer = 1 FROM PossibleValues 
       UNION ALL 
      SELECT CONVERT(VARCHAR(MAX),CONVERT(VARCHAR(MAX), AllRawXmlRows.ConcatedValue)+'</'+(SELECT ColumnName FROM MyColumns WHERE MyColumns.Num = Counterer)+'><'+(SELECT ColumnName FROM MyColumns WHERE MyColumns.Num = Counterer+1)+'>'+CONVERT(VARCHAR(MAX),PossibleValues.Value)) AS ConcatedValue, PossibleValues.Value, PossibleValues.ID, 
      Counterer = Counterer+1 
      FROM AllRawXmlRows 
       INNER JOIN PossibleValues ON AllRawXmlRows.ConcatedValue NOT LIKE '%'+PossibleValues.Value+'%' -- I hate this, there has to be a better way of making sure we don't duplicate values.... 
       AND AllRawXmlRows.ID <> PossibleValues.ID 
       AND Counterer < (SELECT ColumnStats.ColumnCount FROM ColumnStats) 
       ), 

    -- The above made a list but was missing the final closing XML element. so we add it. 
    -- we also restict the list to the items that contain all columns, the section above builds it up over many columns 
    XmlRows AS (SELECT DISTINCT 
       ConcatedValue +'</'+(SELECT ColumnName FROM MyColumns WHERE MyColumns.Num = Counterer)+'>' 
       AS ConcatedValue 
      FROM AllRawXmlRows WHERE Counterer = (SELECT ColumnStats.ColumnCount FROM ColumnStats) 
        ),    
    -- Wrap the output in row and table tags to create the final XML 
    FinalXML AS (SELECT (SELECT CONVERT(XML,(SELECT CONVERT(XML,ConcatedValue) FROM XmlRows FOR XML PATH('row'))) FOR XML PATH('table'))as XMLData), 

    -- Prepare a CTE that represents the structure of the original CTE with 
    DataTable AS (SELECT cte.*, XmlData 
         FROM FinalXML, cte) 
--SELECT * FROM <CTE NAME> 
    -- GETS destination columns with XML data. 
SELECT * 
    INTO #cteSchema 
FROM DataTable 


DECLARE @XML VARCHAR(MAX) =''; 
SELECT @Xml = XMLData FROM #cteSchema --Extract XML Data from the 

ALTER TABLE #cteSchema DROP Column XMLData -- Removes the superflous column 
DECLARE @h INT 
EXECUTE sp_xml_preparedocument @h OUTPUT, @XML 
    SELECT * 
     FROM OPENXML(@h, '/table/row', 2) 
      WITH #cteSchema -- just use the #cteSchema to define the structure of the xml that has been constructed 

EXECUTE sp_xml_removedocument @h 
+0

你得到了我的投票,但是對於五列,它花了2:40分鐘。 –

+0

是的,這不是很有效率,但它也不是解決問題的正確方法。動態SQL將是一個更簡單和高效的方法。感謝您的投票,但。 :) –

0

如何將1,2,3轉換爲一列,看起來與您開始使用的示例完全相同,並使用相同的方法?

;WITH origin (x,y,z) AS (
    SELECT 1,2,3 
), translated (x) AS ( 
    SELECT col 
    FROM origin 
      UNPIVOT (col FOR cols IN (x,y,z)) AS up 
) 
SELECT T1.x , y=T2.x , z=t3.x 
FROM translated T1 
     JOIN translated T2 
      ON T1.x != T2.x 
     JOIN translated T3 
      ON T2.x != T3.x AND T1.x != T3.x 
ORDER BY 1,2,3 

如果我理解正確的請求,這可能只是伎倆。

爲了在更多列上運行它,只需要將它們添加到原始cte定義+ unpivot列表中。

現在,我不知道你是如何傳遞你的1 - n值爲動態的,但如果你告訴我,我可以嘗試編輯腳本也是動態的。