SQL Server如何用SQL實現一批字符串的全部組合


在SQL Server中,如何用SQL去實現得到一批字符串的全部組合呢?這個是同事在實際需求當中遇到的一個問題,他的具體需求如下所示:

 

傳入參數格式為'1,2,3,224,15,6'   'A,BC,GHT,TTY,B,E'

 

輸出的內容為分割后字符串的所有非排列組合

!-階乘,如!=5××××=120  (M!/(N!*((M-N)!)))  

 

公式描述:組合數公式是從m個不同元素中,任取n(n≤m)個元素並成一組,叫做從m個不同元素中取出n個元素的一個組合;

從m個不同元素中取出n(n≤m)個元素的所有組合的個數,叫做從m個不同元素中取出n個元素的組合數。用符號c(m,n) 表示。

如果有5個數字那么就是M=5  單個數字組合N=1   (M!/(N!*((M-N)!)))=5   

                    是M=5  2個數字組合N=2    (M!/(N!*((M-N)!)))=10

                    是M=5  3個數字組合N=3    (M!/(N!*((M-N)!)))=10

                    是M=5  4個數字組合N=4    (M!/(N!*((M-N)!)))=5

                    是M=5  5個數字組合N=5    (M!/(N!*((M-N)!)))=1   

可能全部的組合有 10 + 10 + 5+ 5 + 1 =31 種。傳入的參數分割后越多,組合數也就越龐大。

 

             

他最開始的實現方式就是用多層循環實現(此處就不貼代碼了),但是當要實現組合的字符串數量增多時,效率性能就下降得非常厲害,后面我參考一種寫法寫了下面SQL語句。

--創建輔助表SEQ_NUMBER
CREATE TABLE SEQ_NUMBER(COL_NUM INT);
DECLARE @Index INT =1;
 
WHILE @Index <=32
BEGIN
    INSERT INTO SEQ_NUMBER VALUES(@Index);
 
    SET @Index +=1;
END;
GO
 
 
--創建輔助表,用於保存拆分后的字符串
CREATE TABLE SplitString(COL_NUM INT IDENTITY(1,1) ,VAL  VARCHAR(32));
 
 
--創建函數
CREATE FUNCTION FN_GET_COMBINATIONS()
RETURNS @OutTable TABLE(COL VARCHAR(32), VAL  VARCHAR(32))
AS
BEGIN
    DECLARE @Str  VARCHAR(32)='';
    DECLARE @Index INT =1;
    DECLARE @RowCount INT;
 
    SELECT @RowCount=COUNT(*) FROM SplitString;
 
    
    --注意,如果字符串分隔后有5個字符串(A,BC,GHT,TTY,B,E),就使用12345, 如果分割后有6個字符串,就必須用123456,以此類推
    WHILE @Index <= @RowCount
        BEGIN
 
            SET @Str = @Str + CAST(@Index AS VARCHAR(2))
            SET @Index = @Index +1
        END
 
    INSERT INTO @OutTable
    SELECT   S.COL_NUM, T.VAL FROM SEQ_NUMBER S , SplitString T
    WHERE S.COL_NUM = T.COL_NUM ORDER BY T.COL_NUM;
 
    WHILE NOT EXISTS(SELECT 1 FROM @OutTable where COL = @Str)
    INSERT INTO @OutTable
    SELECT T3.COL + T2.COL, T3.VAL + T2.VAL FROM @OutTable AS T2,@OutTable AS T3 WHERE len(t3.col) = 1 and charindex(T3.COL,T2.COL) =0 and T2.COL > T3.COL
 
    RETURN;
END

 

然后假如,我們需要實現字符串'1,2,3,4,5'中1,2,3,4,5的所有組合方式,那么我們用下面SQL就能得到結果。如下所示,當然你也可以用諸如'A,BC,GHT,TTY,B,E'這樣的字符串去獲取字符串的所有組合。

 

DECLARE @String VARCHAR(200)
DECLARE @SqlText VARCHAR(MAX)
DECLARE @Index INT=1;
 
