Разделить возвращенные строки на n групп и вернуть их рядом как новые столбцы - динамическое количество столбцов - PullRequest
0 голосов
/ 29 августа 2018

У меня есть запрос, который возвращает 2 столбца:

Product   | Price
------------------
Product01 | 10.00
Product02 | 10.00
Product03 | 10.00
Product04 | 10.00
Product05 | 10.00
Product06 | 10.00
Product07 | 10.00
Product08 | 10.00
Product09 | 10.00
Product10 | 10.00

Я пытаюсь вернуть те же данные, но в таком виде:

Product1   | Price1 | Product2   | Price2 | Product3   | Price3
---------------------------------------------------------------
Product01  | 10.00  | Product05  | 10.00  | Product08  | 10.00
Product02  | 10.00  | Product06  | 10.00  | Product09  | 10.00
Product03  | 10.00  | Product07  | 10.00  | Product10  | 10.00
Product04  | 10.00  | NULL       | NULL   | NULL       | NULL

Это мой код:

DECLARE @COLUMNS INT
SET @COLUMNS = 3

;
WITH CTE AS (
SELECT
 ROW_NUMBER() OVER ( PARTITION BY [COLUMN] ORDER BY ROW ASC ) AS ID_IN_GROUP
,*
FROM (
    SELECT
        ROW_NUMBER() OVER ( ORDER BY Name ASC ) AS ROW
       ,NTILE(@COLUMNS) OVER ( ORDER BY Name ASC ) AS [COLUMN]
       ,*
    FROM
      Products
) X
)
SELECT A.Name as Product1, A.Price as Price1, B.Name as Product2, B.Price as Price2, C.Name as Product3, C.Price as Price3 FROM
(SELECT Name,Price,ID_IN_GROUP FROM CTE WHERE [COLUMN]=1) A
CROSS APPLY
(SELECT Name,Price, ID_IN_GROUP FROM CTE WHERE [COLUMN]=2) B
CROSS APPLY
(SELECT Name,Price, ID_IN_GROUP FROM CTE WHERE [COLUMN]=3) C
WHERE 
B.ID_IN_GROUP=A.ID_IN_GROUP
AND C.ID_IN_GROUP=A.ID_IN_GROUP

http://sqlfiddle.com/#!18/bf3b4/30

Возвращает почти желаемый результат:

Product1   | Price1 | Product2   | Price2 | Product3   | Price3
---------------------------------------------------------------
Product01  | 10.00  | Product05  | 10.00  | Product08  | 10.00
Product02  | 10.00  | Product06  | 10.00  | Product09  | 10.00
Product03  | 10.00  | Product07  | 10.00  | Product10  | 10.00

но последняя строка отсутствует - Product04. Как это можно исправить?

РЕДАКТИРОВАТЬ: Я исправил это, изменив CROSS APPLY на LEFT JOIN, как это:

DECLARE @COLUMNS INT 
SET 
  @COLUMNS = 3;
WITH CTE AS (
  SELECT 
    ROW_NUMBER() OVER (
      PARTITION BY [COLUMN] 
      ORDER BY 
        ROW ASC
    ) AS ID_IN_GROUP, 
    * 
  FROM 
    (
      SELECT 
        ROW_NUMBER() OVER (
          ORDER BY 
            Name ASC
        ) AS ROW, 
        NTILE(@COLUMNS) OVER (
          ORDER BY 
            Name ASC
        ) AS [COLUMN], 
        * 
      FROM 
        Products
    ) X
) 
SELECT 
  A.Name as Product1, 
  A.Price as Price1, 
  B.Name as Product2, 
  B.Price as Price2, 
  C.Name as Product3, 
  C.Price as Price3 
FROM 
  (
    SELECT 
      Name, 
      Price, 
      ID_IN_GROUP 
    FROM 
      CTE 
    WHERE 
      [COLUMN] = 1
  ) A 
  LEFT JOIN (
    SELECT 
      Name, 
      Price, 
      ID_IN_GROUP 
    FROM 
      CTE 
    WHERE 
      [COLUMN] = 2
  ) B ON B.ID_IN_GROUP = A.ID_IN_GROUP 
  LEFT JOIN (
    SELECT 
      Name, 
      Price, 
      ID_IN_GROUP 
    FROM 
      CTE 
    WHERE 
      [COLUMN] = 3
  ) C ON C.ID_IN_GROUP = A.ID_IN_GROUP

http://sqlfiddle.com/#!18/bf3b4/61

Возможно ли сделать это более динамичным? Поэтому, когда я изменю @COLUMNS на 4, он вернет 4 группы?

1 Ответ

0 голосов
/ 29 августа 2018

То, что вы просите, гораздо сложнее, чем я думал. Я не совсем понял логику, но это довольно близко:

SELECT MAX(CASE WHEN floor((seqnum - 1) / ceiling(cnt / 3.0)) = 0 THEN Name END) as Product1,
       MAX(CASE WHEN floor((seqnum - 1) / ceiling(cnt / 3.0)) = 0 THEN Price END) as Price1,
       MAX(CASE WHEN floor((seqnum - 1) / ceiling(cnt / 3.0)) = 1 THEN Name END) as Product2,
       MAX(CASE WHEN floor((seqnum - 1) / ceiling(cnt / 3.0)) = 1 THEN Price END) as Price2,
       MAX(CASE WHEN floor((seqnum - 1) / ceiling(cnt / 3.0)) = 2 THEN Name END) as Product3,
       MAX(CASE WHEN floor((seqnum - 1) / ceiling(cnt / 3.0)) = 2 THEN Price END) as Price3
FROM (SELECT p.*,
             ROW_NUMBER() OVER (ORDER BY Name) as seqnum,
             COUNT(*) OVER () as cnt
      FROM Products p
     ) p
GROUP BY (seqnum - 1) % ceiling(cnt / 3.0);

Разница в том, что это заполняет столбцы "жадно", поэтому столбцы 4-4-2, а не 4-3-3. Одним из преимуществ этого подхода является то, что при добавлении 11-й строки столбцы остаются прежними.

Скрипка SQL.

...