简体   繁体   中英

Extracting multiple values from string and ordering them

I have values within a Notes field consisting of values such as this where abc represents different words:

Request Notes: VAR - abc abc abc abc abc

Unfortunately the design of how the data is stored is very poor, and I need to pull out the Note Type for each "Required Notes:" record. It also needs ordering from most recent to oldest (last part of the string to first part of the string).

CREATE TABLE #TestData
(
    ClientID int,
    Notes varchar(8000)
)
insert into #TestData
(
    ClientID,
    Notes
)
select
    1,
    'Request Notes: VAR - abc abc abc abc abc'
union all
select
    2,
    'Request Notes: OZR - abc abc abc abc abc Request Notes: ACC - abc abc abc abc abc Request Notes: TYU - abc abc abc abc abc'
union all
select
    3,
    'Request Notes: TYU - abc abc abc abc abc Request Notes: VAR - abc abc abc abc abc'

This is how I expect the output to be for the above examples:

--Expected Output

Client ID       Type    Order
1               VAR     1
2               TYU     1
2               ACC     2
2               OZR     3
3               VAR     1
3               TYU     2

I have this together so far which extracts OZR, but I'm stumped on how to get the others and order the list into the expected output above.

DECLARE @Text varchar(500) = 'Request Notes: OZR - abc abc abc abc abc Request Notes: ACC - abc abc abc abc abc Request Notes: TYU - abc abc abc abc abc'

SELECT TRIM(REPLACE(REPLACE(SUBSTRING(@Text, CHARINDEX(':', @Text), CHARINDEX('-',@text) - CHARINDEX(':', @Text) + Len('-')),':',''),'-',''))

You can use openjson to extract your data as an array and filter:

select d.ClientId, n.*
from #testdata d
cross apply (
  select 
    Left(j.[value],3) [Type],
    Row_Number() over(order by Convert(int,j.[key]) desc) [Order] 
  from OpenJson(Concat('["',replace(notes,'Notes: ', '","'),'"]')) j
  where j.[value] != 'Request'
 )n;

Example Fiddle

Here's a recursive CTE version. It splits the string based on finding "Request Notes:" then does a left/right combo to extract the 3 letter code. It iterates the order as it builds. Then you select from the CTE and only take rows where there's some NotesRemainder:

    ;
WITH CTESplit
AS (
    SELECT ClientID,
           RIGHT(LEFT(Notes, CHARINDEX('Request Notes:', Notes) + 17), 3) AS NotesPart,
           RIGHT(Notes, LEN(Notes) - CHARINDEX('Request Notes:', Notes) - 17) AS NotesRemainder,
           -1 AS [Order]
    FROM #TestData
    WHERE Notes IS NOT NULL AND CHARINDEX('Request Notes:', Notes) > 0
    UNION ALL
    SELECT CTESplit.ClientID,
           RIGHT(LEFT(CTESplit.NotesRemainder, CHARINDEX('Request Notes:', CTESplit.NotesRemainder) + 17), 3),
           RIGHT(CTESplit.NotesRemainder, LEN(CTESplit.NotesRemainder) - CHARINDEX('Request Notes:', CTESplit.NotesRemainder)),
           CTESplit.[Order] - 1
    FROM CTESplit
    WHERE CTESplit.NotesRemainder IS NOT NULL AND CHARINDEX('Request Notes:', CTESplit.NotesRemainder) > 0
    UNION ALL
    SELECT CTESplit.ClientID,
           RIGHT(LEFT(CTESplit.NotesRemainder, CHARINDEX('Request Notes:', CTESplit.NotesRemainder) + 17), 3),
           NULL,
           CTESplit.[Order] - 1
    FROM CTESplit
    WHERE CTESplit.NotesRemainder IS NOT NULL AND CHARINDEX('Request Notes:', CTESplit.NotesRemainder) = 0
)
SELECT CS.ClientID,
       CS.NotesPart AS Type,
       CS.[Order] +(SELECT MIN([Order])*-1 FROM CTESplit WHERE ClientID = CS.ClientID) AS [Order]
FROM CTESplit AS CS
WHERE CS.NotesRemainder IS NOT NULL
ORDER BY CS.ClientID,
         CS.[Order] ASC;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM