我需要将一个字符串中的字符串拆分为一个字符,每个字符都放在SQL Server 2012中它自己的列中。
示例:如果我的列有'ABCDE'
,我需要将其拆分为'A'
,'B'
,'C'
,'D'
,{{1将每个都放入自己的列中。
要拆分的列的长度可能会有所不同,因此我需要尽可能保持动态。
我的问题与其他帖子(Can Mysql Split a column?)不同,因为我的帖子没有任何分隔符。 感谢
答案 0 :(得分:1)
我正在将问题解释为将字符放入一列(“将一列中的字符串拆分为一个字符,每个字符放入其自己的列中”)。但是,我意识到这可能是模棱两可的。
一种方法是使用递归CTE:
with chars as (
select left(val, 1) as c, substring(val, 2, len(val)) as rest
from (select 'ABCDE' as val union all select '123') t
union all
select left(rest, 1), substring(rest, 2, len(rest))
from chars
where rest <> ''
)
select c
from chars;
只需在子查询中插入表和列即可。请注意,您可能还想包含其他列。
Here是一个SQL小提琴。
如果您想要多个列和,则数字不固定,那么您将需要 动态SQL。
答案 1 :(得分:1)
你可以这样做:
DECLARE @t TABLE(id int, n VARCHAR(50))
INSERT INTO @t VALUES
(1, 'ABCDEF'),
(2, 'EFGHIJKLMNOPQ')
;WITH cte AS
(SELECT id, n, SUBSTRING(n, 1, 1) c, 1 AS ind FROM @t
UNION ALL
SELECT id, n, SUBSTRING(n, ind + 1, 1), ind + 1 FROM cte WHERE LEN(n) > ind
)
SELECT *
FROM cte
PIVOT (MAX(c) FOR ind IN([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[12],[13],[14],[15])) p
输出:
id n 1 2 3 4 5 6 7 8 9 10 12 13 14 15
1 ABCDEF A B C D E F NULL NULL NULL NULL NULL NULL NULL NULL
2 EFGHIJKLMNOPQ E F G H I J K L M N P Q NULL NULL
这是动态版本:
DECLARE @l INT, @c VARCHAR(MAX) = ''
SELECT @l = MAX(LEN(n)) FROM PivotTable
WHILE @l > 0
BEGIN
SET @c = ',[' + CAST(@l AS VARCHAR(MAX)) + ']' + @c
SET @l = @l - 1
END
SET @c = STUFF(@c, 1, 1,'')
DECLARE @s NVARCHAR(MAX) = '
;WITH cte AS
(SELECT id, n, SUBSTRING(n, 1, 1) c, 1 AS ind FROM PivotTable
UNION ALL
SELECT id, n, SUBSTRING(n, ind + 1, 1), ind + 1 FROM cte WHERE LEN(n) > ind
)
SELECT *
FROM cte
PIVOT (MAX(c) FOR ind IN(' + @c + ')) p'
EXEC (@s)
答案 2 :(得分:1)
如果您想为每个角色添加一个新列:
SELECT [1] = SUBSTRING(Col, 1, 1),
[2] = SUBSTRING(Col, 2, 1),
[3] = SUBSTRING(Col, 3, 1),
[4] = SUBSTRING(Col, 4, 1),
[5] = SUBSTRING(Col, 5, 1),
[6] = SUBSTRING(Col, 6, 1),
[7] = SUBSTRING(Col, 7, 1),
[8] = SUBSTRING(Col, 8, 1),
[9] = SUBSTRING(Col, 9, 1)
FROM (VALUES ('ABCDE'), ('FGHIJKLMN')) t (Col);
如果您知道列数,那就没问题。如果您具有未知数量的列,则只需要使用 n 列生成相同的SQL。要做到这一点,你需要一个数字表,因为很多人都没有,我会做一个关于如何动态生成一个的快速演示。
以下将生成一系列数字,1 - 100,000,000。
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2)
SELECT Number
FROM Numbers;
它只是使用一个表值构造函数来生成10行(N1
),然后交叉连接这10行以获得100行(N2
),然后交叉连接这100行以获得10,000行(N3
)等等等等。它最终使用ROW_NUMBER()
来获取序列号。
这可能需要减少这个用途,我希望你不要拆分长度为100,000,000个字符的字符串,但原则适用。您可以使用TOP
和字符串的最大长度来限制它。对于每个数字,您只需构建所需的重复SQL,即:
,[n] = SUBSTRING(Col, n, 1)
所以你有类似的东西:
SELECT Number,
[SQL] = ',[' + CAST(Number AS VARCHAR(10)) + '] = SUBSTRING(Col, ' + CAST(Number AS VARCHAR(10)) + ', 1)'
FROM Numbers;
其中包括:
Number SQL
-----------------------------------
1 ,[1] = SUBSTRING(Col, 1, 1)
2 ,[2] = SUBSTRING(Col, 2, 1)
3 ,[3] = SUBSTRING(Col, 3, 1)
4 ,[4] = SUBSTRING(Col, 4, 1)
最后一步是通过连接SQL
列中的所有文本来建立最终陈述;最好的方法是使用SQL Server的XML Extensions。
所以你的最终查询最终可能会像:
DECLARE @SQL NVARCHAR(MAX) = '';
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T;
CREATE TABLE #T (Col VARCHAR(100));
INSERT #T (Col) VALUES ('ABCDE'), ('FGHIJKLMN');
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2)
SELECT @SQL = 'SELECT Col' +
( SELECT TOP (SELECT MAX(LEN(Col)) FROM #T)
',[' + CAST(Number AS VARCHAR(10)) + '] = SUBSTRING(Col, ' + CAST(Number AS VARCHAR(10)) + ', 1)'
FROM Numbers
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)') + '
FROM #T;';
EXECUTE sp_executesql @SQL;
给出了:
Col 1 2 3 4 5 6 7 8 9
-------------------------------------------------
ABCDE A B C D E
FGHIJKLMN F G H I J K L M N
最后,如果您真的想将其拆分成行,我仍然会使用相同的方法,使用您的adhoc数字表,只需将其加入原始表:
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T;
CREATE TABLE #T (Col VARCHAR(100));
INSERT #T (Col) VALUES ('ABCDE'), ('FGHIJKLMN');
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT TOP (SELECT MAX(LEN(Col)) FROM #T) ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2)
SELECT t.Col,
Position = n.Number,
Character = SUBSTRING(t.Col, n.Number, 1)
FROM #T AS t
INNER JOIN Numbers AS n
ON n.Number <= LEN(t.Col)
ORDER BY t.Col, n.Number;
其中包括:
Col Position Character
-------------------------------
ABCDE 1 A
ABCDE 2 B
ABCDE 3 C
ABCDE 4 D
ABCDE 5 E
答案 3 :(得分:0)
单程
declare @str varchar(max) = 'ABCDE'
declare @sql nvarchar(max) = ''
declare @i int = 1
while (@i <= len(@str)) begin
set @sql += case when @i > 1 then ',' else '' end + '''' + substring(@str, @i, 1) + ''''
set @i += 1
end
exec('select ' + @sql)
(如果'
可以显示为字符,则需要替换''
)
答案 4 :(得分:0)
这是动态文本长度的解决方案。
-- Generate demo data
CREATE TABLE #temp(col nvarchar(100))
INSERT INTO #temp(col)
VALUES(N'A'),(N'ABC'),(N'DEFGHI'),(N'AB'),(N'KLOMA')
-- Split all in multiple rows
CREATE TABLE #output (col nvarchar(100),part nchar(1), pos int)
;WITH cte AS(
SELECT col, LEFT(col, 1) as part, 1 as pos
FROM #temp
UNION ALL
SELECT col, SUBSTRING(col, pos+1,1) as part, pos+1 as part
FROM cte
WHERE LEN(col) > pos
)
INSERT INTO #output(col, part, pos)
SELECT col, part, pos
FROM cte
DECLARE @sql nvarchar(max), @columnlist nvarchar(max)
-- Generate Columlist for dynamic pivot
SELECT @columnlist = COALESCE(@columnlist + N',[' + CONVERT(nvarchar(max),pos) + ']', N'[' + CONVERT(nvarchar(max),pos) + ']')
FROM #output o
WHERE o.col = (SELECT TOP (1) col FROM #output ORDER BY LEN(col) DESC)
-- Pivoting for readability
SET @sql = N'
SELECT pvt.*
FROM #output o
PIVOT (
MAX(o.part)
FOR pos IN('+@columnlist+')
) as pvt'
EXEC (@sql)
-- Cleanup
DROP TABLE #temp
DROP TABLE #output
关键部分是cte和之后的旋转。如果您有任何疑问,请给我一个简短的反馈。