Многозначные данные в столбцы в строки - PullRequest
1 голос
/ 08 июля 2019

Я пытаюсь разделить данные из различных полей, а затем я должен взять их из столбцов в строки. В настоящее время все расходы на 1 клиента хранятся в одном поле. Так же как и все описания расходов, хранящихся в другом поле. У меня есть данные, которые в настоящее время выглядят так в 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 голосов
/ 08 июля 2019

Вы отметили это как [SSMS-2017]. Этого недостаточно, чтобы быть уверенным в вашей СУБД, но я предполагаю, что вы используете 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          |
+------------+----------+------------+--------------+

Идея вкратце:

Разделение строк - это боль в TSQL SQL-сервера. Были различные обходные пути с использованием циклов, рекурсивных CTE или XML (как вы делали выше).
С v2016 разработчики были очень довольны новой функцией STRING_SPLIT(), но MS забыла указать позицию фрагмента. Это делает STRING_SPLIT() довольно бесполезной функцией ...

Но - вместе с STRING_SPLIT - появилась поддержка JSON. Чтение простого JSON-массива с OPENJSON обеспечит положение фрагмента в столбце [key] (внимание: начинается с нуля!).

Приведенный выше код сначала создаст макет-таблицу и заполнит ее вашими примерами данных.
Первый CROSS APPLY преобразует ChargeAmount в JSON-массив, ничего не делая с ним.
Второй CROSS APPLY преобразует ChargeType в JSON-массив и возвращает одну строку на фрагмент.
Затем выборка использует JSON_VALUE, чтобы выбрать соответствующее значение из AmountAsJson, используя [key] (положение) в качестве пути JSON.

Надеюсь, это было ясно ...

...