Объединить несколько столбцов, разделенных символом в SQL - PullRequest
1 голос
/ 24 июня 2019

используя 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'

я пытаюсь создать строки результатов, которые объединяют каждый из этих столбцов списка, например

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 ]

3 голосов
/ 24 июня 2019

Если список имеет одинаковый размер:

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 Добавить параметрвернуть номер строки


РЕДАКТИРОВАТЬ:

Использование 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

2 голосов
/ 24 июня 2019

Поскольку списки не содержат более 4 членов, вы можете использовать PARSENAME () в качестве ярлыка и перекрестного соединения с таблицей чисел или cte только с номерами 1-4.

Что-то вроде этого псевдо-кода, при условии, что у вас есть таблица с именем 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 содержат более 4-х элементов каждый, вы не сможете использовать PARSENAME (), но вы все равно можете использовать CROSS JOIN для подхода таблицы чисел, с UDF, который у вас будет написать, что бы получить N-й элемент списка. Передайте столбец List и значение Num этой функции точно так же, как они передаются PARSENAME в моем псевдокоде выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...