簡體   English   中英

在 SQL 中生成所有組合

[英]Generate all combinations in SQL

我需要在給定的一組大小@n生成大小@k所有組合。 有人可以查看以下 SQL 並首先確定以下邏輯是否返回預期結果,其次是否有更好的方法?

/*CREATE FUNCTION dbo.Factorial ( @x int ) 
RETURNS int 
AS
BEGIN
    DECLARE @value int

    IF @x <= 1
        SET @value = 1
    ELSE
        SET @value = @x * dbo.Factorial( @x - 1 )

    RETURN @value
END
GO*/
SET NOCOUNT ON;
DECLARE @k int = 5, @n int;
DECLARE @set table ( [value] varchar(24) );
DECLARE @com table ( [index] int );

INSERT @set VALUES ('1'),('2'),('3'),('4'),('5'),('6');

SELECT @n = COUNT(*) FROM @set;

DECLARE @combinations int = dbo.Factorial(@n) / (dbo.Factorial(@k) * dbo.Factorial(@n - @k));

PRINT CAST(@combinations as varchar(max)) + ' combinations';

DECLARE @index int = 1;

WHILE @index <= @combinations
BEGIN
    INSERT @com VALUES (@index)
    SET @index = @index + 1
END;

WITH [set] as (
    SELECT 
        [value], 
        ROW_NUMBER() OVER ( ORDER BY [value] ) as [index]
    FROM @set
)
SELECT 
    [values].[value], 
    [index].[index] as [combination]
FROM [set] [values]
CROSS JOIN @com [index]
WHERE ([index].[index] + [values].[index] - 1) % (@n) BETWEEN 1 AND @k
ORDER BY
    [index].[index];

返回組合

使用數字表或數字生成 CTE,選擇 0 到 2^n - 1。使用這些數字中包含 1 的位位置來指示組合中相關成員的存在或不存在,並消除那些沒有的正確數量的值,您應該能夠返回包含您想要的所有組合的結果集。

WITH Nums (Num) AS (
   SELECT Num
   FROM Numbers
   WHERE Num BETWEEN 0 AND POWER(2, @n) - 1
), BaseSet AS (
   SELECT ind = Power(2, Row_Number() OVER (ORDER BY Value) - 1), *
   FROM @set
), Combos AS (
   SELECT
      ComboID = N.Num,
      S.Value,
      Cnt = Count(*) OVER (PARTITION BY N.Num)
   FROM
      Nums N
      INNER JOIN BaseSet S ON N.Num & S.ind <> 0
)
SELECT
   ComboID,
   Value
FROM Combos
WHERE Cnt = @k
ORDER BY ComboID, Value;

這個查詢執行得非常好,但我想到了一種優化它的方法,從Nifty Parallel Bit Count中提取以首先獲得一次獲取正確數量的項目。 這比 CPU 和時間快 3 到 3.5 倍:

WITH Nums AS (
   SELECT Num, P1 = (Num & 0x55555555) + ((Num / 2) & 0x55555555)
   FROM dbo.Numbers
   WHERE Num BETWEEN 0 AND POWER(2, @n) - 1
), Nums2 AS (
   SELECT Num, P2 = (P1 & 0x33333333) + ((P1 / 4) & 0x33333333)
   FROM Nums
), Nums3 AS (
   SELECT Num, P3 = (P2 & 0x0f0f0f0f) + ((P2 / 16) & 0x0f0f0f0f)
   FROM Nums2
), BaseSet AS (
   SELECT ind = Power(2, Row_Number() OVER (ORDER BY Value) - 1), *
   FROM @set
)
SELECT
   ComboID = N.Num,
   S.Value
FROM
   Nums3 N
   INNER JOIN BaseSet S ON N.Num & S.ind <> 0
WHERE P3 % 255 = @k
ORDER BY ComboID, Value;

我去閱讀了位計數頁面,並認為如果我不執行 % 255 但一直使用位算術,這會表現得更好。 當我有機會時,我會嘗試一下,看看效果如何。

我的性能聲明基於沒有 ORDER BY 子句運行的查詢。 為清楚起見,此代碼所做的是計算Numbers表中Num中設置的 1 位的Numbers 這是因為數字被用作一種索引器來選擇集合的哪些元素在當前組合中,因此 1 位的數量將相同。

我希望你喜歡它!

作為記錄,這種使用整數位模式來選擇集合成員的技術就是我創造的“垂直交叉連接”。 它有效地導致多組數據的交叉連接,其中集合和交叉連接的數量是任意的。 這里,套數是一次拍攝的項目數。

實際上,通常水平意義上的交叉連接(通過每個連接向現有列列表中添加更多列)看起來像這樣:

SELECT
   A.Value,
   B.Value,
   C.Value
FROM
   @Set A
   CROSS JOIN @Set B
   CROSS JOIN @Set C
WHERE
   A.Value = 'A'
   AND B.Value = 'B'
   AND C.Value = 'C'

我上面的查詢有效地“交叉連接”了多次,只需要一個連接。 與實際的交叉連接相比,結果是未旋轉的,當然,但這是小事。

對你的代碼的批評

首先,我可以建議對您的因子 UDF 進行此更改:

ALTER FUNCTION dbo.Factorial (
   @x bigint
)
RETURNS bigint
AS
BEGIN
   IF @x <= 1 RETURN 1
   RETURN @x * dbo.Factorial(@x - 1)
END

現在,您可以計算更大的組合集,而且效率更高。 您甚至可以考慮使用decimal(38, 0)來允許在您的組合計算中進行更大的中間計算。

其次,您給定的查詢沒有返回正確的結果。 例如,使用我在下面的性能測試中的測試數據,集合 1 與集合 18 相同。看起來您的查詢采用了環繞的滑動條:每個集合始終是 5 個相鄰的成員,看起來像這樣(我旋轉了以便於查看):

 1 ABCDE            
 2 ABCD            Q
 3 ABC            PQ
 4 AB            OPQ
 5 A            NOPQ
 6             MNOPQ
 7            LMNOP 
 8           KLMNO  
 9          JKLMN   
10         IJKLM    
11        HIJKL     
12       GHIJK      
13      FGHIJ       
14     EFGHI        
15    DEFGH         
16   CDEFG          
17  BCDEF           
18 ABCDE            
19 ABCD            Q

比較來自我的查詢的模式:

 31 ABCDE  
 47 ABCD F 
 55 ABC EF 
 59 AB DEF 
 61 A CDEF 
 62  BCDEF 
 79 ABCD  G
 87 ABC E G
 91 AB DE G
 93 A CDE G
 94  BCDE G
