отображение четных и нечетных строк в 2 отдельных столбца

#sql #sql-server #tsql #split #case

Вопрос:

Но учитывая требования:

a~b~c~d~e~f

и O / p, как:

  a b
 c d 
 e f
 

Здесь a, e — нечетное значение, а c — четное значение. Поэтому я попытался использовать этот запрос, используя string_split() для разделения этого символа ‘~’. Кто-нибудь может помочь?

В этом запросе я не получаю нечетный четный вывод отдельного столбца:

 select 
   C.CID, c.nunique, 
   Row Number() OVER (ORDER BY C.CID) as RowNum,
   CASE 
      WHEN (ROW Number () OVER(ORDER BY c.CID)) % 2 = 0 THEN s.value 
      WHEN (Row Number() OVER (ORDER BY c.CID)) % 2 = 1 THEN s.value
      ELSE " invalid"
   END
from Candidate c
cross apply STRING SPLIT(applist,~) s
 

Комментарии:

1. Я не могу понять, чего вы хотите. Не могли бы вы вставить пример с входными данными и результатом, пожалуйста?

2. К сожалению STRING_SPLIT , может возвращать вложенные строки в любом порядке. Лучший выбор — рекурсивный cte.

Ответ №1:

Ответ:

Вам нужно разделить сохраненную последовательность, используя разделитель, чтобы получить подстроки и порядок подстрок. Здесь может помочь подход, основанный на JSON — просто преобразуйте текст в допустимый массив JSON ( a~b~c~d~e~f into ["a","b","c","d","e","f"]' ) и проанализируйте массив с OPENJSON() помощью . Результатом OPENJSON() вызова является таблица со столбцами key , value type а в случае массива JSON key столбец содержит индекс каждого элемента в массиве на основе 0:

 SELECT c.cid, j.odd, j.even
FROM (VALUES (1, 'a~b~c~d~e~f')) c (cid, applist)
CROSS APPLY (
   SELECT 
      MIN(CASE WHEN CONVERT(int, [key]) % 2 = 0 THEN [value] END) AS [odd],
      MIN(CASE WHEN CONVERT(int, [key]) % 2 = 1 THEN [value] END) AS [even]
   FROM OPENJSON(CONCAT('["', REPLACE(c.applist, '~', '","'), '"]'))
   GROUP BY CONVERT(int, [key]) / 2
) j
 

Результат:

 cid odd even
1   a   b
1   c   d
1   e   f
 

Обновить:

Если подход, основанный на JSON, не подходит, может помочь рекурсивный CTE:

 CREATE TABLE Candidate (cid int, applist varchar(max))
INSERT INTO Candidate (cid, applist) 
VALUES (1, 'a~b~c~d~e~f'), (2, 'g~h~i~i')

;WITH rCTE AS (
   SELECT 
      c.cid,
      LEFT(CONCAT(c.applist, '~'), CHARINDEX('~', CONCAT(c.applist, '~')) - 1) AS substring,
      STUFF(CONCAT(c.applist, '~'), 1, CHARINDEX('~', CONCAT(c.applist, '~')), '') AS remaining,
      0 AS position
   FROM Candidate c 
   UNION ALL
   SELECT 
      r.cid,
      LEFT(r.remaining, CHARINDEX('~', r.remaining) - 1),
      STUFF(r.remaining, 1, CHARINDEX('~', r.remaining), ''),
      r.position   1
   FROM rCTE r
   WHERE LEN(r.remaining) > 0
)
SELECT
   cid, 
   MIN(CASE WHEN position % 2 = 0 THEN [substring] END) AS [odd],
   MIN(CASE WHEN position % 2 = 1 THEN [substring] END) AS [even]
FROM rCTE
GROUP BY cid, position / 2
OPTION (MAXRECURSION 0);
 

Использование STRING_SPLIT() :

Обратите внимание, что, хотя STRING_SPLIT() это и опция, используйте эту функцию осторожно, потому что, как указано в документации, выходные строки могут располагаться в любом порядке, и порядок не гарантируется, чтобы соответствовать порядку подстрок во входной строке.

 SELECT 
   c.cid, 
   MIN(CASE WHEN rn % 2 = 0 THEN [value] END) AS [odd],
   MIN(CASE WHEN rn % 2 = 1 THEN [value] END) AS [even]
FROM (VALUES 
   (1, 'a~b~c~d~e~f'),
   (2, 'g~h~i~i')
) c (cid, applist)
CROSS APPLY (
   SELECT [value], ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 rn
   FROM STRING_SPLIT(c.applist, '~')
) s
GROUP BY c.cid, s.rn / 2
 

Комментарии:

1. Спасибо за ответ! это действительно полезно, и результат, который вы показали, идеален.. но мое требование — решить без использования OpenJson ()

2. Большое спасибо за объяснение этих 3 разных методов решения моего вопроса… Метод CTE помог мне набрать больше баллов.