Как разделить значения из строкового поля в SQL - PullRequest
0 голосов
/ 24 января 2020

У меня есть строковое поле, как показано ниже:

declare @rawData nvarchar (max) =

'{"request_id":"801707","final_decision":"PASS","derived_Attribute_3":"PASS|Number of Total Active Institutions :3","derived_Attribute_1":"PASS|Number of active MFI :0","derived_Attribute_2":"PASS|Overdue Amount:0.0","derived_Attribute_4":"PASS|Total Exposure + Applied Amount :76384.0","derived_Attribute_5":"PASS|Write off amount:0.0","cbResponseMsg":"Final Decision:PASS || Number of Total Active Institutions :3 || Number of active MFI :0 || Overdue Amount:0.0 || Total Exposure + Applied Amount :76384.0 || Write off amount:0.0"}'

здесь мне нужно манипулировать строкой, чтобы получить некоторые конкретные значения c, т.е. получить строку после слова output_Attribute, но не показывать слова ' PASS |» или «FAIL |» и показывать их в отдельном поле.

Ожидаемый результат:

derived_Attribute_1   derived_Attribute_2   derived_Attribute_3  derived_Attribute_4  derived_Attribute_5
 0                      0.0                   3                    76384.0             0.0

Примечание. Я использую MS SQL 2014 или меньшую версию

Ответы [ 6 ]

2 голосов
/ 24 января 2020

Ваш ввод действителен JSON, поэтому, если вы можете использовать SQL Server 2016 или выше, вы можете попробовать подход, основанный на OPENJSON() и динамическом c утверждении:

JSON

DECLARE @json nvarchar(max) = N'{
   "request_id":"801707",
   "final_decision":"PASS",
   "derived_Attribute_3":"PASS|Number of Total Active Institutions :3",
   "derived_Attribute_1":"PASS|Number of active MFI :0",
   "derived_Attribute_2":"PASS|Overdue Amount:0.0",
   "derived_Attribute_4":"PASS|Total Exposure + Applied Amount :76384.0",
   "derived_Attribute_5":"PASS|Write off amount:0.0",
   "cbResponseMsg":"Final Decision:PASS || Number of Total Active Institutions :3 || Number of active MFI :0 || Overdue Amount:0.0 || Total Exposure + Applied Amount :76384.0 || Write off amount:0.0"
}'

Заявление:

-- Dynamic statement
DECLARE @stm nvarchar(max) = N'SELECT'
SELECT @stm = CONCAT(
   @stm,
   CASE WHEN @stm = 'SELECT' THEN N' ' ELSE N', ' END,
   RIGHT([value], LEN([value]) - CHARINDEX(':', [value])),
   N' AS [',
   [key],
   N']'
   )
FROM OPENJSON(@json)
WHERE [key] LIKE 'derived_Attribute_%'

-- Print and execute
PRINT @stm
EXEC sp_executesql @stm

Результат:

---------------------------------------------------------------------------------------------------
derived_Attribute_3 derived_Attribute_1 derived_Attribute_2 derived_Attribute_4 derived_Attribute_5
---------------------------------------------------------------------------------------------------
3                   0                   0.0                 76384.0             0.0
1 голос
/ 27 января 2020

Пожалуйста, попробуйте приведенное ниже решение, проголосуйте, если это будет сочтено полезным

Функция разделения:

CREATE FUNCTION [dbo].[SplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
) 
BEGIN 
    DECLARE @start INT, @end INT 
    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1

        INSERT INTO @output (splitdata)  
        VALUES(SUBSTRING(@string, @start, @end - @start)) 
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)

    END 
    RETURN 
END

Окончательный запрос

DECLARE @rawData NVARCHAR (max) = 
    '{"request_id":"801707","final_decision":"PASS","derived_Attribute_3":"PASS|Number of Total Active Institutions :3","derived_Attribute_1":"PASS|Number of active MFI :0","derived_Attribute_2":"PASS|Overdue Amount:0.0","derived_Attribute_4":"PASS|Total Exposure + Applied Amount :76384.0","derived_Attribute_5":"PASS|Write off amount:0.0","cbResponseMsg":"Final Decision:PASS || Number of Total Active Institutions :3 || Number of active MFI :0 || Overdue Amount:0.0 || Total Exposure + Applied Amount :76384.0 || Write off amount:0.0"}'

