多值数据分成列成行

时间:2019-07-08 12:16:50

标签: sql-server tsql split position sql-server-2016

我正在尝试从各个字段中拆分数据,然后必须将其从列中拆分为行。 当前,一个客户的所有费用都存储在一个字段中。存储在另一个字段中的所有费用说明也是如此。 我有目前在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

1 个答案:

答案 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]中选择相应的值。

希望这很清楚...

相关问题