繁体   English   中英

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

[英]Split a string with no delimiters into columns

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

示例:如果我有一个带有'ABCDE'的列,则需要将其拆分为'A''B''C''D''E' ,并将它们每个都分成自己的列。

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

我的问题与其他文章( Mysql可以拆分列吗? )不同,因为我的没有任何分隔符。 谢谢

我将问题解释为将字符放入一列中(“将一列中的字符串分割成一个字符,每个字符都放入其自己的列中”)。 但是,我意识到这可能是模棱两可的。

一种方法是使用递归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;

只需在子查询中插入表和列即可。 请注意,您可能还希望包括其他列。

是一个SQL Fiddle。

如果要多列并且数量不固定,则需要动态SQL。

您可以这样做:

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)

如果您想为每个字符添加一个新列,则只需要:

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扩展。

因此,您的最终查询可能会像这样:

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

最后,如果您实际上想将其拆分为行,那么我仍将使用相同的方法,将即席数字表与您的原始表连接起来:

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

单程

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)

(如果'可以显示为字符,则需要替换''

这是动态文本长度的解决方案。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM