将多列按SQL字符分开的

时间:2019-06-24 19:32:38

标签: sql sql-server tsql

使用mssql,如果我有以下数据:

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尝试创建将每个列表列连接在一起的结果行,例如

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

基本上,每个列表的每个数字都映射到该索引处的相同数字(由';'分隔)。 我可以使用cross apply + string_split,但对于每种可能的组合都会产生一行(id * description * list1_size * list2_size) 在SQL中甚至有可能吗?

我还尝试使用substring + charindex手动在列表中移动,但这会导致大量的手动列。

2 个答案:

答案 0 :(得分:3)

如果列表大小相等:

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

相关:STRING_SPLIT Add Option to Return Row Number


编辑:

使用OPENJSON获取数组中元素的确定位置:

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


编辑2:

处理不同大小的列表:

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

答案 1 :(得分:2)

只要列表中的成员不超过4个,您就可以使用PARSENAME()作为快捷方式并交叉连接到仅包含数字1-4的数字表或cte。

假设您有一个名为tblNumbers且表名为“ 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

如果List列的确包含四个以上的元素,则将无法使用PARSENAME(),但仍可以使用CROSS JOIN到Numbers表方法,并且使用UDF将不得不写那将获得列表的第N个元素。将List列和Num值传递给该函数,就像在上面我的伪代码中将它们传递给PARSENAME一样。