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

如果MyTable包含 9 行,则需要一些时间来计算,但它会返回 362,880 行。

更新说明:

WITH语句用于定义递归公用表表达式。实际上,该WITH语句循环多次执行 aUNION直到递归完成。

SQL 的第一部分设置起始记录。假设 3 行名为“A”、“B”和“C” 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不会再创建任何行,因为CHARINDEX将永远是0

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 回答