将没有分隔符的字符串拆分为列

时间:2015-06-11 13:50:28

标签: sql sql-server database

我需要将一个字符串中的字符串拆分为一个字符,每个字符都放在SQL Server 2012中它自己的列中。

示例:如果我的列有'ABCDE',我需要将其拆分为'A''B''C''D',{{1将每个都放入自己的列中。

要拆分的列的长度可能会有所不同,因此我需要尽可能保持动态。

我的问题与其他帖子(Can Mysql Split a column?)不同,因为我的帖子没有任何分隔符。 感谢

5 个答案:

答案 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和之后的旋转。如果您有任何疑问,请给我一个简短的反馈。