给定以下带有单个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中即时执行此操作吗?
编辑:理想情况下,我希望在单个存储过程中发生这种情况,但如果我必须使用另一个proc或一些用户定义的函数来解决这个问题,我就可以了。
答案 0 :(得分: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
语句用于定义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
答案 1 :(得分: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;