为 SQL 中的每个字符生成所有值组合和值集列表
Generate all combinations of values with set list of values for each character in SQL
我有一个看起来像这样的数据集
Position
Value
1
1
1
2
1
3
2
8
3
5
3
6
我想生成字符串的所有组合以及每个位置的值。
对于这个例子,输出看起来像
Output
185
285
385
186
286
386
顺序并不特别重要。
注意:可以有任意数量的组和每个组的值。
下面SQL设置示例输入。
Declare @Table Table
(
groupId int,
value int
)
Insert Into @Table
Select 1,1
union select 1,2
union select 1,3
union select 2,8
union select 3,5
union select 3,6
Select
*
From
@Table
由于您不知道组数,我想您需要使用动态 SQL。
例如:
DROP TABLE IF EXISTS #TestGroup;
CREATE TABLE #TestGroup
(
GroupId int,
Value int
);
INSERT INTO #TestGroup
VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 8),
(3, 5),
(3, 6)
;
DECLARE @MaxGroup int;
SELECT @MaxGroup = MAX(GroupId) FROM #TestGroup;
DECLARE @sql nvarchar(max) = N'SELECT ';
DECLARE @i int = 1;
WHILE @i <= @MaxGroup
BEGIN
If @i <> 1 SET @sql = @sql + N', ';
SET @sql = @sql + FORMATMESSAGE(N'T%i.Value As C%i', @i, @i);
SET @i = @i + 1;
END;
SET @sql = @sql + N' FROM ';
SET @i = 1;
WHILE @i <= @MaxGroup
BEGIN
If @i <> 1 SET @sql = @sql + N' CROSS JOIN ';
SET @sql = @sql + FORMATMESSAGE(' (SELECT Value FROM #TestGroup WHERE GroupId = %i) As T%i ', @i, @i);
SET @i = @i + 1;
END;
PRINT @sql;
EXEC(@sql);
DROP TABLE IF EXISTS #TestGroup;
输出:
SELECT T1.Value As C1, T2.Value As C2, T3.Value As C3
FROM (SELECT Value FROM #TestGroup WHERE GroupId = 1) As T1
CROSS JOIN (SELECT Value FROM #TestGroup WHERE GroupId = 2) As T2
CROSS JOIN (SELECT Value FROM #TestGroup WHERE GroupId = 3) As T3
C1
C2
C3
1
8
5
2
8
5
3
8
5
1
8
6
2
8
6
3
8
6
我认为这里真正的问题是你有任意数量的组。使用静态数量的组,您可以执行以下操作:
SELECT CONCAT(YT1.[Value],YT2.[Value],YT3.[Value])
FROM dbo.YourTable YT1
JOIN dbo.YourTable YT2 ON YT1.Position < YT2.Position
JOIN dbo.YourTable YT3 ON YT2.Position < YT3.Position;
因此,一种方法是使用动态解决方案。请注意,在没有详细说明您使用的 SQL 服务器版本的情况下,我 假设 您使用的是完全受支持的版本。我还假设你不能有差距。例如,如果 position
5
被省略,但 position
6
不是,那么您不希望 0
代表第 5 位(并且position
6
将改为第 5 位)。
这给了你这个,老实说,这很乱,但确实有用:
DECLARE @SQL nvarchar(MAX),
@CRLF nchar(2) = NCHAR(13) + NCHAR(10);
WITH DisctinctPositions AS(
SELECT DISTINCT
Position
FROM dbo.YourTable),
Positions AS(
SELECT Position,
LAG(Position) OVER (ORDER BY Position) AS PrevPosition
FROM DisctinctPositions)
SELECT @SQL = N'SELECT CONCAT(' + STRING_AGG(QUOTENAME(CONCAT(N'YT',P.Position)) + N'.[Value]',',') WITHIN GROUP (ORDER BY P.Position) + N')' + @CRLF +
N'FROM dbo.YourTable YT1' + @CRLF +
STRING_AGG(CASE P.Position WHEN 1 THEN NULL ELSE N' JOIN dbo.YourTable' + QUOTENAME(CONCAT(N'YT',P.Position)) + N' ON ' + QUOTENAME(CONCAT(N'YT',P.PrevPosition)) + N'.Position < ' + QUOTENAME(CONCAT(N'YT',P.Position)) + N'.Position' END, @CRLF) WITHIN GROUP (ORDER BY P.Position)
FROM Positions P;
--PRINT @SQL; Your best friend
EXEC sys.sp_executesql @SQL;
您可以为此使用递归 CTE,不需要动态 SQL
WITH cte AS (
SELECT
1 AS Position,
CAST(Value AS varchar(100)) AS Value
FROM #YourTable t
WHERE Position = 1
UNION ALL
SELECT
cte.Position + 1,
CAST(cte.Value + t.Value AS varchar(100))
FROM #YourTable t
JOIN cte ON cte.Position + 1 = t.Position
)
SELECT *
FROM cte
WHERE cte.Position = (SELECT TOP 1 t2.Position FROM #YourTable t2 ORDER BY t2.Position DESC);
如果存在差距,那么您需要更复杂的解决方案:
CREATE FUNCTION dbo.GetNextValues (@gtThanPosition int)
RETURNS TABLE AS RETURN
SELECT TOP (1) WITH TIES
t.Position,
t.Value
FROM YourTable t
WHERE t.Position > @gtThanPosition OR @gtThanPosition IS NULL
ORDER BY t.Position;
GO
WITH cte AS (
SELECT
t.Position,
CAST(Value AS varchar(100)) AS Value
FROM dbo.GetNextValues(NULL) t
UNION ALL
SELECT
t.Position,
CAST(cte.Value + t.Value AS varchar(100)) AS Value
FROM cte
CROSS APPLY dbo.GetNextValues(cte.Position) t
)
SELECT Value
FROM cte
WHERE cte.Position = (SELECT TOP 1 t2.Position FROM YourTable t2 ORDER BY t2.Position DESC);
你可以使用递归cte
:
with cte(c, s) as (
select 2, cast(value AS varchar(100)) from vals where position = 1
union all
select c.c+1, cast(concat(c.s,v.value) as varchar(100)) from cte c join vals v on c.c = v.position
)
select c.s from cte c where c.c = (select top 1 v1.position from vals v1 order by v1.position desc) + 1
我有一个看起来像这样的数据集
Position | Value |
---|---|
1 | 1 |
1 | 2 |
1 | 3 |
2 | 8 |
3 | 5 |
3 | 6 |
我想生成字符串的所有组合以及每个位置的值。
对于这个例子,输出看起来像
Output |
---|
185 |
285 |
385 |
186 |
286 |
386 |
顺序并不特别重要。
注意:可以有任意数量的组和每个组的值。
下面SQL设置示例输入。
Declare @Table Table
(
groupId int,
value int
)
Insert Into @Table
Select 1,1
union select 1,2
union select 1,3
union select 2,8
union select 3,5
union select 3,6
Select
*
From
@Table
由于您不知道组数,我想您需要使用动态 SQL。
例如:
DROP TABLE IF EXISTS #TestGroup;
CREATE TABLE #TestGroup
(
GroupId int,
Value int
);
INSERT INTO #TestGroup
VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 8),
(3, 5),
(3, 6)
;
DECLARE @MaxGroup int;
SELECT @MaxGroup = MAX(GroupId) FROM #TestGroup;
DECLARE @sql nvarchar(max) = N'SELECT ';
DECLARE @i int = 1;
WHILE @i <= @MaxGroup
BEGIN
If @i <> 1 SET @sql = @sql + N', ';
SET @sql = @sql + FORMATMESSAGE(N'T%i.Value As C%i', @i, @i);
SET @i = @i + 1;
END;
SET @sql = @sql + N' FROM ';
SET @i = 1;
WHILE @i <= @MaxGroup
BEGIN
If @i <> 1 SET @sql = @sql + N' CROSS JOIN ';
SET @sql = @sql + FORMATMESSAGE(' (SELECT Value FROM #TestGroup WHERE GroupId = %i) As T%i ', @i, @i);
SET @i = @i + 1;
END;
PRINT @sql;
EXEC(@sql);
DROP TABLE IF EXISTS #TestGroup;
输出:
SELECT T1.Value As C1, T2.Value As C2, T3.Value As C3
FROM (SELECT Value FROM #TestGroup WHERE GroupId = 1) As T1
CROSS JOIN (SELECT Value FROM #TestGroup WHERE GroupId = 2) As T2
CROSS JOIN (SELECT Value FROM #TestGroup WHERE GroupId = 3) As T3
C1 | C2 | C3 |
---|---|---|
1 | 8 | 5 |
2 | 8 | 5 |
3 | 8 | 5 |
1 | 8 | 6 |
2 | 8 | 6 |
3 | 8 | 6 |
我认为这里真正的问题是你有任意数量的组。使用静态数量的组,您可以执行以下操作:
SELECT CONCAT(YT1.[Value],YT2.[Value],YT3.[Value])
FROM dbo.YourTable YT1
JOIN dbo.YourTable YT2 ON YT1.Position < YT2.Position
JOIN dbo.YourTable YT3 ON YT2.Position < YT3.Position;
因此,一种方法是使用动态解决方案。请注意,在没有详细说明您使用的 SQL 服务器版本的情况下,我 假设 您使用的是完全受支持的版本。我还假设你不能有差距。例如,如果 position
5
被省略,但 position
6
不是,那么您不希望 0
代表第 5 位(并且position
6
将改为第 5 位)。
这给了你这个,老实说,这很乱,但确实有用:
DECLARE @SQL nvarchar(MAX),
@CRLF nchar(2) = NCHAR(13) + NCHAR(10);
WITH DisctinctPositions AS(
SELECT DISTINCT
Position
FROM dbo.YourTable),
Positions AS(
SELECT Position,
LAG(Position) OVER (ORDER BY Position) AS PrevPosition
FROM DisctinctPositions)
SELECT @SQL = N'SELECT CONCAT(' + STRING_AGG(QUOTENAME(CONCAT(N'YT',P.Position)) + N'.[Value]',',') WITHIN GROUP (ORDER BY P.Position) + N')' + @CRLF +
N'FROM dbo.YourTable YT1' + @CRLF +
STRING_AGG(CASE P.Position WHEN 1 THEN NULL ELSE N' JOIN dbo.YourTable' + QUOTENAME(CONCAT(N'YT',P.Position)) + N' ON ' + QUOTENAME(CONCAT(N'YT',P.PrevPosition)) + N'.Position < ' + QUOTENAME(CONCAT(N'YT',P.Position)) + N'.Position' END, @CRLF) WITHIN GROUP (ORDER BY P.Position)
FROM Positions P;
--PRINT @SQL; Your best friend
EXEC sys.sp_executesql @SQL;
您可以为此使用递归 CTE,不需要动态 SQL
WITH cte AS (
SELECT
1 AS Position,
CAST(Value AS varchar(100)) AS Value
FROM #YourTable t
WHERE Position = 1
UNION ALL
SELECT
cte.Position + 1,
CAST(cte.Value + t.Value AS varchar(100))
FROM #YourTable t
JOIN cte ON cte.Position + 1 = t.Position
)
SELECT *
FROM cte
WHERE cte.Position = (SELECT TOP 1 t2.Position FROM #YourTable t2 ORDER BY t2.Position DESC);
如果存在差距,那么您需要更复杂的解决方案:
CREATE FUNCTION dbo.GetNextValues (@gtThanPosition int)
RETURNS TABLE AS RETURN
SELECT TOP (1) WITH TIES
t.Position,
t.Value
FROM YourTable t
WHERE t.Position > @gtThanPosition OR @gtThanPosition IS NULL
ORDER BY t.Position;
GO
WITH cte AS (
SELECT
t.Position,
CAST(Value AS varchar(100)) AS Value
FROM dbo.GetNextValues(NULL) t
UNION ALL
SELECT
t.Position,
CAST(cte.Value + t.Value AS varchar(100)) AS Value
FROM cte
CROSS APPLY dbo.GetNextValues(cte.Position) t
)
SELECT Value
FROM cte
WHERE cte.Position = (SELECT TOP 1 t2.Position FROM YourTable t2 ORDER BY t2.Position DESC);
你可以使用递归cte
:
with cte(c, s) as (
select 2, cast(value AS varchar(100)) from vals where position = 1
union all
select c.c+1, cast(concat(c.s,v.value) as varchar(100)) from cte c join vals v on c.c = v.position
)
select c.s from cte c where c.c = (select top 1 v1.position from vals v1 order by v1.position desc) + 1