T-SQL 将逗号分隔列转换为多列 - 动态
T-SQL Convert Comma Delimited column into multiple columns - Dynamic
SQL Server 2008 R2,我有一个 table 和一个 varchar
列,其中包含一个逗号分隔的字符串。逗号分隔字段的数量可能会有所不同,从几个到几十个 (50+)。我需要将 CSV 字符串转换为列。 table 数据示例:
DECLARE @tv_Table1 TABLE
(
Name VARCHAR(MAX),
[Values] VARCHAR(MAX)
);
INSERT INTO @tv_Table1
VALUES ('Number1', '00,1110801100543 20190801103956,123456798708/01/201910:07:13,123456787608/01/201910:08:26,123456776508/01/201910:08:56,123456755508/01/201910:09:26,SOME TEXT1,123456744408/01/201910:29:08,'),
('Number2', 'val,val2,val3,'),
('Number3', 'val5,val7,val9,val14,'),
('Number4', '00,8980801100904 20190801128781,777333489808/01/201910:07:13,777333489808/01/201910:08:26,777333489808/01/201910:08:56,777333489808/01/201910:09:26,777333489808/01/201910:10:14,SOME TEXT2,777333489808/01/201910:05:56,777333489808/01/201910:12:05,777333489808/01/201910:12:44,777333489808/01/201910:13:02,777333489808/01/201910:13:02,777333489808/01/201910:13:32,777333489808/01/201910:14:03,777333489808/01/201910:15:15,777333489808/01/201910:15:46,SOME TEXT2,777333489808/01/201910:19:08,777333489808/01/201910:19:38,SOME TEXT2,777333489808/01/201910:28:31,777333489808/01/201910:29:02,777333489808/01/201910:29:32,777333489808/01/201910:30:02,777333489808/01/201910:30:32,777333489808/01/201910:31:02,777333489808/01/201910:31:32,777333489808/01/201910:32:02,777333489808/01/201910:32:33,777333489808/01/201910:33:02,777333489808/01/201910:33:33,777333489808/01/201910:34:11,SOME TEXT2,777333489808/01/201910:36:47,777333489808/01/201910:37:17,777333489808/01/201910:37:35,')
SELECT
Name,
Commas = LEN([Values]) - LEN(REPLACE([Values], ',', '')),
[Values]
FROM
@tv_Table1
我需要分成多列的字符串中的最后一个字符始终是逗号 (,)。
我需要这样的输出:
我开始使用 CTE:
;WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3)
SELECT
Name,
[1] AS Column1, [2] AS Column2, [3] AS Column3, [4] AS Column4, [5] AS Column5,
[6] AS Column6, [7] AS Column7,[8] AS Column8, [9] AS Column9, [10] AS Column10,
[11] AS Column11, [12] AS Column12, [13] AS Column13, [14] AS Column14,[15] AS Column15
FROM
(SELECT Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY nums.n) AS PositionInList,
LTRIM(RTRIM(SUBSTRING(valueTable.[Values], nums.n, charindex(N',', valueTable.[Values] + N',', nums.n) - nums.n))) AS [Value]
FROM Numbers AS nums INNER JOIN @tv_Table1 AS valueTable ON nums.n <= CONVERT(int, LEN(valueTable.[Values])) AND SUBSTRING(N',' + valueTable.[Values], n, 1) = N',') AS SourceTable
PIVOT
(
MAX([VALUE]) FOR PositionInList IN (
[1], [2], [3], [4], [5],
[6], [7], [8], [9], [10],
[11], [12], [13], [14], [15]
)
) AS Table2
但是,这有几个问题。第一,我突然意识到它不是动态的(数据可以是);第二,到目前为止我停在了 11(Number4 应该有 37 列)。
有没有办法让它变得动态?有没有更好的方法来解决这个问题?有没有办法让我到目前为止的工作?
我有一些限制 - 我无法更改 table 结构。我无法添加功能。如果需要,我应该能够添加存储过程,但我不想这样做。
感谢任何想法、帮助、建议等。
查询将 运行 按需查询,通常是一天,大约 500 行,其中可能有十几行需要 CSV 字符串到列。
使用临时table,可以构建动态查询。 temp table 保存解析原始数据。然后获取列表中的最大位置,使用最大位置构建 COlumns 和 Pivot Value。
DECLARE @tv_Table1 TABLE
(
Name VARCHAR(MAX),
[Values] VARCHAR(MAX)
);
INSERT INTO @tv_Table1
VALUES ('Number1', '00,1110801100543 20190801103956,123456798708/01/201910:07:13,123456787608/01/201910:08:26,123456776508/01/201910:08:56,123456755508/01/201910:09:26,SOME TEXT1,123456744408/01/201910:29:08,'),
('Number2', 'val,val2,val3,'),
('Number3', 'val5,val7,val9,val14,'),
('Number4', '00,8980801100904 20190801128781,777333489808/01/201910:07:13,777333489808/01/201910:08:26,777333489808/01/201910:08:56,777333489808/01/201910:09:26,777333489808/01/201910:10:14,SOME TEXT2,777333489808/01/201910:05:56,777333489808/01/201910:12:05,777333489808/01/201910:12:44,777333489808/01/201910:13:02,777333489808/01/201910:13:02,777333489808/01/201910:13:32,777333489808/01/201910:14:03,777333489808/01/201910:15:15,777333489808/01/201910:15:46,SOME TEXT2,777333489808/01/201910:19:08,777333489808/01/201910:19:38,SOME TEXT2,777333489808/01/201910:28:31,777333489808/01/201910:29:02,777333489808/01/201910:29:32,777333489808/01/201910:30:02,777333489808/01/201910:30:32,777333489808/01/201910:31:02,777333489808/01/201910:31:32,777333489808/01/201910:32:02,777333489808/01/201910:32:33,777333489808/01/201910:33:02,777333489808/01/201910:33:33,777333489808/01/201910:34:11,SOME TEXT2,777333489808/01/201910:36:47,777333489808/01/201910:37:17,777333489808/01/201910:37:35,')
IF OBJECT_ID('tempdb..#RawData') IS NOT NULL
DROP TABLE #RawData;
CREATE TABLE #RawData(
Name varchar(max) COLLATE DATABASE_DEFAULT not null,
PositionInList int not null,
[Value] varchar(max) COLLATE DATABASE_DEFAULT not null
)
;WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3)
INSERT #RawData(Name,PositionInList,[Value])
SELECT Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY nums.n) AS PositionInList,
LTRIM(RTRIM(SUBSTRING(valueTable.[Values], nums.n, charindex(N',', valueTable.[Values] + N',', nums.n) - nums.n))) AS [Value]
FROM Numbers AS nums
INNER JOIN @tv_Table1 AS valueTable
ON nums.n <= CONVERT(int, LEN(valueTable.[Values]))
AND SUBSTRING(N',' + valueTable.[Values], n, 1) = N','
-- SELECT * FROM #RawData;
DECLARE @MaxPos int;
DECLARE @Cols varchar(2048)='';
DECLARE @PvtVals varchar(2048)='';
DECLARE @i int=1;
DECLARE @SQL nvarchar(max)='';
SELECT @MaxPos = MAX(PositionInList) FROM #RawData;
WHILE @i<@MaxPos
BEGIN
SET @Cols=@Cols+',['+CAST(@i AS varchar(20))+'] AS Column' + CAST(@i AS varchar(20))
SET @PvtVals=@PvtVals+',['+CAST(@i AS varchar(20))+']'
SET @i=@i+1;
END
SET @PvtVals=RIGHT(@PvtVals,LEN(@PvtVals)-1);
SET @SQL='
SELECT
Name'+ @Cols +'
FROM #RawData
PIVOT
(
MAX([VALUE]) FOR PositionInList IN ('+@PvtVals+')
) AS Table2';
-- PRINT @SQL;
exec sp_executesql @SQL;
SQL Server 2008 R2,我有一个 table 和一个 varchar
列,其中包含一个逗号分隔的字符串。逗号分隔字段的数量可能会有所不同,从几个到几十个 (50+)。我需要将 CSV 字符串转换为列。 table 数据示例:
DECLARE @tv_Table1 TABLE
(
Name VARCHAR(MAX),
[Values] VARCHAR(MAX)
);
INSERT INTO @tv_Table1
VALUES ('Number1', '00,1110801100543 20190801103956,123456798708/01/201910:07:13,123456787608/01/201910:08:26,123456776508/01/201910:08:56,123456755508/01/201910:09:26,SOME TEXT1,123456744408/01/201910:29:08,'),
('Number2', 'val,val2,val3,'),
('Number3', 'val5,val7,val9,val14,'),
('Number4', '00,8980801100904 20190801128781,777333489808/01/201910:07:13,777333489808/01/201910:08:26,777333489808/01/201910:08:56,777333489808/01/201910:09:26,777333489808/01/201910:10:14,SOME TEXT2,777333489808/01/201910:05:56,777333489808/01/201910:12:05,777333489808/01/201910:12:44,777333489808/01/201910:13:02,777333489808/01/201910:13:02,777333489808/01/201910:13:32,777333489808/01/201910:14:03,777333489808/01/201910:15:15,777333489808/01/201910:15:46,SOME TEXT2,777333489808/01/201910:19:08,777333489808/01/201910:19:38,SOME TEXT2,777333489808/01/201910:28:31,777333489808/01/201910:29:02,777333489808/01/201910:29:32,777333489808/01/201910:30:02,777333489808/01/201910:30:32,777333489808/01/201910:31:02,777333489808/01/201910:31:32,777333489808/01/201910:32:02,777333489808/01/201910:32:33,777333489808/01/201910:33:02,777333489808/01/201910:33:33,777333489808/01/201910:34:11,SOME TEXT2,777333489808/01/201910:36:47,777333489808/01/201910:37:17,777333489808/01/201910:37:35,')
SELECT
Name,
Commas = LEN([Values]) - LEN(REPLACE([Values], ',', '')),
[Values]
FROM
@tv_Table1
我需要分成多列的字符串中的最后一个字符始终是逗号 (,)。
我需要这样的输出:
我开始使用 CTE:
;WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3)
SELECT
Name,
[1] AS Column1, [2] AS Column2, [3] AS Column3, [4] AS Column4, [5] AS Column5,
[6] AS Column6, [7] AS Column7,[8] AS Column8, [9] AS Column9, [10] AS Column10,
[11] AS Column11, [12] AS Column12, [13] AS Column13, [14] AS Column14,[15] AS Column15
FROM
(SELECT Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY nums.n) AS PositionInList,
LTRIM(RTRIM(SUBSTRING(valueTable.[Values], nums.n, charindex(N',', valueTable.[Values] + N',', nums.n) - nums.n))) AS [Value]
FROM Numbers AS nums INNER JOIN @tv_Table1 AS valueTable ON nums.n <= CONVERT(int, LEN(valueTable.[Values])) AND SUBSTRING(N',' + valueTable.[Values], n, 1) = N',') AS SourceTable
PIVOT
(
MAX([VALUE]) FOR PositionInList IN (
[1], [2], [3], [4], [5],
[6], [7], [8], [9], [10],
[11], [12], [13], [14], [15]
)
) AS Table2
但是,这有几个问题。第一,我突然意识到它不是动态的(数据可以是);第二,到目前为止我停在了 11(Number4 应该有 37 列)。
有没有办法让它变得动态?有没有更好的方法来解决这个问题?有没有办法让我到目前为止的工作?
我有一些限制 - 我无法更改 table 结构。我无法添加功能。如果需要,我应该能够添加存储过程,但我不想这样做。
感谢任何想法、帮助、建议等。
查询将 运行 按需查询,通常是一天,大约 500 行,其中可能有十几行需要 CSV 字符串到列。
使用临时table,可以构建动态查询。 temp table 保存解析原始数据。然后获取列表中的最大位置,使用最大位置构建 COlumns 和 Pivot Value。
DECLARE @tv_Table1 TABLE
(
Name VARCHAR(MAX),
[Values] VARCHAR(MAX)
);
INSERT INTO @tv_Table1
VALUES ('Number1', '00,1110801100543 20190801103956,123456798708/01/201910:07:13,123456787608/01/201910:08:26,123456776508/01/201910:08:56,123456755508/01/201910:09:26,SOME TEXT1,123456744408/01/201910:29:08,'),
('Number2', 'val,val2,val3,'),
('Number3', 'val5,val7,val9,val14,'),
('Number4', '00,8980801100904 20190801128781,777333489808/01/201910:07:13,777333489808/01/201910:08:26,777333489808/01/201910:08:56,777333489808/01/201910:09:26,777333489808/01/201910:10:14,SOME TEXT2,777333489808/01/201910:05:56,777333489808/01/201910:12:05,777333489808/01/201910:12:44,777333489808/01/201910:13:02,777333489808/01/201910:13:02,777333489808/01/201910:13:32,777333489808/01/201910:14:03,777333489808/01/201910:15:15,777333489808/01/201910:15:46,SOME TEXT2,777333489808/01/201910:19:08,777333489808/01/201910:19:38,SOME TEXT2,777333489808/01/201910:28:31,777333489808/01/201910:29:02,777333489808/01/201910:29:32,777333489808/01/201910:30:02,777333489808/01/201910:30:32,777333489808/01/201910:31:02,777333489808/01/201910:31:32,777333489808/01/201910:32:02,777333489808/01/201910:32:33,777333489808/01/201910:33:02,777333489808/01/201910:33:33,777333489808/01/201910:34:11,SOME TEXT2,777333489808/01/201910:36:47,777333489808/01/201910:37:17,777333489808/01/201910:37:35,')
IF OBJECT_ID('tempdb..#RawData') IS NOT NULL
DROP TABLE #RawData;
CREATE TABLE #RawData(
Name varchar(max) COLLATE DATABASE_DEFAULT not null,
PositionInList int not null,
[Value] varchar(max) COLLATE DATABASE_DEFAULT not null
)
;WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3)
INSERT #RawData(Name,PositionInList,[Value])
SELECT Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY nums.n) AS PositionInList,
LTRIM(RTRIM(SUBSTRING(valueTable.[Values], nums.n, charindex(N',', valueTable.[Values] + N',', nums.n) - nums.n))) AS [Value]
FROM Numbers AS nums
INNER JOIN @tv_Table1 AS valueTable
ON nums.n <= CONVERT(int, LEN(valueTable.[Values]))
AND SUBSTRING(N',' + valueTable.[Values], n, 1) = N','
-- SELECT * FROM #RawData;
DECLARE @MaxPos int;
DECLARE @Cols varchar(2048)='';
DECLARE @PvtVals varchar(2048)='';
DECLARE @i int=1;
DECLARE @SQL nvarchar(max)='';
SELECT @MaxPos = MAX(PositionInList) FROM #RawData;
WHILE @i<@MaxPos
BEGIN
SET @Cols=@Cols+',['+CAST(@i AS varchar(20))+'] AS Column' + CAST(@i AS varchar(20))
SET @PvtVals=@PvtVals+',['+CAST(@i AS varchar(20))+']'
SET @i=@i+1;
END
SET @PvtVals=RIGHT(@PvtVals,LEN(@PvtVals)-1);
SET @SQL='
SELECT
Name'+ @Cols +'
FROM #RawData
PIVOT
(
MAX([VALUE]) FOR PositionInList IN ('+@PvtVals+')
) AS Table2';
-- PRINT @SQL;
exec sp_executesql @SQL;