将表中的给定输入值拆分为下一行(需要在第3行之后完全拆分)
Temp (column/Table1)
TBL101 | PC | 1.00 | COMP101 | CS | 1.00 | TQR101 | CP | 5.00 | TXL101 | PC | 1.00 | SQL101 | PC | 1.00........etc
ID Pack qty (columns/Table2)
TBL101 PC 1.00
COMP101 CS 1.00
TQR101 CP 5.00
TXL101 PC 1.00
SQL101 PC 1.00
我使用以下代码执行此操作,但仅适用于第一行正常工作
DECLARE @Delimiter VARCHAR(40)
SET @Delimiter = '|'
;WITH CTE AS
(
SELECT
CAST('<M>' + REPLACE(temp, @Delimiter , '</M><M>') + '</M>' AS XML)
AS [ColName XML]
FROM Table1
)
--INSERT INTO Table2
-- (ID,PACK,OrderQty)
SELECT
[ColName XML].value('/M[1]', 'bigint') As [ID],
[ColName XML].value('/M[2]', 'VARCHAR(40)') As [Pack},
[ColName XML].value('/M[3]', 'decimal(18,2)') As [OrderQty]
FROM CTE
GO
答案 0 :(得分:1)
更新:字符串中动态字段数的版本。如果您想在评论中提出一些问题,请在此处解释。享受。
CREATE TABLE #dta(
r NVARCHAR(4000) NOT NULL
);
INSERT INTO #dta(r)VALUES
('TBL101 | PC | 1.00 | COMP101 | CS | 1.00 | TQR101 | CP | 5.00 | TXL101 | PC | 1.00 | SQL101 | PC | 1.00'),
('TBL102 | PC | 4.00 | COMP102 | CS | 3.00 | TQR102 | CP | 6.00 | TXL102 | PC | 7.00 | SQL102 | PC | 9.00');
DECLARE @num_fields INT;
SELECT
@num_fields=MAX(LEN(r) - LEN(REPLACE(r,'|',''))) + 1
FROM
#dta;
DECLARE @fields_sel NVARCHAR(MAX);
SET @fields_sel=STUFF((
SELECT
',[ColName XML].value(''/M['+CAST((N-1)*3+1 AS VARCHAR)+']'', ''NVARCHAR(40)'') As [ID'+CAST(N AS VARCHAR)+']'+
',[ColName XML].value(''/M['+CAST((N-1)*3+2 AS VARCHAR)+']'', ''NVARCHAR(40)'') As [Pack'+CAST(N AS VARCHAR)+']'+
',[ColName XML].value(''/M['+CAST((N-1)*3+3 AS VARCHAR)+']'', ''decimal(18,2)'') As [OrderQty'+CAST(N AS VARCHAR)+']'
FROM (
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
) AS tally(N)
WHERE N<=@num_fields/3
FOR XML PATH('')),1,1,'');
DECLARE @ca_sel NVARCHAR(MAX);
SET @ca_sel=STUFF((
SELECT
' UNION ALL SELECT RTRIM(LTRIM([ID'+CAST(N AS VARCHAR)+'])),RTRIM(LTRIM([Pack'+CAST(N AS VARCHAR)+'])),[OrderQty'+CAST(N AS VARCHAR)+']'
FROM (
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
) AS tally(N)
WHERE N<=@num_fields/3
FOR XML PATH('')),1,LEN(' UNION ALL'),'');
--SELECT @ca_sel;
--SELECT @fields_sel;
DECLARE @Delimiter NVARCHAR(40)
SET @Delimiter = N'|'
DECLARE @sql NVARCHAR(MAX);
SET @sql=N'
;WITH CTE([ColName XML]) AS
(
SELECT
CAST(''<M>'' + REPLACE(r, @Delimiter , ''</M><M>'') + ''</M>'' AS XML) AS [ColName XML]
FROM
#dta
), sep_fields AS (
SELECT
'+@fields_sel+N'
FROM CTE
)
SELECT
up.*
FROM
sep_fields
CROSS APPLY (
'+@ca_sel+N'
) AS up([ID],[Pack],[OrderQty])';
---SELECT @sql;
EXEC sp_executesql @sql, N'@Delimiter NVARCHAR(40)', @Delimiter;
DROP TABLE #dta;
这里有一个答案,当你事先知道字符串字段中会出现多少字段时(例如在你的问题中,15):
CREATE TABLE #dta(
r NVARCHAR(4000) NOT NULL
);
INSERT INTO #dta(r)VALUES
('TBL101 | PC | 1.00 | COMP101 | CS | 1.00 | TQR101 | CP | 5.00 | TXL101 | PC | 1.00 | SQL101 | PC | 1.00'),
('TBL102 | PC | 4.00 | COMP102 | CS | 3.00 | TQR102 | CP | 6.00 | TXL102 | PC | 7.00 | SQL102 | PC | 9.00');
DECLARE @Delimiter NVARCHAR(40)
SET @Delimiter = N'|'
;WITH CTE([ColName XML]) AS
(
SELECT
CAST('<M>' + REPLACE(r, @Delimiter , '</M><M>') + '</M>' AS XML) AS [ColName XML]
FROM
#dta
), sep_fields AS (
SELECT
[ColName XML].value('/M[1]', 'NVARCHAR(40)') As [ID1],
[ColName XML].value('/M[2]', 'NVARCHAR(40)') As [Pack1],
[ColName XML].value('/M[3]', 'decimal(18,2)') As [OrderQty1],
[ColName XML].value('/M[4]', 'NVARCHAR(40)') As [ID2],
[ColName XML].value('/M[5]', 'NVARCHAR(40)') As [Pack2],
[ColName XML].value('/M[6]', 'decimal(18,2)') As [OrderQty2],
[ColName XML].value('/M[7]', 'NVARCHAR(40)') As [ID3],
[ColName XML].value('/M[8]', 'NVARCHAR(40)') As [Pack3],
[ColName XML].value('/M[9]', 'decimal(18,2)') As [OrderQty3],
[ColName XML].value('/M[10]', 'NVARCHAR(40)') As [ID4],
[ColName XML].value('/M[11]', 'NVARCHAR(40)') As [Pack4],
[ColName XML].value('/M[12]', 'decimal(18,2)') As [OrderQty4],
[ColName XML].value('/M[13]', 'NVARCHAR(40)') As [ID5],
[ColName XML].value('/M[14]', 'NVARCHAR(40)') As [Pack5],
[ColName XML].value('/M[15]', 'decimal(18,2)') As [OrderQty5]
FROM CTE
)
SELECT
up.*
FROM
sep_fields
CROSS APPLY (
SELECT [ID1],[Pack1],[OrderQty1]
UNION ALL
SELECT [ID2],[Pack2],[OrderQty2]
UNION ALL
SELECT [ID3],[Pack3],[OrderQty3]
UNION ALL
SELECT [ID4],[Pack4],[OrderQty4]
UNION ALL
SELECT [ID5],[Pack5],[OrderQty5]
) AS up
DROP TABLE #dta;
结果是:
╔═══════════╦═══════╦═══════════╗
║ ID1 ║ Pack1 ║ OrderQty1 ║
╠═══════════╬═══════╬═══════════╣
║ TBL101 ║ PC ║ 1.00 ║
║ COMP101 ║ CS ║ 1.00 ║
║ TQR101 ║ CP ║ 5.00 ║
║ TXL101 ║ PC ║ 1.00 ║
║ SQL101 ║ PC ║ 1.00 ║
║ TBL102 ║ PC ║ 4.00 ║
║ COMP102 ║ CS ║ 3.00 ║
║ TQR102 ║ CP ║ 6.00 ║
║ TXL102 ║ PC ║ 7.00 ║
║ SQL102 ║ PC ║ 9.00 ║
╚═══════════╩═══════╩═══════════╝
答案 1 :(得分:1)
您可以使用:
DECLARE @str nvarchar(max) = N'TBL101 | PC | 1.00 | COMP101 | CS | 1.00 | TQR101 | CP | 5.00 | TXL101 | PC | 1.00 | SQL101 | PC | 1.00',
@x xml
SELECT @x = CAST('<a>'+REPLACE(@str,' | ', '</a><a>')+'</a>' as xml)
;WITH cte AS (
SELECT t.c.value('.','nvarchar(100)') as [values],
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
FROM @x.nodes('/a') as t(c)
)
SELECT [1] as ID1,
[2] as Pack1,
[0] as OrderQty1
FROM (
SELECT rn- CASE WHEN rn%3 = 0 THEN 3 ELSE rn%3 END as seq,
rn%3 as s,
[values]
FROM cte
) as t
PIVOT (
MAX([VALUES]) FOR s IN ([1],[2],[0])
) as pvt
输出:
ID1 Pack1 OrderQty1
TBL101 PC 1.00
COMP101 CS 1.00
TQR101 CP 5.00
TXL101 PC 1.00
SQL101 PC 1.00
首先转换为简单的XML。然后使用ROW_NUMBER()
添加SELECT NULL
(关于此技巧阅读here),它会为每一行添加一些ID。然后我们使用行号来获得一些序列,因此我们可以调整结果。
修改强>
如果您正在使用table,那么将整个表格设为XML:
DECLARE @temptable TABLE (
Column1 nvarchar(max)
)
INSERT INTO @temptable VALUES
(N'TBL101 | PC | 1.00 | COMP101 | CS | 1.00 | TQR101 | CP | 5.00 | TXL101 | PC | 1.00 | SQL101 | PC | 1.00')
DECLARE @x xml
SELECT @x = (
SELECT CAST('<a>'+REPLACE(Column1,' | ', '</a><a>')+'</a>' as xml)
FROM @temptable
FOR XML PATH('')
)
然后如上所述参与CTE。