Data transfer: JSON โ†’ SQL - sql

๐Ÿ˜Š
I would like to ask You for some help.
In our production I would like to transfer many kind of data and I've decided to choose using of JSON for that.
I've created the JSON string with all data on the side of the app and now I need to store it into my DB by using of stored procedure.
Everything is good, but in some case I've met a little issue.
Request
I need to know how many defective pieces should be sent to some process (need to know: which process, what defect and how many...) ๐Ÿ˜‰
Sorry for this confused description, but bellow is an example
Here is the part of JSON which I need to transform:
{"production":{"repairs":{"2":{"1":3},"4":{"3":5},"7":{"2":2,"4":4}}}}
Here is the required output:
RowID
ProcessID
ErrorID
Amount
1
2
1
3
2
4
3
5
3
7
2
2
4
7
4
4
Test:
Here is some SQL script which makes exactly what I want, but I can't use it because it doesn't work in stored procedures...
DECLARE #json NVARCHAR(MAX) = '{"production":{"repairs":{"2":{"1":3},"4":{"3":5},"7":{"2":2,"4":4}}}}'
DECLARE #helper INT = 0
DECLARE #counter INT = 0
DECLARE #RepairData TABLE (RowID INT NOT NULL IDENTITY, ProcessID INT, ErrorID INT, Amount INT)
SELECT ROW_NUMBER() OVER(ORDER BY CAST("key" AS INT) ASC) AS 'Row', CAST("key" AS INT) AS 'ProcessID'
INTO #RepairProcesses
FROM OPENJSON(#json, '$.production.repairs')
WHILE #counter < (SELECT COUNT("key") FROM OPENJSON(#json, '$.production.repairs'))
BEGIN
SET #counter = #counter + 1
SET #helper = (SELECT ProcessID FROM #RepairProcesses WHERE Row = #counter)
INSERT INTO #RepairData (ProcessID, ErrorID, Amount)
SELECT #helper AS 'ProcessID', CAST("key" AS INT) AS 'ErrorID', CAST("value" AS INT) AS 'Amount'
FROM OPENJSON(#json, '$.production.repairs."'+CAST(#helper AS NVARCHAR(3))+'"')
END
DROP TABLE #RepairProcesses
SELECT * FROM #RepairData
Output:
RowID|ProcessID|ErrorID|Amount|
-----+---------+-------+------+
1| 2| 1| 3|
2| 4| 3| 5|
3| 7| 2| 2|
4| 7| 4| 4|
Summary:
The reason why I can't use that is because I've used the WHILE loop and iteration for ProcessID in it and the stored procedures for some reason return syntax error in line where I'm using concatenation of the path string in the function OPENJSON. (Even when it works in the classic "query mode" well... ๐Ÿคจ)
Error message (only within stored procedures):
SQL Error [102] [S0001]: Incorrect syntax near '+'.
The error occurs even when I don't use the concatenation and use the whole path string as the parameter...
Seems like the function OPENJSON in stored procedures needs for the path string only absolute value in the ' '...
My request:
Guys I would like to ask You if somebody knows how to solve it in better way (maybe even without the WHILE loop?)...
Thanks a lot. ๐Ÿ˜‰

I think you need this:
DECLARE #json NVARCHAR(MAX) = '{"production":{"repairs":{"2":{"1":3},"4":{"3":5},"7":{"2":2,"4":4}}}}'
SELECT
RowId = ROW_NUMBER() OVER (ORDER BY CONVERT(int, j1.[key]), CONVERT(int, j2.[key])),
ProcessID = j1.[key],
ErrorID = j2.[key],
Amount = j2.[value]
FROM OPENJSON(#json, '$.production.repairs') j1
CROSS APPLY OPENJSON(j1.value) j2
Result:
RowId
ProcessID
ErrorID
Amount
1
2
1
3
2
4
3
5
3
7
2
2
4
7
4
4

Related

How can I find in SQL for a match of 15 characters out of 17?

I have a table where a field is varchar(17) and contains 17 char long phrases. They are alphanumeric. I need to add another row this table, but before I do, I need to check for possible duplicates. The logic for duplicates needs to be if any 15 characters of the new phrase matches with any 15 characters to a record in the table.
Samples:
5 Table rows:
1N6BF0KM2HN802620
1N6BF0KMXHN801974
1N6BF0LYXHN811101
1N9BF0KM6HN800482
1N12F0LY4HN809375
New phrase to add: 1N6BF0KAXHN802974
Found in table row 2: 1N6BF0KMXHN801974
Duplicate: Does not match 2 charaters only
New phrase to add: 109BF0KM6HN800492
Found in table row 4: 1N9BF0KM6HN800482
Duplicate: Does not match 2 charaters only
New phrase to add: 1N12F0LY4HN709375
Found in table row 5: 1N12F0LY4HN809375
Not Duplicate: Does not match 1 charaters
New phrase to add: 1N6AF0BYXHN911101
Found in table row 3: 1N6BF0LYXHN811101
Not Duplicate: Does not match more than 2 charaters
I try searching for regular expression or any algorism.
Here is a solution using
a function which compares a pair of string character by character and returns the number of matches
which uses the function to compare a string with all the values already in the table before entering it.
I don't think that regex is appropriate because we would need to count the number of matches with different regex pattern fir each existing entry. Regex is good for matching to a pattern which doesn't change.
create table sample (Samples char(17));
insert into sample values
('1N6BF0KM2HN802620'),
('1N6BF0KMXHN801974'),
('1N6BF0LYXHN811101'),
('1N9BF0KM6HN800482'),
('1N12F0LY4HN809375');
CREATE FUNCTION dbo.countMatches
(
#string1 CHAR(17) ,
#string2 CHAR(17)
)
returns int
AS
BEGIN
DECLARE #IsMatching int =0 ;
DECLARE #loopCount INT = 0;
SET #IsMatching = 0;
WHILE #loopCount < 17
BEGIN
IF(
SUBSTRING(#string1,#loopCount+1,1)
= SUBSTRING(#string2,#loopCount+1,1)
)
BEGIN
SET #IsMatching = #IsMatching + 1;
END;
SET #loopCount = #loopCount + 1;
END
RETURN #IsMatching
END
create procedure insertSample (#newSample char(17))
as
begin
DECLARE #sample char(17);
declare #IsMatching int;
declare #thisMatching int = 0;
DECLARE sample_Cursor CURSOR
FOR SELECT samples FROM sample
OPEN sample_Cursor
FETCH NEXT FROM sample_Cursor INTO #sample
WHILE ##FETCH_STATUS = 0
BEGIN
set #IsMatching=dbo.countMatches(
#sample, #newSample);
FETCH NEXT FROM sample_Cursor INTO #sample
if (#IsMatching > #thisMatching)
begin
set #thisMatching = #IsMatching;
end
END
CLOSE sample_Cursor
DEALLOCATE sample_Cursor
if (#thisMatching > 14)
RAISERROR (
'too many matching characters',16, 1)
else
begin
insert into sample values(#newSample);
select 'ok accepted' result,
#thisMatching numMatches;
end
RETURN;
end
EXEC insertSample '1N6BF0KAXHN802974'
GO
Msg 50000 Level 16 State 1 Line 26
too many matching characters
EXEC insertSample '109BF0KM6HN800492'
GO
Msg 50000 Level 16 State 1 Line 26
too many matching characters
EXEC insertSample '1N12F0LY4HN709375'
GO
Msg 50000 Level 16 State 1 Line 26
too many matching characters
EXEC insertSample '1N6AF0BYXHN911101'
GO
result | numMatches
:---------- | ---------:
ok accepted | 14
select * from sample;
GO
| Samples |
| :---------------- |
| 1N6BF0KM2HN802620 |
| 1N6BF0KMXHN801974 |
| 1N6BF0LYXHN811101 |
| 1N9BF0KM6HN800482 |
| 1N12F0LY4HN809375 |
| 1N6AF0BYXHN911101 |
SELECT dbo.countMatches(
'1N12F0LY4HN709375',
'1N12F0LY4HN709366');
| (No column name) |
| ---------------: |
| 15 |
db<>fiddle here

custom aggregate function in postgres return NULL value

in order to create a more complex custom aggregate function, i followed first this amazing tutorial.
Here the data i use :
create table entries(
id serial primary key,
amount float8 not null
);
select setseed(0);
insert into entries(amount)
select (2000 * random()) - 1000
from generate_series(1, 1000000);
So I have this table :
id | amount | running_total
---------+-----------------------+--------------------
1 | -462.016298435628 | -462.016298435628
2 | 162.440904416144 | -299.575394019485
3 | -820.292402990162 | -1119.86779700965
4 | -866.230697371066 | -1986.09849438071
5 | -495.30001822859 | -2481.3985126093
6 | 772.393747232854 | -1709.00476537645
7 | -323.866365477443 | -2032.87113085389
8 | -856.917716562748 | -2889.78884741664
9 | 285.323366522789 | -2604.46548089385
10 | -867.916810326278 | -3472.38229122013
-- snip --
And I would like the max of the running_total column
(I know I can do do it without a new aggregate function, but it's for the demonstration)
So i've made this aggregate function
create or replace function grt_sfunc(agg_state point, el float8)
returns point
immutable
language plpgsql
as $$
declare
greatest_sum float8;
current_sum float8;
begin
current_sum := agg_state[0] + el;
greatest_sum := 40;
/*if agg_state[1] < current_sum then
greatest_sum := current_sum;
else
greatest_sum := agg_state[1];
end if;*/
return point(current_sum, greatest_sum);
/*return point(3.14159, 0);*/
end;
$$;
create or replace function grt_finalfunc(agg_state point)
returns float8
immutable
strict
language plpgsql
as $$
begin
return agg_state[0];
end;
$$;
create or replace aggregate greatest_running_total (float8)
(
sfunc = grt_sfunc,
stype = point,
finalfunc = grt_finalfunc
);
Normally it sould work, but in the end, it gives me a null result :
select greatest_running_total(amount order by id asc)
from entries;
id | running_total
---------+---------------
1 | [NULL]
I tried to change the type of the data, to check the 2 first aggregate functions separately, they are working well. Does someone could help me find a solution please ? :)
Thank you very much !
You need to set a non-NULL initcond for the aggregate. Presumably that would be (0,0), or maybe negative very large numbers for each? Or manually check for the agg_state being NULL.
Also, it seems like your grt_finalfunc should be returning subscript [1], not [0].
So, the solution was to add an initial condition. Indeed, without initial condition, the first value is considered as NULL :D (thank you #jjanes and #The Impaler)
So I corrected ma aggregated function :
create or replace aggregate greatest_running_total (float8)
(
sfunc = grt_sfunc,
stype = point,
finalfunc = grt_finalfunc,
initcond = '(0,0)'
);
And, indeed SQL indexes its tables from 1 and not from 0... Here was my second mistake,
Thank you very much !!

How to capture the last column data with SQL Server to insert into database

I would like to get the last column data and insert into database.
This is the sample data.
================================= Yield =======================================================
S1| S2| S3| S4| S5| S6| S7| Pensoft|
.# Inspected =| 2506| 2507| 2500| 0| 2487| 2513| 2691| 2535|
.# Pass =| 2495| 2480| 2485| 0| 2487| 2513| 2691| 2503|
.# Fail =| 11| 0| 14| 0| 0| 0| 0| 26|
.# Invalid =| 0| 27| 1| 0| 0| 0| 0| 6|
.Yield =| 99.56| 98.92| 99.40| 0.00| 100.00| 100.00| 100.00| 98.73|
In the table there is "Pensoft" column. I need to capture the "Inspected" row value in Pensoft column. The "Pensoft" column is total. I can't get the value into the database it keep showing
Error converting data type varchar to numeric.
Below is my code. Is it I have missing something?
ELSE IF #ls_long_line LIKE '% S1| S2| S3| S4| S5| S6| S7| Pensoft|%'
BEGIN
SELECT #li_total_index = charindex('Pensoft',#ls_long_line) -1
END
ELSE IF CHARINDEX('.# Inspected =|' ,#ls_long_line) > 0
BEGIN
print #ls_long_line
select #li_pos = charindex('|',#ls_long_line)
--INSPECTED---
select #li_pos = charindex('|',#ls_long_line,#li_pos)
select #ls_temp = replace(ltrim(rtrim(SUBSTRING (#ls_long_line,#li_pos,len(#ls_long_line)))) ,' ','')
SELECT #ls_total_units_tested = convert(decimal(9,0),#ls_temp)
END
I get this messages when I execute it. May I know where did I did wrong and how can I change it? This is to read text file data and the value will be in 5 digit also. How can I get it on the last column data?
FileName :D:\TESTENG\DATA\BACKEND\PENTA\P0248591_NOV92020_0H47M_SERVER.TXT
.# INSPECTED =| 2506| 2507| 2500| 0| 2487| 2513| 2691| 2535|
Msg 8114, Level 16, State 5, Procedure load_PENTA, Line 320
Error converting data type varchar to numeric.
Unlike what you said before, your code handles your input like it comes from a text file and not from an existing (sample) table in your database... You are looping over the input lines and storing them in #ls_long_line and parsing each line (whether it contains a header or values).
Reproduction
Removing the header bit and adding some comments gives this bit of runnable code that reproduces your error:
-- input
declare #ls_long_line nvarchar(max) = '.# Inspected =| 2506| 2507| 2500| 0| 2487| 2513| 2691| 2535|';
select #ls_long_line as Input;
-- process
declare #li_pos int;
declare #ls_temp nvarchar(100);
declare #ls_total_units_tested decimal(9,0);
IF CHARINDEX('.# Inspected =|', #ls_long_line) > 0
BEGIN
-- (1) find starting |
select #li_pos = charindex('|', #ls_long_line);
-- (2) find next |
select #li_pos = charindex('|', #ls_long_line, #li_pos);
-- (3) remove row label and spaces
declare #substr nvarchar(100) = SUBSTRING(#ls_long_line, #li_pos, len(#ls_long_line));
select #ls_temp = replace(ltrim(rtrim(#substr)),' ' , '');
-- (4) intermediate result
select #ls_temp as Intermediate;
-- (5) attempt to convert intermediate result to numeric value
SELECT #ls_total_units_tested = convert(decimal(9,0), #ls_temp);
END
-- result
select #ls_total_units_tested as Result;
My additional comments (1) and (2) already show where you are going wrong. Commenting out the select from part (5) will show the intermediate result #ls_temp:
|2506|2507|2500|0|2487|2513|2691|2535|
This entire string value obviously cannot be converted to a single number, which explains your error.
Solution
Fix parts (1) and (2) to get the proper positions of the last two separators |.
Correct part (3) to select a substring with only the right numbers (using the positions from step 1.).
Converting the substring in part (5) should no longer produce an error.
Additional comments:
Why use a decimal with 0 digits after the decimal place (decimal(9,0))? Using an int looks more appropriate.
If your SQL Server version allows it (2016 and higher), then the STRING_SPLIT() function could help you to parse the columns.
To get the value between the last two | characters, after the print #ls_long_line line, you can use the following code (I used this solution to construct the code: Is there a LastIndexOf in SQL Server?):
declare #ls_long_line_Temp as varchar(MAX)
select #ls_long_line_Temp = left(#ls_long_line, len(#ls_long_line) - charindex('|', reverse(#ls_long_line) + '|'))
select #ls_temp = right(#ls_long_line_Temp, charindex('|', reverse(#ls_long_line_Temp) + '|') - 1)

Removing varchar(s) after a semicolon

So I have a lot of data like this:
pix11co;10.115.0.1
devapp087co;10.115.0.100
old_main-mgr;10.115.0.101
radius03co;10.115.0.110
And I want to delete the stuff after the ; so it just becomes
pix11co
devapp087co
old_main-mgr
radius03co
Since they're all different I can live with the semi-colon staying there.
I have the following query and it runs successfully but doesn't delete anything.
UPDATE dns$ SET [Name;] = REPLACE ([Name;], '%_;%__________%', '%_;');
What wildcards can I use to specify the characters after the ; ?
Can you use CHARINDEX? E.g.:
SELECT LEFT('pix11co;10.115.0.1', CHARINDEX(';', 'pix11co;10.115.0.1') - 1)
You can use SUBSTRING() and CHARINDEX() functions:
CREATE TABLE MyStrings (
STR VARCHAR(MAX)
);
INSERT INTO MyStrings VALUES
('pix11co;10.115.0.1'),
('devapp087co;10.115.0.100'),
('old_main-mgr;10.115.0.101'),
('radius03co;10.115.0.110');
SELECT STR, SUBSTRING(STR, 1, CHARINDEX(';', STR) -1 ) AS Result
FROM MyStrings;
Results:
+---------------------------+--------------+
| STR | Result |
+---------------------------+--------------+
| pix11co;10.115.0.1 | pix11co |
| devapp087co;10.115.0.100 | devapp087co |
| old_main-mgr;10.115.0.101 | old_main-mgr |
| radius03co;10.115.0.110 | radius03co |
+---------------------------+--------------+

SQLite- Normalizing a concatenated field and joining with it?

I have some data stored in comma-separated values in a field and I want to turn those comma-separated values into a temporary table and use those to join to another table
CREATE TABLE STRATEGY (STRATEGY_ID INTEGER PRIMARY KEY, APPLIED_SET_IDS VARCHAR);
CREATE TABLE ACTION_SET (APPLIED_ACTION_SET_ID INTEGER PRIMARY KEY, VALUE VARCHAR);
+-----------+---------------+
|STRATEGY_ID|APPLIED_SET_IDS|
+-----------+---------------+
| 1|1,3,6,7 |
| 2|1,2,4 |
+---------------------+-----+
|APPLIED_ACTION_SET_ID|VALUE|
+---------------------+-----+
| 1|X |
| 2|Y |
| 3|Z |
| 4|H |
| 5|I |
| 6|J |
| 7|K |
| 8|L |
I know I have to use some form of recursion as shown here. But every attempt I've done has made my head spin a bit. And my temporary table needs to preserve the original concatenated order of APPLIED_SET_ID values as well, like this...
+-----------+-----+--------------+
|STRATEGY_ID|ORDER|APPLIED_SET_ID|
+-----------+-----+--------------+
| 1| 1| 1|
| 1| 2| 3|
| 1| 3| 6|
| 1| 4| 7|
| 2| 1| 1|
| 2| 2| 2|
| 2| 3| 4|
Ultimately, I will join this table to the second existing table and use GROUP_CONCAT to replace the ID's with the corresponding values in the same order.
+-----------+------------------+
|STRATEGY_ID|APPLIED_SET_VALUES|
+-----------+------------------+
| 1|X,Z,J,K |
| 2|X,Y,H |
So regular expressions are out thanks to the order (otherwise I could have turned the commas to pipes and joined on a REGEXP statement). How can I achieve this? I know this is not normalized but I need to work with this current structure. Thank you for any help in advance.
It is possible to call PHP functions from SQLite. It makes it possible to use simple queries to 'normalize' the table.
I have converted the 'normalize comma delimited strings holding keys' to SQLite. see: (joining on ';' separated values in a column) for a more complete explanation
I started out looking for ways of converting the functions to run in SQLite. After some searching I came across this: Working with PHP UDFs in SQLite.
Which I found interesting - call PHP functions from SQLite. That sounds like fun!
It works but you cannot use PDO. You have to use the SQLite functions directly. Vendor Specific Database Extensions: SQLite3
Updated with your data (see previous edits for working code for other question)
The code:
<?php // Q34231542 -- count_in_set, value_in_set
/*
* See this question for a rather more complete explanation of what this is doing...
*
* https://stackoverflow.com/questions/33782728/can-i-resolve-this-with-pure-mysql-joining-on-separated-values-in-a-column/
*/
define('SQLITE_DB', __DIR__ .'/Q34231542.sqlite');
/**
* #var SQLite3
*/
$db = new SQLite3(SQLITE_DB);
/*
* Define the functions for use by SQLite.
*/
$db->createFunction('count_in_set', 'count_in_set', 2);
$db->createFunction('value_in_set', 'value_in_set', 3);
$sql ="
SELECT STRATEGY.STRATEGY_ID as 'applied_strategy_id',
STRATEGY.APPLIED_SET_IDS as 'applied_strategy_list',
isequence.id as 'which_strategy',
COUNT_IN_SET(STRATEGY.APPLIED_SET_IDS, ',') as 'StrategyCount',
VALUE_IN_SET(STRATEGY.APPLIED_SET_IDS, ',', isequence.id)
as 'TheStrategy',
ACTION_SET.VALUE as 'Action_Set_Value'
FROM STRATEGY
JOIN integerseries AS isequence
ON isequence.id <= COUNT_IN_SET(STRATEGY.APPLIED_SET_IDS, ',') /* normalize */
JOIN ACTION_SET
ON ACTION_SET.APPLIED_ACTION_SET_ID = VALUE_IN_SET(STRATEGY.APPLIED_SET_IDS, ',', isequence.id)
ORDER BY
STRATEGY.STRATEGY_ID , ACTION_SET.APPLIED_ACTION_SET_ID;
";
/*
* Run the query
*/
$stmt = $db->prepare($sql);
$result = $stmt->execute();
/*
* Get the results
*/
$rows = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) { // fetch all the rows for now...
$rows[] = $row;
}
/*
* output...
*/
// \Kint::dump($rows);
echo '<pre>';
var_dump($rows);
echo '</pre>';
exit;
/* -------------------------------------------------------------------------
* The PHP functions called from SQLite
*/
/**
* Count the number of delimited items in a string
*
* #param string $delimitedValues
* #param string $delim
* #return integer
*/
function count_in_set($delimitedValues, $delim)
{
return substr_count(trim($delimitedValues, $delim), $delim) + 1;
}
/**
* Treat the delimited values as ONE BASED array.
*
* #param string $delimitedValues
* #param string $delim
* #param integer $which
* #return string
*/
function value_in_set($delimitedValues, $delim, $which)
{
$items = explode($delim, $delimitedValues);
return $items[$which - 1];
}
The output:
applied_strategy_id applied_strategy_list which_strategy StrategyCount TheStrategy Action_Set_Value
#1 1 "1,3,6,7" 1 4 "1" "X"
#2 1 "1,3,6,7" 2 4 "3" "Z"
#3 1 "1,3,6,7" 3 4 "6" "J"
#4 1 "1,3,6,7" 4 4 "7" "K"
#5 2 "1,2,4" 1 3 "1" "X"
#6 2 "1,2,4" 2 3 "2" "Y"
#7 2 "1,2,4" 3 3 "4" "H"
The data:
CREATE TABLE [integerseries] (
[id] INTEGER NOT NULL PRIMARY KEY);
INSERT INTO "integerseries" VALUES(1);
INSERT INTO "integerseries" VALUES(2);
INSERT INTO "integerseries" VALUES(3);
INSERT INTO "integerseries" VALUES(4);
INSERT INTO "integerseries" VALUES(5);
INSERT INTO "integerseries" VALUES(6);
INSERT INTO "integerseries" VALUES(7);
INSERT INTO "integerseries" VALUES(8);
INSERT INTO "integerseries" VALUES(9);
INSERT INTO "integerseries" VALUES(10);
CREATE TABLE STRATEGY (STRATEGY_ID INTEGER PRIMARY KEY, APPLIED_SET_IDS VARCHAR);
INSERT INTO "STRATEGY" VALUES(1,'1,3,6,7');
INSERT INTO "STRATEGY" VALUES(2,'1,2,4');
CREATE TABLE ACTION_SET (APPLIED_ACTION_SET_ID INTEGER PRIMARY KEY, VALUE VARCHAR);
INSERT INTO "ACTION_SET" VALUES(1,'X');
INSERT INTO "ACTION_SET" VALUES(2,'Y');
INSERT INTO "ACTION_SET" VALUES(3,'Z');
INSERT INTO "ACTION_SET" VALUES(4,'H');
INSERT INTO "ACTION_SET" VALUES(5,'I');
INSERT INTO "ACTION_SET" VALUES(6,'J');
INSERT INTO "ACTION_SET" VALUES(7,'K');
INSERT INTO "ACTION_SET" VALUES(8,'L');
My colleague developed a very clever solution, assuming the separator is a pipe | and not a comma ,.
He used REGEXP and the INSTR() function to get a numerical position, and that value drove the sorting.
SELECT STRATEGY_ID,
APPLIED_SET_IDS,
GROUP_CONCAT(VALUE,'|') as DESCRIPTION
FROM (
SELECT STRATEGY_ID,
APPLIED_SET_IDS,
CASE
WHEN APPLIED_ACTION_SET_ID = APPLIED_SET_IDS THEN 1
WHEN instr(APPLIED_SET_IDS, APPLIED_ACTION_SET_ID || '|') = 1 Then 1
WHEN instr(APPLIED_SET_IDS, '|' || APPLIED_ACTION_SET_ID || '|') > 0 Then instr(APPLIED_SET_IDS, '|' || APPLIED_ACTION_SET_ID || '|')
ELSE 999999
END AS APPLIED_ORDER,
VALUE
FROM STRATEGY
INNER JOIN ACTION_SET
ON ACTION_SET.APPLIED_ACTION_SET_ID REGEXP '^(' || STRATEGY.APPLIED_SET_IDS || ')$'
ORDER BY APPLIED_ORDER
) DESCRIPTIONS
GROUP BY 1,2
This gave me the exact output I was looking for.