CREATE TABLE #table 
  ( 
     id      INT IDENTITY(1, 1), 
     value   VARCHAR(200), 
     colname VARCHAR(100) 
  ) 

CREATE TABLE #result 
  ( 
     colname  VARCHAR(100), 
     colvalue VARCHAR(50), 
  ) 

INSERT INTO #table 
            (value, 
             colname) 
SELECT splitdata, 
       Substring(splitdata, 2, Charindex('":"', splitdata) - 1) 
FROM   Splitstring(@rawData, ',') 
WHERE  splitdata LIKE '%derived_Attribute%' 
ORDER  BY splitdata 

--, SUBSTRING(splitdata,2,charindex('":"',splitdata)-1)  
DECLARE @count INT=1 
DECLARE @recordcount INT=0 

SELECT @recordcount = Count(*) 
FROM   #table 

WHILE( @count < @recordcount ) 
  BEGIN 
      DECLARE @strValue VARCHAR(200), 
              @strName  VARCHAR(200) 

      SELECT @strValue = value, 
             @strName = colname 
      FROM   #table 
      WHERE  id = @count 

      INSERT INTO #result 
      SELECT Replace(@strName, '"', ''), 
             Replace(splitdata, '"', '') 
      FROM   Splitstring(@strValue, ':') 
      WHERE  Isnumeric(Replace(splitdata, '"', '')) = 1 

      SET @count=@count + 1 
  END 

SELECT * 
FROM   #result 