SET @String='A,BC,GHT,TTY,B';
SET @SqlText='SELECT COL='''+ REPLACE(@STRING,',',''' UNION ALL SELECT ''')+''''
 
 
--清空舊數據,保存需要進行組合的字符串數據。
TRUNCATE TABLE SplitString;
 
 
INSERT INTO SplitString
EXEC (@SqlText)
 
 
 
 
SELECT DISTINCT
        CHAR_VAL ,
        COL_NUM ,LEN(COL_NUM) AS STR_CNT 
FROM    dbo.FN_GET_COMBINATIONS()
WHERE   LEN(COL_NUM) >= 1
ORDER BY LEN(COL_NUM) ,COL_NUM;

clipboard

 

后面測試發現,當組合的字符串數量超過或等於10個時,這個函數就有問題了(有興趣的可以自行測試,例如傳入的參數為’1,2,3,4,5,6,7,8,9,10’)。所以又對這個進行了一番修改。目前最多支持獲取26個字符串的全部組合,這個已經完全滿足業務需要了。如果再需要跟多的字符串組合,則還需修改函數。這個函數效率也是與需要組合的字符串個數有相關,如果組合11個字符串,基本上需要3~4秒的時間,如果組合的字符串個數越多,則所需時間越多。當然,如果組合的字符串個數7~8個,幾乎就是1秒內。

 

CREATE TABLE SEQ_CHARACTER(COL_NUM INT ,COL_CHAR VARCHAR(36));
 
INSERT INTO SEQ_CHARACTER
SELECT 1 ,'A' UNION ALL
SELECT 2 ,'B' UNION ALL
SELECT 3 ,'C' UNION ALL
SELECT 4 ,'D' UNION ALL
SELECT 5 ,'E' UNION ALL
SELECT 6 ,'F' UNION ALL
SELECT 7 ,'G' UNION ALL
SELECT 8 ,'H' UNION ALL
SELECT 9 ,'I' UNION ALL
SELECT 10,'J' UNION ALL
SELECT 11,'K' UNION ALL
SELECT 12,'L' UNION ALL
SELECT 13,'M' UNION ALL
SELECT 14,'N' UNION ALL
SELECT 15,'O' UNION ALL
SELECT 16,'P' UNION ALL
SELECT 17,'Q' UNION ALL
SELECT 18,'R' UNION ALL
SELECT 19,'S' UNION ALL
SELECT 20,'T' UNION ALL
SELECT 21,'U' UNION ALL
SELECT 22,'V' UNION ALL
SELECT 23,'W' UNION ALL
SELECT 24,'X' UNION ALL
SELECT 25,'Y' UNION ALL
SELECT 26,'Z'
 
 
CREATE TABLE SplitString(COL_NUM INT IDENTITY(1,1) ,VAL  VARCHAR(32));
 
 
 
CREATE FUNCTION FN_GET_COMBINATIONS()
RETURNS @OutTable TABLE(COL_NUM VARCHAR(32), CHAR_VAL  VARCHAR(32))
AS
BEGIN
    DECLARE @Str  VARCHAR(32)='';
    DECLARE @Index INT =1;
    DECLARE @RowCount INT;
 
    SELECT @RowCount=COUNT(*) FROM SplitString;
 
    
    --注意,如果字符串分隔后有5個字符串(A,BC,GHT,TTY,B,E),就使用12345, 如果分割后有6個字符串,就必須用123456,以此類推
    WHILE @Index <= @RowCount
        BEGIN
 
            SELECT  @Str = @Str + LTRIM(RTRIM(COL_CHAR)) FROM SEQ_CHARACTER WHERE COL_NUM=@Index
            SET @Index = @Index +1
        END
 
    INSERT INTO @OutTable
    SELECT   S.COL_CHAR, T.VAL FROM SEQ_CHARACTER S , SplitString T
    WHERE S.COL_NUM = T.COL_NUM ORDER BY T.COL_NUM;
 
    WHILE NOT EXISTS(SELECT 1 FROM @OutTable where COL_NUM = @Str)
    INSERT INTO @OutTable
    SELECT T3.COL_NUM + T2.COL_NUM, T3.CHAR_VAL + T2.CHAR_VAL FROM @OutTable AS T2,@OutTable AS T3 WHERE len(T3.COL_NUM) = 1 and charindex(T3.COL_NUM,T2.COL_NUM) =0 and T2.COL_NUM > T3.COL_NUM
 
    RETURN;
END

 

測試腳本如下:

 

DECLARE @String VARCHAR(200)
DECLARE @SqlText VARCHAR(MAX)
DECLARE @Index INT=1;
 
SET @String='A,B,C,D,E,F,G,H,G,H,I';
SET @SqlText='SELECT COL='''+ REPLACE(@STRING,',',''' UNION ALL SELECT ''')+''''
 
 
--清空舊數據,保存需要進行組合的字符串數據。
TRUNCATE TABLE SplitString;
 
 
INSERT INTO SplitString
EXEC (@SqlText)
 
 
 
 
SELECT DISTINCT
        CHAR_VAL ,
        COL_NUM ,LEN(COL_NUM) AS STR_CNT 
FROM    dbo.FN_GET_COMBINATIONS()
WHERE   LEN(COL_NUM) >= 1
ORDER BY LEN(COL_NUM) ,COL_NUM;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM