T-SQL“动态”联接

T-SQL“动态”联接,sql,sql-server,tsql,permutation,common-table-expression,Sql,Sql Server,Tsql,Permutation,Common Table Expression,给定具有单个char1列的以下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' 看起来我应该能够使用交叉连接来完成这项工作,但由于我不知道提前会有多少行,我不确定自己要交叉连接多少次 SELEC

给定具有单个char1列的以下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'
看起来我应该能够使用交叉连接来完成这项工作,但由于我不知道提前会有多少行,我不确定自己要交叉连接多少次

SELECT a.Value + '+' + b.Value
FROM MyTable a
CROSS JOIN MyTable b
WHERE a.Value <> b.Value
在任何给定的时间,总有少于10行,实际上更像1-3行。我可以在SQL Server中动态执行此操作吗


编辑:理想情况下,我希望在单个存储过程中实现这一点,但如果我必须使用另一个过程或某些用户定义的函数来实现这一点,我对此没有意见。

您可以使用递归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;

此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行,则需要一些时间进行计算,但它将返回362880行

更新并解释:

WITH语句用于定义。实际上,WITH语句在执行并集时循环多次,直到递归完成

SQL的第一部分设置起始记录。假设MyTable中有3行名为“A”、“B”和“C”,这将生成以下行:

    Result     Depth
    ------     -----
    A          1
    B          1
    C          1
    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
    Result
    ------
    A+B+C
    A+C+B
    B+A+C
    B+C+A
    C+A+B
    C+B+A
然后,下一个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
这将获取到目前为止生成的所有记录,这些记录将位于递归表中,并再次将它们连接到MyTable中的所有记录。ON子句过滤MyTable中的记录列表,只返回此行排列中不存在的记录。这将产生以下行:

    Result     Depth
    ------     -----
    A          1
    B          1
    C          1
    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
    Result
    ------
    A+B+C
    A+C+B
    B+A+C
    B+C+A
    C+A+B
    C+B+A
然后递归循环再次给出以下行:

    Result     Depth
    ------     -----
    A          1
    B          1
    C          1
    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
    Result
    ------
    A+B+C
    A+C+B
    B+A+C
    B+C+A
    C+A+B
    C+B+A
此时,递归停止,因为UNION不会创建更多的行,因为CHARINDEX将始终为0

最后一个SQL过滤计算出的深度列与MyTable中的记录数匹配的所有结果行。这将抛出除最后一次递归深度生成的行之外的所有行。因此,最终结果将是这些行:

    Result     Depth
    ------     -----
    A          1
    B          1
    C          1
    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
    Result
    ------
    A+B+C
    A+C+B
    B+A+C
    B+C+A
    C+A+B
    C+B+A

如果是1,2,3,4呢?没错,那就是我被卡住的地方:@DaveZiegler。请看修改后的答案。如果有5个值,你希望这5个数字的所有版本都被添加,还是最多3个?是的,我希望所有可能的组合,没有最大值。只有不同的值?@GoatCO是的,只有不同的值。这家伙似乎在做你想做的事情,本质上:哇,这太酷了。它到底是怎么工作的?看起来很神奇。我现在正在检查,它似乎在做我想做的事情:我很快会回来的…我只是在摆弄这个。。。不错的一个:说真的,@David,你能用这个巫毒的解释来更新答案吗?我已经添加了一些例子来支持@bhamby的解释。