SELECT * 
FROM   (SELECT colname, 
               colvalue 
        FROM   #result) DS 
       PIVOT ( Max(colvalue) 
             FOR [colname] IN ([derived_Attribute_1], 
                               [derived_Attribute_2], 
                               [derived_Attribute_3], 
                               [derived_Attribute_4]) ) PVT; 

DROP TABLE #table 

DROP TABLE #result 
1 голос
/ 24 января 2020

Я помещу это как второй ответ, так как это совершенно другой метод:

Вы можете использовать double-split-in-one- go как здесь :

declare @rawData nvarchar (max) =
'{"request_id":"801707","final_decision":"PASS","derived_Attribute_3":"PASS|Number of Total Active Institutions :3","derived_Attribute_1":"PASS|Number of active MFI :0","derived_Attribute_2":"PASS|Overdue Amount:0.0","derived_Attribute_4":"PASS|Total Exposure + Applied Amount :76384.0","derived_Attribute_5":"PASS|Write off amount:0.0","cbResponseMsg":"Final Decision:PASS || Number of Total Active Institutions :3 || Number of active MFI :0 || Overdue Amount:0.0 || Total Exposure + Applied Amount :76384.0 || Write off amount:0.0"}';

SELECT CastedToXml 
FROM (VALUES(CAST(CONCAT('<x><y>',REPLACE(REPLACE(REPLACE(REPLACE(@rawData,'{"',''),'"}',''),'":"','</y><y>'),'","','</y></x><x><y>'),'</y></x>') AS XML)))A(CastedToXml)

Это вернет XML, например:

<x>
  <y>request_id</y>
  <y>801707</y>
</x>
<x>
  <y>final_decision</y>
  <y>PASS</y>
</x>
<x>
  <y>derived_Attribute_3</y>
  <y>PASS|Number of Total Active Institutions :3</y>
</x>
<x>
  <y>derived_Attribute_1</y>
  <y>PASS|Number of active MFI :0</y>
</x>
<x>
  <y>derived_Attribute_2</y>
  <y>PASS|Overdue Amount:0.0</y>
</x>
<x>
  <y>derived_Attribute_4</y>
  <y>PASS|Total Exposure + Applied Amount :76384.0</y>
</x>
<x>
  <y>derived_Attribute_5</y>
  <y>PASS|Write off amount:0.0</y>
</x>
<x>
  <y>cbResponseMsg</y>
  <y>Final Decision:PASS || Number of Total Active Institutions :3 || Number of active MFI :0 || Overdue Amount:0.0 || Total Exposure + Applied Amount :76384.0 || Write off amount:0.0</y>
</x>

С помощью такого запроса вы можете прочитать значения атрибута:

SELECT CastedToXml.value('(/x[y[1]/text()="derived_Attribute_1"]/y[2]/text())[1]','nvarchar(max)') AS  derived_Attribute_1
      ,CastedToXml.value('(/x[y[1]/text()="derived_Attribute_2"]/y[2]/text())[1]','nvarchar(max)') AS  derived_Attribute_2
      ,CastedToXml.value('(/x[y[1]/text()="derived_Attribute_3"]/y[2]/text())[1]','nvarchar(max)') AS  derived_Attribute_3
      ,CastedToXml.value('(/x[y[1]/text()="derived_Attribute_4"]/y[2]/text())[1]','nvarchar(max)') AS  derived_Attribute_4
      ,CastedToXml.value('(/x[y[1]/text()="derived_Attribute_5"]/y[2]/text())[1]','nvarchar(max)') AS  derived_Attribute_5
FROM (VALUES(CAST(CONCAT('<x><y>',REPLACE(REPLACE(REPLACE(REPLACE(@rawData,'{"',''),'"}',''),'":"','</y><y>'),'","','</y></x><x><y>'),'</y></x>') AS XML)))A(CastedToXml);

Идея XPath заключается в следующем:

Найдите <x>, где first <y> 'text() - это заданное имя и прочитайте second <y> text() в виде строки.

Относительно отрезания необходимой части я предложил решение в своем другом ответе.
Подсказка: реверс, левый + charindex, реверс

1 голос
/ 24 января 2020

То, что у вас есть JSON, вы знаете это, казалось бы. Начиная с v2016, есть встроенная поддержка JSON, но с v2014 для этого потребуются некоторые хитрые строковые трюки (которые могут легко потерпеть неудачу) ...

Мое предложение состояло в том, чтобы преобразовать JSON в атрибут-центрированный XML:

declare @rawData nvarchar (max) =
'{"request_id":"801707","final_decision":"PASS","derived_Attribute_3":"PASS|Number of Total Active Institutions :3","derived_Attribute_1":"PASS|Number of active MFI :0","derived_Attribute_2":"PASS|Overdue Amount:0.0","derived_Attribute_4":"PASS|Total Exposure + Applied Amount :76384.0","derived_Attribute_5":"PASS|Write off amount:0.0","cbResponseMsg":"Final Decision:PASS || Number of Total Active Institutions :3 || Number of active MFI :0 || Overdue Amount:0.0 || Total Exposure + Applied Amount :76384.0 || Write off amount:0.0"}';

SELECT CAST(REPLACE(REPLACE(REPLACE(REPLACE(@rawData,'{"','<rawData '),'":"','="'),'","','" '),'"}','"/>') AS XML);

Результат:

<rawData request_id="801707" 
         final_decision="PASS" 
         derived_Attribute_3="PASS|Number of Total Active Institutions :3" 
         derived_Attribute_1="PASS|Number of active MFI :0" 
         derived_Attribute_2="PASS|Overdue Amount:0.0" 
         derived_Attribute_4="PASS|Total Exposure + Applied Amount :76384.0" 
         derived_Attribute_5="PASS|Write off amount:0.0" 
         cbResponseMsg="Final Decision:PASS || Number of Total Active Institutions :3 || Number of active MFI :0 || Overdue Amount:0.0 || Total Exposure + Applied Amount :76384.0 || Write off amount:0.0" />

Теперь мы можем использовать запрос для получения значения каждого отдельного атрибута.

SELECT B.* 
FROM (VALUES(CAST(REPLACE(REPLACE(REPLACE(REPLACE(@rawData,'{"','<rawData '),'":"','="'),'","','" '),'"}','"/>') AS XML)))A(CastedToXml)
CROSS APPLY(VALUES(CastedToXml.value('(/rawData/@request)[1]','int') 
                  ,CastedToXml.value('(/rawData/@final_decision)[1]','varchar(100)')
                  ,CastedToXml.value('(/rawData/@derived_Attribute_1)[1]','varchar(1000)') 
                  ,CastedToXml.value('(/rawData/@derived_Attribute_2)[1]','varchar(1000)') 
                  ,CastedToXml.value('(/rawData/@derived_Attribute_3)[1]','varchar(1000)') 
                  ,CastedToXml.value('(/rawData/@derived_Attribute_4)[1]','varchar(1000)') 
                  ,CastedToXml.value('(/rawData/@derived_Attribute_5)[1]','varchar(1000)') 
                  ,CastedToXml.value('(/rawData/@cbResponseMsg)[1]','varchar(1000)'))
            )B(request,final_decision,derived_Attribute_1,derived_Attribute_2,derived_Attribute_3,derived_Attribute_4,derived_Attribute_5,cbResponseMsg);

Это позволяет использовать простые строковые методы для каждой извлеченной строки.

Последний запрос

Это приводит нас к последнему запросу. Посмотрите, как я использую REVERSE(), чтобы вернуть прочитанные элементы справа налево. Это позволяет искать первый : и использовать LEFT(), чтобы отрезать эту часть. Для окончательного результата мы также должны использовать REVERSE() на обрезанной строке.

SELECT B.request_id 
      ,B.final_decision
      ,REVERSE(LEFT(B.derived_Attribute_1,CHARINDEX(':',B.derived_Attribute_1)-1)) AS derived_Attribute_1
      ,REVERSE(LEFT(B.derived_Attribute_2,CHARINDEX(':',B.derived_Attribute_2)-1)) AS derived_Attribute_2
      ,REVERSE(LEFT(B.derived_Attribute_3,CHARINDEX(':',B.derived_Attribute_3)-1)) AS derived_Attribute_3
      ,REVERSE(LEFT(B.derived_Attribute_4,CHARINDEX(':',B.derived_Attribute_4)-1)) AS derived_Attribute_4
      ,REVERSE(LEFT(B.derived_Attribute_5,CHARINDEX(':',B.derived_Attribute_5)-1)) AS derived_Attribute_5
      ,B.cbResponseMsg
FROM (VALUES(CAST(REPLACE(REPLACE(REPLACE(REPLACE(@rawData,'{"','<rawData '),'":"','="'),'","','" '),'"}','"/>') AS XML)))A(CastedToXml)
CROSS APPLY(VALUES(CastedToXml.value('(/rawData/@request_id)[1]','int') 
                  ,CastedToXml.value('(/rawData/@final_decision)[1]','varchar(100)')
                  ,REVERSE(CastedToXml.value('(/rawData/@derived_Attribute_1)[1]','varchar(1000)')) 
                  ,REVERSE(CastedToXml.value('(/rawData/@derived_Attribute_2)[1]','varchar(1000)')) 
                  ,REVERSE(CastedToXml.value('(/rawData/@derived_Attribute_3)[1]','varchar(1000)')) 
                  ,REVERSE(CastedToXml.value('(/rawData/@derived_Attribute_4)[1]','varchar(1000)')) 
                  ,REVERSE(CastedToXml.value('(/rawData/@derived_Attribute_5)[1]','varchar(1000)')) 
                  ,CastedToXml.value('(/rawData/@cbResponseMsg)[1]','varchar(1000)'))
            )B(request_id,final_decision,derived_Attribute_1,derived_Attribute_2,derived_Attribute_3,derived_Attribute_4,derived_Attribute_5,cbResponseMsg);

Результат

+------------+----------------+---------------------+---------------------+---------------------+---------------------+---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| request_id | final_decision | derived_Attribute_1 | derived_Attribute_2 | derived_Attribute_3 | derived_Attribute_4 | derived_Attribute_5 | cbResponseMsg                                                                                                                                                                      |
+------------+----------------+---------------------+---------------------+---------------------+---------------------+---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 801707     | PASS           | 0                   | 0.0                 | 3                   | 76384.0             | 0.0                 | Final Decision:PASS || Number of Total Active Institutions :3 || Number of active MFI :0 || Overdue Amount:0.0 || Total Exposure + Applied Amount :76384.0 || Write off amount:0.0 |
+------------+----------------+---------------------+---------------------+---------------------+---------------------+---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 голос
/ 24 января 2020

Ok

Здесь я использую XMl способ разбиения строки на сервере 2014 sql.

  1. first remove {}
  2. Split string on ","
  3. Разделить каждую подстроку на ":", чтобы получить поля Name и Value, dn удалить "

Теперь строка

"derived_Attribute_3":"PASS|Number of Total Active Institutions :3"

становится

Name= derived_Attribute_3
Value= PASS|Number of Total Active Institutions :3

Наконец, я беру это значение, нахожу последний «:» и делю строку на него, чтобы получить значение справа, которое «3» в примере

. Решение выглядит так:

declare @rawData nvarchar (max) =
'{"request_id":"801707","final_decision":"PASS","derived_Attribute_3":"PASS|Number of Total Active Institutions :3","derived_Attribute_1":"PASS|Number of active MFI :0","derived_Attribute_2":"PASS|Overdue Amount:0.0","derived_Attribute_4":"PASS|Total Exposure + Applied Amount :76384.0","derived_Attribute_5":"PASS|Write off amount:0.0","cbResponseMsg":"Final Decision:PASS || Number of Total Active Institutions :3 || Number of active MFI :0 || Overdue Amount:0.0 || Total Exposure + Applied Amount :76384.0 || Write off amount:0.0"}'
;with cte AS
(
/*Split in name and value by ":" and remove " */
select replace(left(Item,patindex('%":"%',ITem)),'"','') Name
,replace(Right(Item,len(item)-patindex('%":"%',ITem)-1),'"','') Value
from
(
/* use xml to split string by , and remove  {}*/
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
      FROM 
      ( 
        SELECT x = CONVERT(XML, '<i>' 
          + replace(replace(REPLACE(@rawData, ',', '</i><i>') ,'}',''),'{','')
          + '</i>').query('.')
      ) AS a CROSS APPLY x.nodes('i') AS y(i)
) a
)
,unpacked AS
(
/* Finally look in the value column, and find the number to the right of : */
SELECT
Name
,right(value,charindex(':',reverse(Value))-1) Value
from cte
where charindex(':',reverse(Value)) between 1 and len(Value)-1
and name like 'derived_attribute%'

)
SELECT
     max(iif(name='derived_Attribute_1',value,null)) derived_Attribute_1
    ,max(iif(name='derived_Attribute_2',value,null)) derived_Attribute_2
    ,max(iif(name='derived_Attribute_3',value,null)) derived_Attribute_3
    ,max(iif(name='derived_Attribute_4',value,null)) derived_Attribute_4
    ,max(iif(name='derived_Attribute_5',value,null)) derived_Attribute_5
from unpacked
1 голос
/ 24 января 2020

Если вы используете SQL 2014 или более раннюю версию, вам понадобится функция разделения строк, например, из Джефф Моден

Затем вам нужно будет разбить строку дважды, один раз на ",", а затем на ":" И затем используйте условное агрегирование

Как это

;WITH cteSplitVals
AS(
    SELECT CSV_ItemNumber=CSV.ItemNumber,COL.*
    FROM dbo.DelimitedSplit8K(replace(@rawData, '"', ''), ',') CSV
    CROSS APPLY
         dbo.DelimitedSplit8K(CSV.Item, ':') 
          COL
)
SELECT  
    derived_Attribute_1=MAX(CASE WHEN X.ColumnRow=4 THEN X.ColumnValue END),
    derived_Attribute_2=MAX(CASE WHEN X.ColumnRow=5 THEN X.ColumnValue END),
    derived_Attribute_3=MAX(CASE WHEN X.ColumnRow=3 THEN X.ColumnValue END),
    derived_Attribute_4=MAX(CASE WHEN X.ColumnRow=6 THEN X.ColumnValue END),
    derived_Attribute_5=MAX(CASE WHEN X.ColumnRow=7 THEN X.ColumnValue END)
FROM(
    SELECT  ColumnRow=P.CSV_ItemNumber, ColumnName=P.Item, ColumnValue = NULL
    FROM    cteSplitVals    P
    WHERE   P.CSV_ItemNumber IN (3, 4, 5, 6, 7)
    AND     P.ItemNumber IN (1)
    UNION
    SELECT  ColumnRow=P.CSV_ItemNumber, ColumnName=NULL, ColumnValue = P.Item
    FROM    cteSplitVals    P
    WHERE   P.CSV_ItemNumber IN (3, 4, 5, 6, 7)
    AND     P.ItemNumber IN (3)
) X

Чтобы получить это

derived_Attribute_1 derived_Attribute_2 derived_Attribute_3 derived_Attribute_4 derived_Attribute_5
0                   0.0                 3                   76384.0                 0.0

ALl это при условии, что порядок строк остается неизменным, в противном случае вам нужно будет использовать Dynami c SQL

...