I have a whole SWIFT message with fees in one "cell" as SwiftMessage.Body. So the whole message is in one string. What I need to do is, extract certain data from it. Using SSRS and MS SQL The message looks something like this:
.....(FEE 1)
:20C::PCOM//C22033100734330
:20C::PREF//FC22033100734330
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//NEUR50
:99A::DAAC//001
.....(FEE 2)
:20C::PCOM//C22033100734331
:20C::PREF//FC22033100734331
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//EUR40
:99A::DAAC//002
.....(FEE n)
there can be any number of fees, not just 2
The result should be extracting :20C::PREF// and :19A::AMCO//:
ID | Amount |
---|---|
FC22033100734330 | -50 |
FC22033100734331 | 40 |
what I have right now
SELECT
SUBSTRING(swf.SwiftMessage.Body, (CHARINDEX(':20C::PREF', swf.SwiftMessage.Body) 12)
, CHARINDEX(':22H::PNTP',swf.SwiftMessage.Body) - CHARINDEX(':20C::PREF', swf.SwiftMessage.Body) - 12) as REF1,
SUBSTRING(swf.SwiftMessage.Body, (CHARINDEX(':19A::AMCO', swf.SwiftMessage.Body) 12)
, CHARINDEX(':99A::DAAC',swf.SwiftMessage.Body) - CHARINDEX(':19A::AMCO', swf.SwiftMessage.Body) - 12) as AMT
FROM
swf.SwiftMessage
so with this I am somehow able to extract data I need (the amount is in format currency amt which I can deal with later hopefuly). The main problem right now is how to deal with the fact, that there might be more fees, than just one so I need to make some kind of a loop? that will go through the whole string and find every :20C::PREF// and :19A::AMCO// values.
CodePudding user response:
First of all, I would suggest doing this splitting at a different place, either when inserting the data into the database, or after retrieving it, perhaps using the language that the program used to communicate with the DB.
If you really must do the splitting using SQL alone, you have a few options to loop over the Data. They either use Recursion, classic WHILE
loops or use the STRING_SPLIT
function, as mentioned in this post.
If you happen to use SQL Server 2016 or later, I suggest you use STRING_SPLIT
, something along the lines of
SELECT value FROM STRING_SPLIT('.....(FEE 1):20C::PCOM//C22033100734330:20C::PREF//FC22033100734330:22H::PNTP//SEFP:24B::ACTV//NEWP:19A::AMCO//NEUR50:99A::DAAC//001.....(FEE 2):20C::PCOM//C22033100734331:20C::PREF//FC22033100734331:22H::PNTP//SEFP:24B::ACTV//NEWP:19A::AMCO//EUR40:99A::DAAC//002.....(FEE n)', ':99A::DAAC');
this together with CROSS APPLY
whould get you a whole step closer to parsing the data for each message:
SELECT value FROM swf.SwiftMessage CROSS APPLY STRING_SPLIT(Body,':99A::DAAC')
then just substring each value
CodePudding user response:
Here is an option which parses the string via JSON to maintain the SEQUENCE. Then it becomes a small matter of a conditional aggregation
Note: this is keyed on EUR. If you have other currencies, it would be a small tweak
Example
Declare @S varchar(max)='.....(FEE 1)
:20C::PCOM//C22033100734330
:20C::PREF//FC22033100734330
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//NEUR50
:99A::DAAC//001
.....(FEE 2)
:20C::PCOM//C22033100734331
:20C::PREF//FC22033100734331
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//EUR40
:99A::DAAC//002'
Select ID = max( case when value like ':20C::PREF//%' then substring(value,13,100) end)
,Amt = max( case when value like ':19A::AMCO//%' then try_convert(decimal(15,4),replace(replace(substring(value,13,100),'NEUR','-'),'EUR','')) end)
From (
Select *
,Grp = sum( case when value like '.....(FEE%' then 1 end ) over (order by convert(int,[key]))
From OpenJSON( '["' replace(string_escape(replace(@S,char(13) char(10),'|||'),'json'),'|||','","') '"]' )
) A
Group By Grp
Results
ID Amt
FC22033100734330 -50.0000
FC22033100734331 40.0000
CodePudding user response:
Give this a try:
DECLARE @swifty NVARCHAR(MAX) = '.....(FEE 1)
:20C::PCOM//C22033100734330
:20C::PREF//FC22033100734330
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//NEUR50
:99A::DAAC//001
.....(FEE 2)
:20C::PCOM//C22033100734331
:20C::PREF//FC22033100734331
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//EUR40
:99A::DAAC//002
.....(FEE 3)
:20C::PCOM//C22033100734332
:20C::PREF//FC22033100734332
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//USD9999
:99A::DAAC//001
.....(FEE 4)
:20C::PCOM//C22033100734333
:20C::PREF//FC22033100734333
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//GBP1
:99A::DAAC//001
.....(FEE 5)
:20C::PCOM//C22033100734334
:20C::PREF//FC22033100734334
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//NGBP300
:99A::DAAC//001
.....(FEE 6)
:20C::PCOM//C22033100734335
:20C::PREF//FC22033100734335
:22H::PNTP//SEFP
:24B::ACTV//NEWP
:19A::AMCO//NUSD325412254
:99A::DAAC//001
'
SELECT FeeID,
MAX(CASE WHEN value LIKE CHAR(10) ':20C::PREF//%' THEN SUBSTRING(value,CHARINDEX('//',value) 2,LEN(value)) END) AS ID,
MAX(CASE WHEN value LIKE CHAR(10) ':19A::AMCO//N%' THEN SUBSTRING(value,CHARINDEX('//',value) 3,3)
WHEN value LIKE CHAR(10) ':19A::AMCO//[^N]%' THEN SUBSTRING(value,CHARINDEX('//',value) 2,3) END) AS Currency,
MAX(CASE WHEN value LIKE CHAR(10) ':19A::AMCO//N%' THEN SUBSTRING(value,CHARINDEX('//',value) 6,LEN(value))*-1
WHEN value LIKE CHAR(10) ':19A::AMCO//[^N]%' THEN SUBSTRING(value,CHARINDEX('//',value) 5,LEN(value)) END) AS Fee
FROM (
SELECT Value, ((ROW_NUMBER() OVER (ORDER BY (SELECT 1))-1) / 7) 1 AS FeeID
FROM STRING_SPLIT(@swifty,CHAR(13))
) A
WHERE value LIKE CHAR(10) ':19A::AMCO//%'
OR value LIKE CHAR(10) ':20C::PREF//%'
GROUP BY FeeID
This shouldn't be reliant on the values being in any specific format or type, just that they are referenced as :19A::
and :20C::
FeeID ID Currency Fee
-------------------------------------------
1 FC22033100734330 EUR -50
2 FC22033100734331 EUR 40
3 FC22033100734332 USD 9999
4 FC22033100734333 GBP 1
5 FC22033100734334 GBP -300
6 FC22033100734335 USD -325412254