【问题标题】:Delimiter splitting into separate dynamic columns in SQL Server在 SQL Server 中将分隔符拆分为单独的动态列
【发布时间】:2021-04-27 13:32:40
【问题描述】:

我知道很多与此相关的问题都可以在 Google 和 SO 中找到,但具体问题不可用,因此请在这里寻求伟大的人的帮助。

主要任务是在 SQL Server 中将单个列的管道分隔值拆分为多个列(值会发生变化,因此需要动态创建列)。

我浏览了多个链接并理解

  1. 2016年后,STRING_SPLIT()内置函数将有助于拆分并显示在单独的行中(但我需要显示在列中)。

  2. [PIVOT] -> PIVOT 在静态和动态中都是可能的,但这对于按行顺序排列的数据很有用,以使其成为列顺序。但我的表格数据是按列顺序排列的,管道分隔成一列。

  3. XML -> 这也是静态的(参见我的SQL Fiddle 查询和输出)并且需要动态创建列而不定义查询中的列数。

代码:

CREATE TABLE DELIMTEDPATH
(
    ID int,
    Path varchar(max)
);

INSERT INTO DELIMTEDPATH 
VALUES (1, 'John|Albert|James'),
       (2, 'Cricket'),
       (3, 'Mary|Joseph|Priyanka|Gilbert|Customer|Service|Passenger|MN-1234|MK-5678');`

;WITH SplitMenus AS
(
    SELECT 
        ID, 
        CONVERT(XML, '<MENUS><Menu>' + REPLACE(Path, '|', '</Menu><Menu>') + '</Menu></MENUS>') AS Path
    FROM
        DELIMTEDPATH
    WHERE
        ID IN (1, 2, 3)
)
SELECT
    ID, 
    Path.value('/MENUS[2]/Menu[2]', 'varchar(100)') AS Name1,
    Path.value('/MENUS[2]/Menu[1]', 'varchar(100)') AS Name2,
    Path.value('/MENUS[2]/Menu[3]', 'varchar(100)') AS Name3,
    Path.value('/MENUS[2]/Menu[4]', 'varchar(100)') AS Name4
FROM
    SplitMenus

我得到这个输出:

但我需要的输出是动态的,以显示 ID=3 的所有值。

【问题讨论】:

  • 这能回答你的问题吗? SQL Server dynamic PIVOT query?
  • @TAM。感谢您提供建议链接,但它将有助于按 ROW 顺序排列的数据,并且需要将其动态转换为 COLUMN,但我的表数据按 COLUMN 顺序排列,并带有 PIPE 分隔,并且在单个列中可用。我需要将单列数据解析成多列。
  • 您需要 row_number() 拆分结果中的行,然后按行号将其转回

标签: sql-server dynamic csv


【解决方案1】:

这是一个使用 XML、XQuery 和动态 SQL 的解决方案。

SQL

USE tempdb;
GO

-- DDL and sample data population, start
DROP TABLE IF EXISTS DELIMTEDPATH;
CREATE TABLE DELIMTEDPATH (ID INT IDENTITY PRIMARY KEY, [Path] VARCHAR(MAX));

INSERT INTO DELIMTEDPATH ([Path]) VALUES 
('John|Albert|James'),
('Cricket'),
('Mary|Joseph|Priyanka|Gilbert|Customer|Service|Passenger|MN-1234|MK-5678');
-- DDL and sample data population, end

DECLARE @separator CHAR(1) = '|'
    , @CrLf CHAR(2) = CHAR(13) + CHAR(10)
    , @nameCounter INT
    , @i INT = 1;

SET @nameCounter = (SELECT MAX(LEN([Path]) - LEN(REPLACE([Path], @separator,'')))/COALESCE(NULLIF(LEN(@separator), 0), 1) AS cnt
FROM DELIMTEDPATH) + 1;

DECLARE @SQL NVARCHAR(MAX) = 
N';WITH rs AS
(
    SELECT ID, 
        CAST(''<root><r>'' + REPLACE([Path], ''' + @separator + ''', ''</r><r>'') + ''</r></root>'' AS XML) AS xmldata
    FROM DELIMTEDPATH
)
SELECT ID' + @CrLf;

WHILE @i <= @nameCounter BEGIN
    SET @SQL += ', c.value(''(r[' + CAST(@i AS VARCHAR(3)) + ']/text())[1]'', ''VARCHAR(100)'') AS Name' + CAST(@i AS VARCHAR(3)) + @CrLf
    SET @i += 1
END

SET @SQL += 'FROM rs CROSS APPLY xmldata.nodes(''/root'') AS t(c);'

-- just to see it
PRINT @sql;

-- we are ready at this point
EXEC sp_executesql @SQL;

输出

+----+---------+--------+----------+---------+----------+---------+-----------+---------+
| ID |  Name1  | Name2  |  Name3   |  Name4  |  Name5   |  Name6  |   Name7   |  Name8  |
+----+---------+--------+----------+---------+----------+---------+-----------+---------+
|  1 | John    | Albert | James    | NULL    | NULL     | NULL    | NULL      | NULL    |
|  2 | Cricket | NULL   | NULL     | NULL    | NULL     | NULL    | NULL      | NULL    |
|  3 | Mary    | Joseph | Priyanka | Gilbert | Customer | Service | Passenger | MN-1234 |
+----+---------+--------+----------+---------+----------+---------+-----------+---------+

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-26
    相关资源
    最近更新 更多