问题
using mssql, if i have data such as:
cols:
id, name, list1, list2
1, 'first', '10;15;30;50', '25;12;15;18'
2, 'second', '50;30;15;10, '12;25;11;15'
...
10,'tenth', '9;2;15;1', '5;13;17;45'
im trying to create rows of results that join each of those list columns together, such as
1, 'first', 10, 25
1, 'first', 15, 12
1, 'first', 30, 15
1, 'first', 50, 18
2, 'second', 50, 12
2, 'second', 30, 25
2, 'second', 15, 11
2, 'second', 10, 15
...
10, 'tenth', 9, 5
10, 'tenth', 2, 13
10, 'tenth', 15, 17
10, 'tenth', 1, 45
basically, each number of each list maps to the same number at that index (split by ';').
i'm able to use cross apply
+ string_split
, but it results in a row for each possible combination (id * description * list1_size * list2_size)
is this even possible in sql?
I've also tried using substring
+ charindex
to manually move around the lists, but this would result in an exorbitant amount of manual columns.
回答1:
If the list have equal size:
SELECT 1 AS id, 'first' AS name, '10;15;30;50' AS list1, '25;12;15;18' AS list2
INTO t
UNION ALL
SELECT 2, 'second', '50;30;15;10', '12;25;11;15';
-- a bit undeterministic, ROW_NUMBER ordered by placeholder 1/0
SELECT id, name, s1.value, s2.value
FROM t
CROSS APPLY (SELECT *, ROW_NUMBER() OVER(ORDER BY 1/0) AS r FROM STRING_SPLIT(list1, ';')) s1
CROSS APPLY (SELECT *, ROW_NUMBER() OVER(ORDER BY 1/0) AS r FROM STRING_SPLIT(list2, ';')) s2
WHERE s1.r = s2.r;
db<>fiddle demo
Related: STRING_SPLIT Add Option to Return Row Number
EDIT:
Using OPENJSON
to get deterministic position of element in array:
SELECT id, name, A.value, B.value
FROM t
CROSS APPLY (SELECT value, [key] AS rn
FROM OPENJSON(JSON_QUERY(CONCAT('[',REPLACE(t.list1,';',','),']')))) A
CROSS APPLY (SELECT value, [key] AS rn
FROM OPENJSON(JSON_QUERY(CONCAT('[',REPLACE(t.list2,';',','),']')))) B
WHERE A.rn = B.rn;
db<>fiddle demo 2
EDIT 2:
Handling list of different sizes:
WITH cte1 AS (
SELECT id, name, A.value, A.rn
FROM t
CROSS APPLY (SELECT value, [key] AS rn
FROM OPENJSON(JSON_QUERY(CONCAT('[',REPLACE(t.list1,';',','),']')))) A
),cte2 AS (
SELECT id, name, A.value, A.rn
FROM t
CROSS APPLY (SELECT value, [key] AS rn
FROM OPENJSON(JSON_QUERY(CONCAT('[',REPLACE(t.list2,';',','),']')))) A
)
SELECT id = COALESCE(cte1.id, cte2.id)
,name = COALESCE(cte1.name, cte2.name)
,cte1.value
,cte2.value
FROM cte1
FULL JOIN cte2
ON cte1.id = cte2.id
AND cte1.rn = cte2.rn
ORDER BY id;
db<>fiddle demo 3
回答2:
As long as the lists don't contain more than 4 members, you can use PARSENAME() as a shortcut and cross join to a number-table or cte with just the numbers 1-4.
Something like this psuedo-code, assuming you have a Table called tblNumbers
with a column called "num"
SELECT id, name,
PARSENAME(REPLACE(list1, ';', '.'), tblNumbers.num) AS L1,
PARSENAME(REPLACE(list2, ';', '.'), tblNumbers.num) AS L2,
FROM YourDataTable
CROSS JOIN tblNumbers
ORDER BY id ASC, tblNumbers.num DESC
If the List
columns ever do contain more than 4 elements each, you won't be able to use PARSENAME(), but you can still use the CROSS JOIN to a Numbers table approach, with a UDF you would have to write that would get the Nth element of a list. Pass the List column and the Num
value to that function just like they're being passed to PARSENAME in my pseudo code above.
来源:https://stackoverflow.com/questions/56742851/join-together-multiple-columns-split-by-a-character-in-sql