7

単一の char(1) 列を持つ次の SQL Server テーブルがあるとします。

Value
------
'1'
'2'
'3'

T-SQL で次の結果を取得するにはどうすればよいですか?

Result
------
'1+2+3'
'1+3+2'
'2+1+3'
'2+3+1'
'3+2+1'
'3+1+2'

これも動的である必要があるため、テーブルが行「1」と「2」のみを保持している場合、次のようになります。

Result
------
'1+2'
'2+1'

CROSS JOIN を使えばできそうなのですが、何行になるかは事前にわからないので、自分で CROSS JOIN を何回するかわかりません..?

SELECT a.Value + '+' + b.Value
FROM MyTable a
CROSS JOIN MyTable b
WHERE a.Value <> b.Value

常に 10 未満 (実際には 1 ~ 3 程度) の行が存在します。これを SQL Server でオンザフライで実行できますか?

編集: 理想的には、これを単一のストアド プロシージャで実行したいのですが、別のプロシージャまたはユーザー定義関数を使用してこれを実行する必要がある場合は、それで問題ありません。

4

2 に答える 2

12

この SQL は、繰り返しなしで順列を計算します。

WITH recurse(Result, Depth) AS
(
    SELECT CAST(Value AS VarChar(100)), 1
    FROM MyTable

    UNION ALL

    SELECT CAST(r.Result + '+' + a.Value AS VarChar(100)), r.Depth + 1
    FROM MyTable a
    INNER JOIN recurse r
    ON CHARINDEX(a.Value, r.Result) = 0
)

SELECT Result
FROM recurse
WHERE Depth = (SELECT COUNT(*) FROM MyTable)
ORDER BY Result

9 行が含まれている場合MyTable、計算に時間がかかりますが、362,880 行が返されます。

説明付きで更新:

このWITHステートメントは、再帰的な共通テーブル式を定義するために使用されます。実際には、ステートメントは、再帰が終了WITHするまで a を実行して複数回ループしています。UNION

SQL の最初の部分は、開始レコードを設定します。に「A」、「B」、「C」という名前の 3 つの行があると仮定すると、MyTable次の行が生成されます。

    Result     Depth
    ------     -----
    A          1
    B          1
    C          1

次に、SQL の次のブロックが最初のレベルの再帰を実行します。

    SELECT CAST(r.Result + '+' + a.Value AS VarChar(100)), r.Depth + 1
    FROM MyTable a
    INNER JOIN recurse r
    ON CHARINDEX(a.Value, r.Result) = 0

これにより、これまでに生成されたすべてのレコード (recurseテーブルに表示されます) が取得され、それらが再びすべてのレコードに結合されMyTableます。このON句は、レコードのリストをフィルタリングしMyTableて、この行の順列にまだ存在しないレコードのみを返します。これにより、次の行が生成されます。

    Result     Depth
    ------     -----
    A          1
    B          1
    C          1
    A+B        2
    A+C        2
    B+A        2
    B+C        2
    C+A        2
    C+B        2

次に、再帰ループが再び繰り返され、次の行が返されます。

    Result     Depth
    ------     -----
    A          1
    B          1
    C          1
    A+B        2
    A+C        2
    B+A        2
    B+C        2
    C+A        2
    C+B        2
    A+B+C      3
    A+C+B      3
    B+A+C      3
    B+C+A      3
    C+A+B      3
    C+B+A      3

この時点で、は常に であるUNIONため、 はこれ以上行を作成しないため、再帰は停止します。CHARINDEX0

Depth最後の SQL は、計算列が のレコード数と一致するすべての結果行をフィルター処理しますMyTable。これにより、再帰の最後の深さによって生成された行を除くすべての行が破棄されます。したがって、最終結果は次の行になります。

    Result
    ------
    A+B+C
    A+C+B
    B+A+C
    B+C+A
    C+A+B
    C+B+A
于 2013-09-17T20:20:26.743 に答える
2

再帰的な CTE でこれを行うことができます。

with t as (
      select 'a' as value union all
      select 'b' union all
      select 'c'
     ),
     const as (select count(*) as cnt from t),
     cte as (
      select cast(value as varchar(max)) as value, 1 as level
      from t
      union all
      select cte.value + '+' + t.value, 1 + level
      from cte join
           t 
           on '+'+cte.value+'+' not like '%+'+t.value+'+%' cross join
           const
      where level <= const.cnt
     )
select cte.value
from cte cross join
     const
where level = const.cnt;
于 2013-09-17T20:01:54.940 に答える