103 ABC  FG
107 AB D FG
109 A CD FG
110  BCD FG
115 AB  EFG
117 A C EFG
118  BC EFG
121 A  DEFG
...

只是為了讓感興趣的人了解位模式 -> 組合事物的索引,請注意二進制中的 31 = 11111 並且模式是 ABCDE。 二進制中的 121 是 1111001,模式是 A__DEFG(向后映射)。

實數表的性能結果

我在上面的第二個查詢中使用大集合進行了一些性能測試。 我目前沒有使用的服務器版本的記錄。 這是我的測試數據:

DECLARE
   @k int,
   @n int;

DECLARE @set TABLE (value varchar(24));
INSERT @set VALUES ('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('I'),('J'),('K'),('L'),('M'),('N'),('O'),('P'),('Q');
SET @n = @@RowCount;
SET @k = 5;

DECLARE @combinations bigint = dbo.Factorial(@n) / (dbo.Factorial(@k) * dbo.Factorial(@n - @k));
SELECT CAST(@combinations as varchar(max)) + ' combinations', MaxNumUsedFromNumbersTable = POWER(2, @n);

Peter 表明,這種“垂直交叉聯接”的性能不如簡單地編寫動態 SQL 來實際執行它避免的 CROSS JOIN。 以多讀幾遍的微不足道的代價,他的解決方案的指標提高了 10 到 17 倍。 隨着工作量的增加,他的查詢的性能下降得比我的快,但速度不足以阻止任何人使用它。

下面的第二組數字是除以表中第一行的系數,只是為了顯示它是如何縮放的。

埃里克

Items  CPU   Writes  Reads  Duration |  CPU  Writes  Reads Duration
----- ------ ------ ------- -------- | ----- ------ ------ --------
17•5    7344     0     3861    8531  |
18•9   17141     0     7748   18536  |   2.3          2.0      2.2
20•10  76657     0    34078   84614  |  10.4          8.8      9.9
21•11 163859     0    73426  176969  |  22.3         19.0     20.7
21•20 142172     0    71198  154441  |  19.4         18.4     18.1

彼得

Items  CPU   Writes  Reads  Duration |  CPU  Writes  Reads Duration
----- ------ ------ ------- -------- | ----- ------ ------ --------
17•5     422    70    10263     794  | 
18•9    6046   980   219180   11148  |  14.3   14.0   21.4    14.0
20•10  24422  4126   901172   46106  |  57.9   58.9   87.8    58.1
21•11  58266  8560  2295116  104210  | 138.1  122.3  223.6   131.3
21•20  51391     5  6291273   55169  | 121.8    0.1  613.0    69.5

推斷,最終我的查詢會更便宜(盡管它是從讀取開始的),但不會持續很長時間。 要使用集合中的 21 個項目已經需要一個數字表,直到 2097152...

這是我最初發表的評論,然后才意識到我的解決方案在使用動態數字表時會表現得更好:

我喜歡針對此類問題的單查詢解決方案,但是如果您正在尋找最佳性能,那么實際的交叉連接是最好的,除非您開始處理大量的組合。 但是,對於數十萬甚至數百萬行,人們想要什么? 即使讀取次數不斷增加似乎也不是什么大問題,盡管 600 萬已經很多了,而且還在快速增長……

反正。 動態 SQL 獲勝。 我仍然有一個漂亮的查詢。 :)

帶有動態數字表的性能結果

當我最初寫這個答案時,我說:

請注意,您可以使用動態數字表,但我還沒有嘗試過。

嗯,我試了一下,結果是效果好多了! 這是我使用的查詢:

DECLARE @N int = 16, @K int = 12;

CREATE TABLE #Set (Value char(1) PRIMARY KEY CLUSTERED);
CREATE TABLE #Items (Num int);
INSERT #Items VALUES (@K);
INSERT #Set 
SELECT TOP (@N) V
FROM
   (VALUES ('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('I'),('J'),('K'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U'),('V'),('W'),('X'),('Y'),('Z')) X (V);
GO
DECLARE
   @N int = (SELECT Count(*) FROM #Set),
   @K int = (SELECT TOP 1 Num FROM #Items);

DECLARE @combination int, @value char(1);
WITH L0 AS (SELECT 1 N UNION ALL SELECT 1),
L1 AS (SELECT 1 N FROM L0, L0 B),
L2 AS (SELECT 1 N FROM L1, L1 B),
L3 AS (SELECT 1 N FROM L2, L2 B),
L4 AS (SELECT 1 N FROM L3, L3 B),
L5 AS (SELECT 1 N FROM L4, L4 B),
Nums AS (SELECT Row_Number() OVER(ORDER BY (SELECT 1)) Num FROM L5),
Nums1 AS (
   SELECT Num, P1 = (Num & 0x55555555) + ((Num / 2) & 0x55555555)
   FROM Nums
   WHERE Num BETWEEN 0 AND Power(2, @N) - 1
), Nums2 AS (
   SELECT Num, P2 = (P1 & 0x33333333) + ((P1 / 4) & 0x33333333)
   FROM Nums1
), Nums3 AS (
   SELECT Num, P3 = (P2 & 0x0F0F0F0F) + ((P2 / 16) & 0x0F0F0F0F)
   FROM Nums2
), BaseSet AS (
   SELECT Ind = Power(2, Row_Number() OVER (ORDER BY Value) - 1), *
   FROM #Set
)
SELECT
   @Combination = N.Num,
   @Value = S.Value
FROM
   Nums3 N
   INNER JOIN BaseSet S
      ON N.Num & S.Ind <> 0
WHERE P3 % 255 = @K;

請注意,我將這些值選擇到變量中以減少測試所有內容所需的時間和內存。 服務器仍然執行所有相同的工作。 我將 Peter 的版本修改為類似的,並刪除了不必要的額外內容,使它們盡可能精簡。 用於這些測試的服務器版本是Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) Standard Edition on Windows NT 5.2 <X86> (Build 3790: Service Pack 2) (VM)在 VM 上運行。

下面的圖表顯示了 N 和 K 值高達 21 的性能曲線。它們的基礎數據在本頁的另一個答案中 這些值是每個查詢在每個 K 和 N 值上運行 5 次的結果,然后拋出每個指標的最佳和最差值,並對剩余的 3 個進行平均。

基本上,我的版本在 N 的高值和 K 的低值處有一個“肩部”(在圖表的最左角),這使得它在那里的表現比動態 SQL 版本差。 然而,這保持相當低和恆定,並且 N = 21 和 K = 11 附近的中心峰值對於 Duration、CPU 和 Reads 比動態 SQL 版本低得多。

我包含了每個項目預計返回的行數的圖表,以便您可以查看查詢的執行情況與它必須完成的工作的大小。

持續時間圖表CPU圖表讀取圖表行數圖表

有關完整的性能結果,請參閱我在此頁面上的其他答案 我達到了帖子字符限制,無法將其包含在此處。 (有什么想法可以放在哪里?)為了將事情與我的第一個版本的性能結果進行比較,這里的格式與以前相同:

埃里克

Items  CPU  Duration  Reads  Writes |  CPU  Duration  Reads 
----- ----- -------- ------- ------ | ----- -------- -------
17•5    354      378   12382      0 |
18•9   1849     1893   97246      0 |   5.2      5.0     7.9
20•10  7119     7357  369518      0 |  20.1     19.5    29.8
21•11 13531    13807  705438      0 |  38.2     36.5    57.0
21•20  3234     3295     48       0 |   9.1      8.7     0.0

彼得

Items  CPU  Duration  Reads  Writes |  CPU  Duration  Reads 
----- ----- -------- ------- ------ | ----- -------- -------
17•5     41       45    6433      0 | 
18•9   2051     1522  214021      0 |  50.0     33.8    33.3
20•10  8271     6685  864455      0 | 201.7    148.6   134.4
21•11 18823    15502 2097909      0 | 459.1    344.5   326.1
21•20 25688    17653 4195863      0 | 626.5    392.3   652.2

結論

  • 動態數字表比包含行的真實表要好,因為以大量行數讀取一個表需要大量 I/O。 最好用一點CPU。
  • 我最初的測試不夠廣泛,無法真正顯示兩個版本的性能特征。
  • Peter 的版本可以通過使每個 JOIN 不僅大於前一個項目來改進,而且還根據集合中必須容納多少項目來限制最大值。 例如,在一次取 21 個條目的 21 個條目中,只有 21 行的一個答案(所有 21 個條目,一次),但是動態 SQL 版本中的中間行集,在執行計划的早期,包含諸如“ AU”在第 2 步,即使這將在下一次加入時被丟棄,因為沒有高於“U”的可用值。 同樣,第 5 步的中間行集將包含“ARSTU”,但此時唯一有效的組合是“ABCDE”。 這個改進的版本在中心不會有一個較低的峰值,所以可能不會改進到足以成為明顯的贏家,但它至少會變得對稱,這樣圖表就不會在超過該區域的中間時保持最大值,而是會回落像我的版本一樣接近 0(請參閱每個查詢的峰值的頂角)。

持續時間分析

  • 在持續時間(> 100 毫秒)上,版本之間沒有真正顯着的差異,直到一次取 12 個 14 個項目。 到目前為止,我的版本贏了 30 次,動態 SQL 版本贏了 43 次。
  • 從 14•12 開始,我的版本快了 65 倍(59 >100 毫秒),動態 SQL 版本快了 64 倍(60 >100 毫秒)。 然而,在我的版本更快的所有時間里,它平均節省了 256.5 秒的總持續時間,而當動態 SQL 版本更快時,它節省了 80.2 秒。
  • 所有試驗的總平均持續時間為 Erik 270.3 秒,Peter 446.2 秒。
  • 如果創建了一個查找表來確定要使用哪個版本(為輸入選擇更快的版本),則所有結果都可以在 188.7 秒內完成。 每次使用最慢的一次需要 527.7 秒。

閱讀分析

持續時間分析顯示我的查詢以顯着但不是過大的數量獲勝。 當指標切換到讀取時,會出現一個非常不同的畫面——我的查詢平均使用 1/10 的讀取。

  • 在讀取(> 1000)中的版本之間沒有真正顯着的差異,直到 9 個項目一次取 9 個。 到目前為止,我的版本贏了 30 次,動態 SQL 版本贏了 17 次。
  • 從 9•9 開始,我的版本使用了更少的讀取 118 次(113 >1000),動態 SQL 版本使用了 69 次(31 >1000)。 然而,我的版本一直使用較少的讀取,平均節省了 7590 萬次讀取,而當動態 SQL 版本更快時,它節省了 38 萬次讀取。
  • 所有試驗的總平均讀數為 Erik 8.4M,Peter 84M。
  • 如果創建了一個查找表來確定使用哪個版本(為輸入選擇最好的一個),則所有結果都可以在 8M 讀取中執行。 每次使用最差的一次將需要 84.3M 讀取。

我很想看到更新的動態 SQL 版本的結果,該版本對如上所述的每個步驟中選擇的項目設置了額外的上限。

附錄

我的查詢的以下版本比上面列出的性能結果提高了大約 2.25%。 我使用了 MIT 的 HAKMEM 位計數方法,並在row_number()的結果上添加了一個Convert(int) ,因為它返回一個 bigint。 當然,我希望這是我用於上述所有性能測試、圖表和數據的版本,但我不太可能重做,因為它是勞動密集型的。

WITH L0 AS (SELECT 1 N UNION ALL SELECT 1),
L1 AS (SELECT 1 N FROM L0, L0 B),
L2 AS (SELECT 1 N FROM L1, L1 B),
L3 AS (SELECT 1 N FROM L2, L2 B),
L4 AS (SELECT 1 N FROM L3, L3 B),
L5 AS (SELECT 1 N FROM L4, L4 B),
Nums AS (SELECT Row_Number() OVER(ORDER BY (SELECT 1)) Num FROM L5),
Nums1 AS (
   SELECT Convert(int, Num) Num
   FROM Nums
   WHERE Num BETWEEN 1 AND Power(2, @N) - 1
), Nums2 AS (
   SELECT
      Num,
      P1 = Num - ((Num / 2) & 0xDB6DB6DB) - ((Num / 4) & 0x49249249)
   FROM Nums1
),
Nums3 AS (SELECT Num, Bits = ((P1 + P1 / 8) & 0xC71C71C7) % 63 FROM Nums2),
BaseSet AS (SELECT Ind = Power(2, Row_Number() OVER (ORDER BY Value) - 1), * FROM #Set)
SELECT
   N.Num,
   S.Value
FROM
   Nums3 N
   INNER JOIN BaseSet S
      ON N.Num & S.Ind <> 0
WHERE
   Bits = @K

我忍不住再展示一個版本,它通過查找來獲取位數。 它甚至可能比其他版本更快:

DECLARE @BitCounts binary(255) = 
   0x01010201020203010202030203030401020203020303040203030403040405
   + 0x0102020302030304020303040304040502030304030404050304040504050506
   + 0x0102020302030304020303040304040502030304030404050304040504050506
   + 0x0203030403040405030404050405050603040405040505060405050605060607
   + 0x0102020302030304020303040304040502030304030404050304040504050506
   + 0x0203030403040405030404050405050603040405040505060405050605060607
   + 0x0203030403040405030404050405050603040405040505060405050605060607
   + 0x0304040504050506040505060506060704050506050606070506060706070708;
WITH L0 AS (SELECT 1 N UNION ALL SELECT 1),
L1 AS (SELECT 1 N FROM L0, L0 B),
L2 AS (SELECT 1 N FROM L1, L1 B),
L3 AS (SELECT 1 N FROM L2, L2 B),
L4 AS (SELECT 1 N FROM L3, L3 B),
L5 AS (SELECT 1 N FROM L4, L4 B),
Nums AS (SELECT Row_Number() OVER(ORDER BY (SELECT 1)) Num FROM L5),
Nums1 AS (SELECT Convert(int, Num) Num FROM Nums WHERE Num BETWEEN 1 AND Power(2, @N) - 1),
BaseSet AS (SELECT Ind = Power(2, Row_Number() OVER (ORDER BY Value) - 1), * FROM ComboSet)
SELECT
   @Combination = N.Num,
   @Value = S.Value
FROM
   Nums1 N
   INNER JOIN BaseSet S
      ON N.Num & S.Ind <> 0
WHERE
   @K =
      Convert(int, Substring(@BitCounts, N.Num & 0xFF, 1))
      + Convert(int, Substring(@BitCounts, N.Num / 256 & 0xFF, 1))
      + Convert(int, Substring(@BitCounts, N.Num / 65536 & 0xFF, 1))
      + Convert(int, Substring(@BitCounts, N.Num / 16777216, 1))

請原諒這個額外的答案。 我在原始答案中遇到了帖子字符限制。

以下是我的答案中圖表的完整平均數字性能結果。

      |             Erik             |             Peter
 N  K |  CPU  Duration Reads  Writes |  CPU  Duration  Reads  Writes
-- -- - ----- -------- ------ ------ - ----- -------- ------- ------
 1  1 |     0        0      7      0 |     0        0       7      0
 2  1 |     0        0     10      0 |     0        0       7      0
 2  2 |     0        0      7      0 |     0        0      11      0
 3  1 |     0        0     12      0 |     0        0       7      0
 3  2 |     0        0     12      0 |     0        0      13      0
 3  3 |     5        0      7      0 |     0        0      19      0
 4  1 |     0        0     14      0 |     0        0       7      0
 4  2 |     0        0     18      0 |     0        0      15      0
 4  3 |     0        0     14      0 |     5        0      27      0
 4  4 |     0        0      7      0 |     0        0      35      0
 5  1 |     5        0     16      0 |     5        0       7      0
 5  2 |     0        0     26      0 |     0        0      17      0
 5  3 |     0        0     26      0 |     0        0      37      0
 5  4 |     0        0     16      0 |     0        0      57      0
 5  5 |     0        0      7      0 |     0        0      67      0
 6  1 |     0        0     18      0 |     0        0       7      0
 6  2 |     5        0     36      0 |     0        0      19      0
 6  3 |     0        0     46      0 |     0        0      49      0
 6  4 |     0        0     36      0 |     0        0      89      0
 6  5 |     5        0     18      0 |     5        0     119      0
 6  6 |     0        0      7      0 |     0        0     131      0
 7  1 |     5        0     20      0 |     0        0       7      0
 7  2 |     0        0     48      0 |     0        0      21      0
 7  3 |     0        0     76      0 |     0        0      63      0
 7  4 |     0        0     76      0 |     0        0     133      0
 7  5 |     0        1     48      0 |     0        1     203      0
 7  6 |     5        0     20      0 |     0        1     245      0
 7  7 |     5        0      7      0 |     0        3     259      0
 8  1 |     5        2     22      0 |     0        4       7      0
 8  2 |     0        1     62      0 |     0        0      23      0
 8  3 |     0        1    118      0 |     0        0      79      0
 8  4 |     0        1    146      0 |     0        1     191      0
 8  5 |     5        3    118      0 |     0        1     331      0
 8  6 |     5        1     62      0 |     5        2     443      0
 8  7 |     0        0     22      0 |     5        3     499      0
 8  8 |     0        0      7      0 |     5        3     515      0
 9  1 |     0        2     24      0 |     0        0       7      0
 9  2 |     5        3     78      0 |     0        0      25      0
 9  3 |     5        3    174      0 |     0        1      97      0
 9  4 |     5        5    258      0 |     0        2     265      0
 9  5 |     5        7    258      0 |    10        4     517      0
 9  6 |     5        5    174      0 |     5        5     769      0
 9  7 |     0        3     78      0 |    10        4     937      0
 9  8 |     0        0     24      0 |     0        3    1009      0
 9  9 |     0        1      7      0 |     0        4    1027      0
10  1 |    10        4     26      0 |     0        0       7      0
10  2 |     5        5     96      0 |     0        0      27      0
10  3 |     5        2    246      0 |     0        0     117      0
10  4 |    10       10    426      0 |    10        4     357      0
10  5 |    15       12    510      0 |     5        8     777      0
10  6 |    15       16    426      0 |    10        9    1281      0
10  7 |    10        4    246      0 |    10        9    1701      0
10  8 |    10        5     96      0 |    10        5    1941      0
10  9 |     5        4     26      0 |    10        7    2031      0
10 10 |     5        0      7      0 |    10        7    2051      0
11  1 |    10        8     28      0 |     0        0       7      0
11  2 |    15       11    116      0 |     0        0      29      0
11  3 |    21       24    336      0 |    10        3     139      0
11  4 |    21       18    666      0 |     5        2     469      0
11  5 |    21       20    930      0 |     5        3    1129      0
11  6 |    26       35    930      0 |    15       12    2053      0
11  7 |    20       14    666      0 |     5       25    2977      0
11  8 |    15        9    336      0 |    20       14    3637      0
11  9 |    10        7    116      0 |    21       27    3967      0
11 10 |    10        8     28      0 |    36       34    4086      0
11 11 |     5        8      7      0 |    15       15    4109      0
12  1 |    16       18     30      0 |     5        0       7      0
12  2 |    31       32    138      0 |     0        0      31      0
12  3 |    31       26    446      0 |    10        2     163      0
12  4 |    47       40    996      0 |    10        7     603      0
12  5 |    47       46   1590      0 |    21       17    1593      0
12  6 |    57       53   1854      0 |    31       30    3177      0
12  7 |    41       39   1590      0 |    31       30    5025      0
12  8 |    41       42    996      0 |    42       43    6609      0
12  9 |    31       26    446      0 |    52       52    7607      0
12 10 |    20       19    138      0 |    57       62    8048      0
12 11 |    15       17     30      0 |    72       64    8181      0
12 12 |    15       10      7      0 |    67       38    8217      0
13  1 |    31       32     32      0 |     0        0       7      0
13  2 |    21       25    162      0 |     0        0      33      0
13  3 |    36       34    578      0 |     5        2     189      0
13  4 |    57       65   1436      0 |    10        5     761      0
13  5 |    41       40   2580      0 |    10       10    2191      0
13  6 |    62       56   3438      0 |    31       32    4765      0
13  7 |    62       62   3438      0 |    57       53    8251      0
13  8 |    52       64   2580      0 |    52       47   11710      0
13  9 |    26       28   1436      0 |    93       96   14311      0
13 10 |    31       29    578      0 |   161      104   15891      0
13 11 |    36       35    162      0 |   129       99   16525      0
13 12 |    21       22     32      0 |   156       96   16383      0
13 13 |    26       30      7      0 |   166       98   16411      0
14  1 |    57       53     34      0 |     0        0       7      0
14  2 |    52       50    188      0 |     0        0      35      0
14  3 |    57       60    734      0 |    10        4     217      0
14  4 |    78       76   2008      0 |    15        8     945      0
14  5 |    99       97   4010      0 |    36       34    2947      0
14  6 |   120      125   6012      0 |    41       47    6951      0
14  7 |   125      119   6870      0 |    93       94   12957      0
14  8 |   135      138   6012      0 |    88       98   19821      0
14  9 |    78      153   4010      0 |   234      156   26099      0
14 10 |    94       92   2008      0 |   229      133   30169      0
14 11 |    83       90    734      0 |   239      136   32237      0
14 12 |    47       46    188      0 |   281      176   33031      0
14 13 |    52       53     34      0 |   260      167   32767      0
14 14 |    46       47      7      0 |   203      149   32797      0
15  1 |    83       83     36      0 |     0        0       7      0
15  2 |   145      139    216      0 |     0        2      37      0
15  3 |   104       98    916      0 |     0        2     247      0
15  4 |   135      135   2736      0 |    15       17    1157      0
15  5 |    94       97   6012      0 |    26       27    3887      0
15  6 |   192      188  10016      0 |    57       53    9893      0
15  7 |   187      192  12876      0 |    73       73   19903      0
15  8 |   286      296  12876      0 |   338      230   33123      0
15  9 |   208      207  10016      0 |   354      223   46063      0
15 10 |   140      143   6012      0 |   443      334   56143      0
15 11 |    88       86   2736      0 |   391      273   62219      0
15 12 |    73       72    916      0 |   432      269   65019      0
15 13 |   109      117    216      0 |   317      210   65999      0
15 14 |   156      187     36      0 |   411      277   66279      0
15 15 |   140      142      7      0 |   354      209   65567      0
16  1 |   281      281     38      0 |     0        0       7      0
16  2 |   141      146    246      0 |     0        0      39      0
16  3 |   208      206   1126      0 |    10        4     279      0
16  4 |   187      189   3646      0 |    15       13    1399      0
16  5 |   234      234   8742      0 |    42       42    5039      0
16  6 |   333      337  16022      0 |    83       85   13775      0
16  7 |   672      742  22886      0 |   395      235   30087      0
16  8 |   510      510  25746      0 |   479      305   53041      0
16  9 |   672      675  22886      0 |   671      489   78855      0
16 10 |   489      492  16022      0 |   859      578  101809      0
16 11 |   250      258   8742      0 |   719      487  117899      0
16 12 |   198      202   3646      0 |   745      483  126709      0
16 13 |   119      119   1126      0 |   770      506  130423      0
16 14 |   291      327    246      0 |   770      531  131617      0
16 15 |   156      156     38      0 |   713      451  131931      0
16 16 |   125      139      7      0 |   895      631  132037      0
17  1 |   406      437     40      0 |     0        0       7      0
17  2 |   307      320    278      0 |     0        0      41      0
17  3 |   281      290   1366      0 |     0        3     313      0
17  4 |   307      317   4766      0 |    31       28    1673      0
17  5 |   354      378  12382      0 |    41       45    6433      0
17  6 |   583      582  24758      0 |   130      127   18809      0
17  7 |   839      859  38902      0 |   693      449   43873      0
17  8 |  1177     1183  48626      0 |   916      679   82847      0
17  9 |  1031     1054  48626      0 |  1270      944  131545      0
17 10 |   828      832  38902      0 |  1469     1105  180243      0
17 11 |   672      668  24758      0 |  1535     1114  219217      0
17 12 |   422      422  12382      0 |  1494      991  244047      0
17 13 |   474      482   4766      0 |  1615     1165  256501      0
17 14 |   599      607   1366      0 |  1500     1042  261339      0
17 15 |   223      218    278      0 |  1401     1065  262777      0
17 16 |   229      228     40      0 |  1390      918  263127      0
17 17 |   541      554      7      0 |  1562     1045  263239      0
18  1 |   401      405     42      0 |     0        0       7      0
18  2 |   401      397    312      0 |     0        0      43      0
18  3 |   458      493   1638      0 |     5        6     349      0
18  4 |   583      581   6126      0 |    16       13    1981      0
18  5 |   697      700  17142      0 |    83      130    8101      0
18  6 |   792      799  37134      0 |   156      162   25237      0
18  7 |  1672     1727  63654      0 |  1098      751   62693      0
18  8 |  1598     1601  87522      0 |  1416     1007  126423      0
18  9 |  1849     1893  97246      0 |  2051     1522  214021      0
18 10 |  1963     2083  87522      0 |  2734     2103  311343      0
18 11 |  1411     1428  63654      0 |  2849     2352  398941      0
18 12 |  1042     1048  37134      0 |  3021     2332  462671      0
18 13 |   942      985  17142      0 |  3036     2314  499881      0
18 14 |   656      666   6126      0 |  3052     2177  517099      0
18 15 |   526      532   1638      0 |  2910     2021  523301      0
18 16 |   614      621    312      0 |  3083     2108  525015      0
18 17 |   536      551     42      0 |  2921     2031  525403      0
18 18 |   682      680      7      0 |  3141     2098  525521      0
19  1 |   885      909     44      0 |     0        0       7      0
19  2 |  1411     1498    348      0 |     0        0      45      0
19  3 |   880      887   1944      0 |     5        4     387      0
19  4 |  1119     1139   7758      0 |    26       25    2325      0
19  5 |  1120     1127  23262      0 |    73       72   10077      0
19  6 |  1395     1462  54270      0 |   453      387   33591      0
19  7 |  1875     1929 100782      0 |  1197      838   87941      0
19  8 |  2656     2723 151170      0 |  2255     1616  188803      0
19  9 |  3046     3092 184762      0 |  3317     2568  340053      0
19 10 |  3635     3803 184762      0 |  5171     4041  524895      0
19 11 |  2739     2774 151170      0 |  5577     4574  709737      0
19 12 |  3203     3348 100782      0 |  6182     5194  860987      0
19 13 |  1672     1750  54270      0 |  6458     5561  961849      0
19 14 |  1760     1835  23262      0 |  6177     4964 1016199      0
19 15 |   968     1006   7758      0 |  6266     4331 1039541      0
19 16 |  1099     1134   1944      0 |  6208     4254 1047379      0
19 17 |   995     1037    348      0 |  6385     4366 1049403      0
19 18 |   916      964     44      0 |  6036     4268 1049831      0
19 19 |  1135     1138      7      0 |  6234     4320 1049955      0
20  1 |  1797     1821     46      0 |     0        0       7      0
20  2 |  2000     2029    386      0 |     0        0      47      0
20  3 |  2031     2071   2286      0 |    10        6     427      0
20  4 |  1942     2036   9696      0 |    31       34    2707      0
20  5 |  2104     2161  31014      0 |    88       85   12397      0
20  6 |  2880     2958  77526      0 |   860      554   43675      0
20  7 |  3791     3940 155046      0 |  2026     1405  121285      0
20  8 |  5130     5307 251946      0 |  3823     2731  276415      0
20  9 |  6547     6845 335926      0 |  5380     4148  528445      0
20 10 |  7119     7357 369518      0 |  8271     6685  864455      0
20 11 |  5692     5803 335926      0 |  9557     8029 1234057      0
20 12 |  4734     4850 251946      0 | 11114     9504 1570067      0
20 13 |  3604     3641 155046      0 | 11551    10434 1822097      0
20 14 |  2911     2999  77526      0 | 12317    10822 1977227      0
20 15 |  2115     2134  31014      0 | 12806    10679 2054837      0
20 16 |  2041     2095   9696      0 | 13062     9115 2085935      0
20 17 |  2390     2465   2286      0 | 12807     9002 2095715      0
20 18 |  1765     1788    386      0 | 12598     8601 2098085      0
20 19 |  2067     2143     46      0 | 12578     8626 2098555      0
20 20 |  1640     1663      7      0 | 12932     9064 2098685      0
21  1 |  3374     3425     48      0 |     0        0       7      0
21  2 |  4031     4157    426      0 |     0        1      49      0
21  3 |  3218     3250   2666      0 |    10        5     469      0
21  4 |  3687     3734  11976      0 |    21       25    3129      0
21  5 |  3692     3735  40704      0 |   115      114   15099      0
21  6 |  4859     4943 108534      0 |   963      661   56079      0
21  7 |  6114     6218 232566      0 |  2620     1880  164701      0
21  8 |  8573     8745 406986      0 |  4999     3693  397355      0
21  9 | 11880    12186 587866      0 |  9047     6863  804429      0
21 10 | 13255    13582 705438      0 | 14358    11436 1392383      0
21 11 | 13531    13807 705438      0 | 18823    15502 2097909      0
21 12 | 12244    12400 587866      0 | 21834    18760 2803435      0
21 13 |  9406     9528 406986      0 | 23771    21274 3391389      0
21 14 |  7114     7180 232566      0 | 26677    24296 3798463      0
21 15 |  4869     4961 108534      0 | 26479    23998 4031117      0
21 16 |  4416     4521  40704      0 | 26536    22976 4139739      0
21 17 |  4380     4443  11976      0 | 26490    19107 4180531      0
21 18 |  3265     3334   2666      0 | 25979    17995 4192595      0
21 19 |  3640     3768    426      0 | 26186    17891 4195349      0
21 20 |  3234     3295     48      0 | 25688    17653 4195863      0
21 21 |  3156     3219      7      0 | 26140    17838 4195999      0

一些動態SQL怎么樣?

DECLARE @k int = 5, @n INT

IF OBJECT_ID('tempdb..#set') IS NOT NULL DROP TABLE #set
CREATE TABLE #set ( [value] varchar(24) )
INSERT #set VALUES ('1'),('2'),('3'),('4'),('5'),('6')
SET @n = @@ROWCOUNT

SELECT dbo.Factorial(@n) / (dbo.Factorial(@k) * dbo.Factorial(@n - @k)) AS [expected combinations]

-- let's generate some sql.
DECLARE 
  @crlf     NCHAR(2) = NCHAR(13)+NCHAR(10)
, @sql      NVARCHAR(MAX)
, @select   NVARCHAR(MAX)
, @from     NVARCHAR(MAX)
, @order    NVARCHAR(MAX)
, @in       NVARCHAR(MAX)

DECLARE @j INT = 0
WHILE @j < @k BEGIN 
  SET @j += 1
  IF @j = 1 BEGIN
    SET @select = 'SELECT'+@crlf+'  _1.value AS [1]'
    SET @from   = @crlf+'FROM #set AS _1'
    SET @order  = 'ORDER BY _1.value'
    SET @in     = '[1]'
  END 
  ELSE BEGIN
    SET @select += @crlf+', _'+CONVERT(VARCHAR,@j)+'.value AS ['+CONVERT(VARCHAR,@j)+']'
    SET @from   += @crlf+'INNER JOIN #set AS _'+CONVERT(VARCHAR,@j)+' ON _'+CONVERT(VARCHAR,@j)+'.value > _'+CONVERT(VARCHAR,@j-1)+'.value'
    SET @order  += ', _'+CONVERT(VARCHAR,@j)+'.value' 
    SET @in     += ', ['+CONVERT(VARCHAR,@j)+']'
  END
END
SET @select += @crlf+', ROW_NUMBER() OVER ('+@order+') AS combination'
SET @sql = @select + @from

-- let's see how it looks
PRINT @sql
EXEC (@sql)

-- ok, now dump pivot and dump into a table for later use
IF OBJECT_ID('tempdb..#combinations') IS NOT NULL DROP TABLE #combinations
CREATE TABLE #combinations (
  combination INT
, value VARCHAR(24)
, PRIMARY KEY (combination, value)
)

SET @sql 
= 'WITH CTE AS ('+@crlf+@sql+@crlf+')'+@crlf
+ 'INSERT #combinations (combination, value)'+@crlf
+ 'SELECT combination, value FROM CTE a'+@crlf
+ 'UNPIVOT (value FOR position IN ('+@in+')) AS b'

PRINT @sql
EXEC (@sql)

SELECT COUNT(DISTINCT combination) AS [returned combinations] FROM #combinations
SELECT * FROM #combinations

為@k = 5 生成以下查詢:

SELECT
  _1.value AS [1]
, _2.value AS [2]
, _3.value AS [3]
, _4.value AS [4]
, _5.value AS [5]
, ROW_NUMBER() OVER (ORDER BY _1.value, _2.value, _3.value, _4.value, _5.value) AS combination
FROM #set AS _1
INNER JOIN #set AS _2 ON _2.value > _1.value
INNER JOIN #set AS _3 ON _3.value > _2.value
INNER JOIN #set AS _4 ON _4.value > _3.value
INNER JOIN #set AS _5 ON _5.value > _4.value

然后它取消旋轉並轉儲到表格中。

動態 SQL 很丑陋,您無法將其包裝在 UDF 中,但生成的查詢非常高效。

group by子句的CUBE擴展表示給定列表的所有組合。 例如,以下將給出 4 元素集的所有 3 組合。

select concat(a,b,c,d)
from (select 'a','b','c','d') as t(a,b,c,d)
group by cube(a,b,c,d)
having len(concat(a,b,c,d)) = 3

首先創建這個UDF...

CREATE FUNCTION [dbo].[_ex_fn_SplitToTable] (@str varchar(5000), @sep char(1) = null)                           

RETURNS @ReturnVal table (n int, s varchar(5000))                           

AS                          
/*                          
Alpha Test                          
-----------                         
select * from [dbo].[_ex_fn_SplitToTable_test01]('abcde','')                            
*/                          

BEGIN                           
    declare @str2 varchar(5000)                     
    declare @sep2 char(1)                       
    if LEN(ISNULL(@sep,'')) = 0                     
    begin                       
        declare @i int                  
        set @i = 0                  

        set @str2 = ''                  
        declare @char varchar(1)                    
        startloop:                  
            set @i += 1             
            --print @i              
            set @char = substring(@str,@i,1)                
            set @str2 = @str2 + @char + ','             
            if LEN(@str) <= @i              
                goto exitloop           
            goto startloop              
        exitloop:                   
        set @str2 = left(@str2,LEN(@str2) - 1)                  
        set @sep2 = ','                 
        --print @str2                   

    end                         
    else                        
    begin                       
        set @str2 = @str                    
        set @sep2 = @sep                    
    end                     

    ;WITH Pieces(n, start, stop) AS (                           

      SELECT 1, 1, CHARINDEX(@sep2, @str2)                          

      UNION ALL                             

      SELECT n + 1, stop + 1, CHARINDEX(@sep2, @str2, stop + 1)                             

      FROM Pieces                           

      WHERE stop > 0                            

    )                           

    insert into @ReturnVal(n,s)                             
    SELECT n,                           
      SUBSTRING(@str2, start, CASE WHEN stop > 0 THEN stop-start ELSE 5000 END) AS s                            
    FROM Pieces option (maxrecursion 32767)                             


    RETURN                          

END                         



GO                          

然后創建這個存儲過程...

CREATE proc [CombinationsOfString]                          
(                           
    @mystring varchar(max) = '0,5,10,15,20,25'                      
)                           
as                          

/*                          
ALPHA TEST                          
---------                           
exec CombinationsOfString '-20,-10,0,10,20'                         
*/                          
if object_id('tempdb..#_201606070947_myorig') is not null drop table #_201606070947_myorig                          

CREATE TABLE #_201606070947_myorig                          
 (                          
   SourceId  int      not null  identity(1,1)                           
  ,Element   varchar(100)  not null                         
 )                          
insert into #_201606070947_myorig                           
select s from dbo._ex_fn_SplitToTable(@mystring,',')                            

--select SourceId, Element from #_201606070947_myorig                           

declare @mynumerics varchar(max)                            

set @mynumerics = (                         
    select STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(SourceId)) AS 'data()'                      
    FROM #_201606070947_myorig                      
    FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands                      
)                           

set @mynumerics = REPLACE(@mynumerics,' ','')                           

print @mynumerics                           

if object_id('tempdb..#_201606070947_source') is not null drop table #_201606070947_source                          
if object_id('tempdb..#_201606070947_numbers') is not null drop table #_201606070947_numbers                            
if object_id('tempdb..#_201606070947_results') is not null drop table #_201606070947_results                            
if object_id('tempdb..#_201606070947_processed') is not null drop table #_201606070947_processed                            



CREATE TABLE #_201606070947_source                          
 (                          
   SourceId  int      not null  identity(1,1)                           
  ,Element   char(1)  not null                          
 )                          

--declare @mynumerics varchar(max)                          
--set @mynumerics = '1,2,3,4,5'                         
insert into #_201606070947_source                           
select s from dbo._ex_fn_SplitToTable(@mynumerics,',')                          

-- select * from #_201606070947_source                          

declare @Length int                         
set @Length = (select max(SourceId) from #_201606070947_source)                         
declare @columnstring varchar(max) = (SELECT REPLICATE('c.',@Length))                           
print @columnstring                         
declare @subs varchar(max) = (SELECT REPLICATE('substring.',@Length))                           
print @subs                         

if object_id('tempdb..#_201606070947_columns') is not null drop table #_201606070947_columns                            

select s+CONVERT(varchar,dbo.PadLeft(convert(varchar,n),'0',3)) cols                            
into #_201606070947_columns                         
from [dbo].[_ex_fn_SplitToTable](@columnstring,'.') where LEN(s) > 0                            

if object_id('tempdb..#_201606070947_subcolumns') is not null drop table #_201606070947_subcolumns                          
select s+'(Combo,'+CONVERT(varchar,n)+',1) ' + 'c'+CONVERT(varchar,dbo.PadLeft(convert(varchar,n),'0',3)) cols                          
into #_201606070947_subcolumns                          
from [dbo].[_ex_fn_SplitToTable](@subs,'.') where LEN(s) > 0                            

-- select * from #_201606070947_subcolumns                          
-- select * from #_201606070947_columns                         


declare @columns_sql varchar(max)                           
set @columns_sql =                          
        (                   
            select distinct                 
              stuff((SELECT distinct + cast(cols as varchar(50)) +  ' VARCHAR(1), '             
                       FROM (       
                        select cols 
                        from #_201606070947_columns     
                        ) t2    
                       --where t2.n = t1.n      
                       FOR XML PATH('')),3,0,'')        
            from (              
                select cols         
                from #_201606070947_columns             
                ) t1            
        )                   

declare @substring_sql varchar(max)                         
set @substring_sql =                            
        (                   
            select distinct                 
              stuff((SELECT distinct + cast(cols as varchar(100)) +  ', '               
                       FROM (       
                        select cols 
                        from #_201606070947_subcolumns  
                        ) t2    
                       --where t2.n = t1.n      
                       FOR XML PATH('')),3,0,'')        
            from (              
                select cols         
                from #_201606070947_subcolumns          
                ) t1            
        )                   
set @substring_sql = left(@substring_sql,LEN(@substring_sql) - 1)                           
print @substring_sql                            

set @columns_sql = LEFT(@columns_sql,LEN(@columns_sql) - 1)                         
--SELECT @columns_sql                           
declare @sql varchar(max)                           
set @sql = 'if object_id(''tempdb..##_201606070947_01'') is not null drop table ##_201606070947_01 create table ##_201606070947_01 (rowid int,' + @columns_sql + ')'                            
print @sql                          
execute(@sql)                           

CREATE TABLE #_201606070947_numbers (Number int not null)                           

insert into #_201606070947_numbers                          
select SourceId from #_201606070947_source                          

CREATE TABLE #_201606070947_results                         
 (                          
   Combo   varchar(10)  not null                            
  ,Length  int          not null                            
 )                          

 SET NOCOUNT on                         

DECLARE                         
  @Loop     int                         
 ,@MaxLoop  int                         


--  How many elements there are to process                          
SELECT @MaxLoop = max(SourceId)                         
 from #_201606070947_source                         


--  Initialize first value                          
TRUNCATE TABLE #_201606070947_results                           
INSERT #_201606070947_results (Combo, Length)                           
 select Element, 1                          
  from #_201606070947_source                            
  where SourceId = 1                            

SET @Loop = 2                           

--  Iterate to add each Element after the first                         
WHILE @Loop <= @MaxLoop                         
 BEGIN                          

    INSERT #_201606070947_results (Combo, Length)                       
     select distinct                        
        left(re.Combo, @Loop - nm.Number)                   
        + so.Element                    
        + RIGHT(re.Combo, nm.Number - 1)                    
       ,@Loop                       
      from #_201606070947_results re                        
       inner join #_201606070947_numbers nm                     
        on nm.Number <= @Loop                   
       inner join #_201606070947_source so                      
        on so.SourceId = @Loop                  
      where re.Length = @Loop - 1                       

    SET @Loop = @Loop + 1                       
 END                            
-- select * from #_201606070947_results                         
--  Show #_201606070947_results                         
SELECT *                            
into #_201606070947_processed                           
 from #_201606070947_results                            
 where Length = @MaxLoop                            
 order by Combo                         



-- select * from #_201606070947_processed                           
set @sql = 'if object_id(''tempdb..##_201606070947_02'') is not null drop table ##_201606070947_02 '                            
print @sql                          
execute(@sql)                           

set @sql = ' ' +                            
'  SELECT ROW_NUMBER() OVER(ORDER BY Combo Asc) AS RowID,' + @substring_sql +                           
'  into ##_201606070947_02 ' +                          
' FROM #_201606070947_processed ' +                         
' '                         
PRINT @sql                          
execute(@sql)                           

declare @columns_sql_new varchar(max)                           
set @columns_sql_new = REPLACE(@columns_sql,'(1)','(100)')                          
set @sql = 'if object_id(''tempdb..##_201606070947_03'') is not null drop table ##_201606070947_03 create table ##_201606070947_03 (RowId int,' + @columns_sql_new + ')'                            
PRINT @sql                          
execute(@sql)                           

insert into ##_201606070947_03 (RowId)                          
select RowId from ##_201606070947_02                            

--select * from ##_201606070947_03                          




DECLARE @ColumnId varchar(10)                           
DECLARE @getColumnId CURSOR                         
SET @getColumnId = CURSOR FOR                           
    select cols ColumnId from #_201606070947_columns                        
OPEN @getColumnId                           
FETCH NEXT                          
FROM @getColumnId INTO @ColumnId                            
WHILE @@FETCH_STATUS = 0                            
BEGIN                           
    PRINT @ColumnId                     
    set @sql = ' ' +                        
    ' update ##_201606070947_03                         
      set ' + @ColumnId + ' = B.Element                         
      from ##_201606070947_03 A                         
      , (                       

            select A.RowID, B.*             
            from                
            (               
                select * from ##_201606070947_02            
            ) A             
            ,               
            (               
                select * from #_201606070947_myorig         
            ) B             
            where A.' + @ColumnId + ' = B.SourceId              
        ) B                 
       where A.RowId = B.RowId                      
    '                       
    execute(@sql)                       
    print @sql                      

FETCH NEXT                          
FROM @getColumnId INTO @ColumnId                            
END                         
 CLOSE @getColumnId                         
DEALLOCATE @getColumnId                         

select * from ##_201606070947_03                            

我遇到了另一種計算組合的技術,它非常簡單。 在公開的 SQL 挑戰中,基於與我(當前)接受的答案相同的位模式技術,它也完全擊敗了我自己的嘗試。 雖然我承認我沒有玩過很長時間,或者從這里獲取我最好的解決方案並進行調整,但我又重新寫了一遍。

無論如何,我確信我已經把自己放在了一個釘子上——在這里我以為我找到了一些很酷的東西,但后來發現我的解決方案很容易被超越。 但令我驚訝的是,當我嘗試該技術時,它比上面最好的方法還要糟糕。 由於它易於實施且對小型作業具有合理的性能,因此在此處供參考(因此使其更出色,除非作業可能需要增加位模式技術之類的復雜性)。

給定一個表#Set,其行數與從中選擇的項目相同,變量@K 表示一次選取的項目數,這是該方法:

WITH Chains AS (
   SELECT
      Generation = 1,
      Chain = Convert(varchar(1000),'|' + Value + '|'),
      Value
   FROM
      #Set
   WHERE
      @K > 0
      AND value <= ALL (
         SELECT TOP (@K) Value
         FROM #Set
         ORDER BY Value DESC
      )
   UNION ALL
   SELECT
      C.Generation + 1,
      Convert(varchar(1000), C.Chain + S.Value + '|'),
      S.Value
   FROM
      Chains C
      INNER JOIN #Set S
         ON C.Value < S.Value
   WHERE
      C.Generation <= @K
)
SELECT
   C.Chain,
   S.Value
FROM
   Chains C
   INNER JOIN #Set S
      ON C.Chain LIKE '%|' + S.Value + '|%'
WHERE
   Generation = @K;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM