Вам необходимо сначала выделить столбец для каждой записи, что можно сделать с помощью NTILE()
:
WITH Table1 AS
(
SELECT TOP 26 Col1 = CHAR(64 + ROW_NUMBER() OVER(ORDER BY object_id))
FROM sys.all_objects
)
SELECT [Col1],
5 - NTILE(5) OVER(ORDER BY Col1 DESC) AS Col
FROM Table1 ;
Предоставление:
Col1 Col
---------
Z 4
Y 4
X 4
W 4
V 4
U 4
T 3
....
Y 4
Z 4
Обратите внимание: чтобы начать заполнение с последнего столбца, вам нужно разместить столбцы в обратном порядке (ORDER BY Col1 DESC
), а затем вычесть это из общего числа столбцов.
Затем вы можете обработать строку, упорядочив ее по столбцам:
WITH Table1 AS
(
SELECT TOP 26 Col1 = CHAR(64 + ROW_NUMBER() OVER(ORDER BY object_id))
FROM sys.all_objects
)
SELECT Col1, Col, ROW_NUMBER() OVER(PARTITION BY Col ORDER BY Col1) AS Row
FROM ( SELECT [Col1],
5 - NTILE(5) OVER(ORDER BY Col1 DESC) AS Col
FROM Table1
) c;
Предоставление:
Col1 Col Row
--------------------
A 0 1
B 0 2
C 0 3
D 0 4
E 0 5
F 1 1
G 1 2
Тогда вы можете применить пивот:
WITH Table1 AS
(
SELECT TOP 26 Col1 = CHAR(64 + ROW_NUMBER() OVER(ORDER BY object_id))
FROM sys.all_objects
), CTE AS
(
SELECT Col1, COL, ROW_NUMBER() OVER(PARTITION BY Col ORDER BY Col1) AS Row
FROM ( SELECT [Col1],
5 - NTILE(5) OVER(ORDER BY Col1 DESC) AS Col
FROM Table1
) c
)
SELECT [0], [1], [2], [3], [4]
FROM CTE
PIVOT (MAX([COL1]) FOR Col IN ([0], [1], [2], [3], [4])) AS Pvt
ORDER BY Row;
Предоставление:
0 1 2 3 4
-------------------------------------
A F K P U
B G L Q V
C H M R W
D I N S X
E J O T Y
NULL NULL NULL NULL Z
ДОПОЛНЕНИЕ
Я настроил образец таблицы и использовал процедуру с динамическим SQL для простоты повторного использования, чтобы продемонстрировать это решение, похоже, оно работает для меня как ожидалось.
SET UP
-- SET UP TABLE AND INSERT RANDOM VALUES
IF OBJECT_ID(N'tempdb..#Table1', 'U') IS NOT NULL
DROP TABLE #Table1;
CREATE TABLE #Table1 (Col1 CHAR(2));
INSERT #Table1 (Col1)
SELECT CONCAT(Letter, Number)
FROM (SELECT TOP 26 Letter = CHAR(64 + ROW_NUMBER() OVER(ORDER BY object_id))
FROM sys.all_objects) l
CROSS JOIN (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9)) n (Number);
GO
IF OBJECT_ID(N'tempdb..#GenerateMatrix', 'P') IS NOT NULL
DROP PROCEDURE #GenerateMatrix;
GO
CREATE PROCEDURE #GenerateMatrix @Records INT, @Columns INT
AS
BEGIN
-- GENERATE COLUMNS FOR PIVOT AND SELECT
DECLARE @ColSQL NVARCHAR(MAX) =
STUFF((SELECT TOP (@Columns)
CONCAT(',', QUOTENAME(ROW_NUMBER() OVER(ORDER BY Col1) - 1))
FROM #Table1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
-- FOR @Cols = 5 Generates "[0],[1],[2],[3],[4]"
DECLARE @SQL NVARCHAR(MAX) =
CONCAT('SELECT ', @ColSQL, '
FROM (SELECT Col1, Col, Row = ROW_NUMBER() OVER(PARTITION BY Col ORDER BY Col1)
FROM ( SELECT [Col1], Col = @Columns - NTILE(@Columns) OVER(ORDER BY Col1 DESC)
FROM (SELECT TOP (@Records) Col1 FROM #Table1 Col1) t
) c) c
PIVOT (MAX([COL1]) FOR Col IN (', @ColSQL, ')) AS Pvt
ORDER BY Row;');
EXECUTE sp_executesql @SQL, N'@Columns INT, @Records INT', @Columns = @Columns, @Records = @Records;
END
GO
ТЕСТ 1
EXECUTE #GenerateMatrix @Records = 26, @Columns = 5;
0 1 2 3 4
----------------------------------
A1 A6 B2 B7 C3
A2 A7 B3 B8 C4
A3 A8 B4 B9 C5
A4 A9 B5 C1 C6
A5 B1 B6 C2 C7
NULL NULL NULL NULL C8
ТЕСТ 2
EXECUTE #GenerateMatrix @Records = 8, @Columns = 4;
0 1 2 3
----------------------------
A1 A3 A5 A7
A2 A4 A6 A8
ТЕСТ 3
EXECUTE #GenerateMatrix @Records = 40, @Columns = 8;
0 1 2 3 4 5 6 7
-----------------------------------------------------------
A1 A6 B2 B7 C3 C8 D4 D9
A2 A7 B3 B8 C4 C9 D5 E1
A3 A8 B4 B9 C5 D1 D6 E2
A4 A9 B5 C1 C6 D2 D7 E3
A5 B1 B6 C2 C7 D3 D8 E4
ТЕСТ 4
EXECUTE #GenerateMatrix @Records = 50, @Columns = 6;
0 1 2 3 4 5
---------------------------------------
A1 A9 B8 C7 D6 E6
A2 B1 B9 C8 D7 E7
A3 B2 C1 C9 D8 E8
A4 B3 C2 D1 D9 E9
A5 B4 C3 D2 E1 F1
A6 B5 C4 D3 E2 F2
A7 B6 C5 D4 E3 F3
A8 B7 C6 D5 E4 F4
NULL NULL NULL NULL E5 F5
В любом из тестов отсутствуют пропущенные записи и нет строк с только нулевыми значениями.
Пример на DB Fiddle