【问题标题】:T-SQL "Dynamic" JoinT-SQL“动态”连接
【发布时间】:2013-09-17 19:55:21
【问题描述】:

给定以下带有单个 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 中即时执行此操作吗?

编辑:理想情况下,我希望这发生在单个存储过程中,但如果我必须使用另一个过程或一些用户定义的函数来实现这一点,我很好。

【问题讨论】:

  • 如果有 5 个值,您希望添加这 5 个数字的所有版本,还是最多添加 3 个?
  • 是的,我想要所有可能的组合,没有最大值。
  • 只有不同的值?
  • @GoatCO 是的,只有不同的值。
  • 这家伙似乎在做你正在寻找的事情,本质上是:stackoverflow.com/questions/3686062/…

标签: sql sql-server tsql permutation common-table-expression


【解决方案1】:

此 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 语句用于定义recursive common table expression。实际上,WITH 语句循环多次执行UNION,直到递归完成。

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

    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

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

    Result
    ------
    A+B+C
    A+C+B
    B+A+C
    B+C+A
    C+A+B
    C+B+A

【讨论】:

  • 哇,这太酷了。它到底是如何工作的?看起来像魔术。
  • 我现在正在检查这个,它似乎正在做我想要的:) 我会尽快报告......
  • 我只是在玩这个...不错:)
  • 说真的@David 你能用这个巫毒教的解释更新答案吗?
  • 我添加了一些示例来配合@bhamby 的解释。
【解决方案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;

【讨论】:

  • 如果是 1,2,3,4 怎么办?
  • 没错,这就是我卡住的地方:(
  • @DaveZiegler 。 . .查看修改后的答案。
猜你喜欢
  • 1970-01-01
  • 2010-12-24
  • 1970-01-01
  • 2022-11-17
  • 2013-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多