我正在尝试从各个字段中拆分数据,然后必须将其从列中拆分为行。 当前,一个客户的所有费用都存储在一个字段中。存储在另一个字段中的所有费用说明也是如此。 我有目前在SQL中看起来像这样的数据
ID ChargeType ChargeAmount
1000:1597 F^F^F 1000^500^250
01000:6597 F^F^F^F^F 500^250^50^2000^1000
00010:0001 F 70
我主要使用XML将费用和费用类型分为几列。 我已经成功地将费用和金额进行了划分,并且能够加入,但这给了我错误的费用计数。
Create table Charges(
IDx int,
ID varchar(55),
ChargeAmount varchar(55),
ChargeType varchar(55)
)
DECLARE @chargetype TABLE(IDx int Identity, ID varchar(max), data1 varchar(max))
INSERT INTO @chargetype SELECT ID, ChargesType from TrnDeal
SELECT
F1.IDx,
F1.ID,
O.ChargesType
into #tempsplitchargetype
FROM
(
SELECT *,
cast('<Y>'+replace(F.data1,'^','</Y><Y>')+'</Y>' as XML) as xmlfilter
from @chargetype F
)F1
CROSS Apply
(
SELECT fdata1.D.value('.','varchar(50)') as ChargesType
FROM F1.xmlfilter.nodes('Y') as fdata1(D)) O
DECLARE @chargeamount TABLE(IDx int IDENTITY, ID varchar(max), data1
varchar(max))
INSERT INTO @chargeamount SELECT ID, ChargesAmount from TrnDeal
SELECT
S1.IDx,
S1.ID,
M.ChargeAmount
into #tempsplitchargeamount
FROM
(
SELECT *,
cast('<X>'+replace(S.data1,'^','</X><X>')+'</X>' as XML) as xmlfilter
from @chargeamount S
)S1
CROSS APPLY
(
SELECT Fdata.D.value('.','varchar(50)') as ChargeAmount
FROM S1.xmlfilter.nodes('X') as fdata(D)) M
-- Insert into dbo.Charges
Select CA.IDx, CA.ID, CA.ChargeAmount, b.ChargesType
from #tempsplitchargeamount CA
outer apply (Select IDx, ChargesType from #tempsplitchargetype) b
where CA.IDx = b.IDx
我希望1个客户端的数据如下所示。
ID ChargeType ChargeAmount
1000:1597 F 1000
1000:1597 F 500
1000:1597 F 250
01000:6597 F 500
01000:6597 F 250
01000:6597 F 50
01000:6597 F 2000
01000:6597 F 1000
答案 0 :(得分:0)
您用[SSMS-2017]
标记了此内容。这不足以确保您的RDBMS,但我认为您正在使用SQL-Server 2016或更高版本...
首先,我想指出的是,您最重要的问题是:为什么要以CSV格式存储它?这正在破坏1.NF
...如果您可以更改表格的布局,则应该使用相关的边桌。
但是-如果必须坚持使用-可以对JSON
使用技巧:
DECLARE @mockup TABLE(ID VARCHAR(100),ChargeType VARCHAR(100),ChargeAmount VARCHAR(100));
INSERT INTO @mockup(ID,ChargeType,ChargeAmount) VALUES
('1000:1597','F^F^F','1000^500^250')
,('01000:6597','F^F^F^F^F','500^250^50^2000^1000')
,('00010:0001','F','70');
SELECT m.ID
,B.[key] AS Position
,B.[value] AS ChargeType
,JSON_VALUE(A.AmountAsJson,CONCAT('$[',B.[key],']') COLLATE Latin1_General_BIN2) AS ChargeAmount
FROM @mockup m
CROSS APPLY (SELECT JSON_QUERY('["' + REPLACE(m.ChargeAmount,'^','","') + '"]')) A(AmountAsJson)
CROSS APPLY OPENJSON('["' + REPLACE(m.ChargeType,'^','","') + '"]') B
ORDER BY m.ID
,Position;
结果:
+------------+----------+------------+--------------+
| ID | Position | ChargeType | ChargeAmount |
+------------+----------+------------+--------------+
| 00010:0001 | 0 | F | 70 |
+------------+----------+------------+--------------+
| 01000:6597 | 0 | F | 500 |
+------------+----------+------------+--------------+
| 01000:6597 | 1 | F | 250 |
+------------+----------+------------+--------------+
| 01000:6597 | 2 | F | 50 |
+------------+----------+------------+--------------+
| 01000:6597 | 3 | F | 2000 |
+------------+----------+------------+--------------+
| 01000:6597 | 4 | F | 1000 |
+------------+----------+------------+--------------+
| 1000:1597 | 0 | F | 1000 |
+------------+----------+------------+--------------+
| 1000:1597 | 1 | F | 500 |
+------------+----------+------------+--------------+
| 1000:1597 | 2 | F | 250 |
+------------+----------+------------+--------------+
简而言之:
在SQL Server的TSQL中,字符串拆分是很麻烦的。使用循环,递归CTE或XML(如上所述)有多种解决方法。
使用v2016,开发人员对新功能STRING_SPLIT()
感到非常高兴,但MS忘记提供片段的位置。这使STRING_SPLIT()
成为一个非常无用的功能...
但是-与STRING_SPLIT
-一起引入了JSON支持。使用OPENJSON
读取一个简单的JSON数组将提供片段在[key]
列中的位置(注意:从零开始!)。
上面的代码将首先创建一个 mockup-table 并将其填充为示例数据。
第一个CROSS APPLY
会将ChargeAmount
转换为JSON数组,而无需对其进行任何处理。
第二个CROSS APPLY
会将ChargeType
转换为JSON数组,并且每个片段返回一行。
然后,选择通过使用JSON_VALUE
(位置)作为JSON路径,使用AmountAsJson
从[key]
中选择相应的值。
希望这很清楚...