Dear All,
I have a table with one field name as City.
Below are the data.
City
--------
A
B
C
D
E
F
G
H
Write a query or stored procedure or function?
If a pass 2 , the query result will be below
A B
C D
E F
G H
If a pass 3, the query result will be below
A B C
D E F
G H
If a pass 4, the query result will be below
A B C D
E F G H
If a pass 5, the query result will be and so on as above result.
Thanks in advance
October 10, 2019 at 6:21 am
/*
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 1
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 2
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 3
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 4
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 5
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 6
*/
CREATE PROCEDURE #usp_Get_City
(
@No_Of_Rows_To_Group TINYINT
)
AS
BEGIN
SET NOCOUNT ON
DECLARE @Partition TINYINT
DECLARE @Table TABLE
(
City VARCHAR(50)
)
INSERT INTO @Table (City) VALUES ('A'), ('B'), ('C'), ('D'), ('E'), ('F'), ('G'), ('H')
IF OBJECT_ID('tempdb..#city_stag') IS NOT NULL DROP TABLE #city_stag
SELECT ROW_NUMBER() OVER(ORDER BY City ASC) AS RowID
,
CASE (ROW_NUMBER() OVER(ORDER BY City ASC) % NULLIF(@No_Of_Rows_To_Group, 0))
WHEN 0 THEN @No_Of_Rows_To_Group
ELSE (ROW_NUMBER() OVER(ORDER BY City ASC) % NULLIF(@No_Of_Rows_To_Group, 0))
END AS GroupID
, City
INTO #city_stag
FROM @Table
SELECT @Partition = COUNT(1)
FROM #city_stag
WHERE GroupID = 1
; WITH cte_groups_stag
AS
(
SELECT RowID
, NTILE(ISNULL(@Partition, 1)) OVER(PARTITION BY GroupID ORDER BY RowID ASC) AS GroupID
, City
FROM #city_stag
)
, cte_unique_groups_stag
AS
(
SELECT DISTINCT GroupID
FROM cte_groups_stag
)
SELECT GroupID
, City = STUFF(
(
SELECT ',' + City
FROM cte_groups_stag STG1
WHERE STG1.GroupID = STG2.GroupID
FOR XML PATH('')
), 1, 1, '')
FROM cte_unique_groups_stag STG2
END
If you want city in the different columns instead of comma separated values then you can use the dynamic sql.
October 10, 2019 at 9:44 am
Hi,
Thank you so much
Output will be as Column as well
Write a query or stored procedure or function?
If a pass 2 , the query result will be below
Col1 Col2
A B
C D
E F
G H
If a pass 3, the query result will be below
Col1 Col2 Col3
A B C
D E F
G H
If a pass 4, the query result will be below
Col1 Col2 Col3 Clo4
A B C D
E F G H
October 10, 2019 at 10:37 am
Here you go. Mark it as answer if it serves your purpose.
First of all create the below user defined scalar function
CREATE FUNCTION [ufn_SPLIT_STRING_BY_DELIMITER](@String nvarchar(4000), @Delimiter char(1))
RETURNS @Results TABLE (Items nvarchar(4000),ID int)
WITH SCHEMABINDING
AS
BEGIN
DECLARE @INDEX INT
DECLARE @SLICE nvarchar(4000)
-- HAVE TO SET TO 1 SO IT DOESNT EQUAL Z
-- ERO FIRST TIME IN LOOP
DECLARE @ID INT
SET @ID=1
SELECT @INDEX = 1
WHILE @INDEX !=0
BEGIN
-- GET THE INDEX OF THE FIRST OCCURENCE OF THE SPLIT CHARACTER
SELECT @INDEX = CHARINDEX(@Delimiter,@STRING)
-- NOW PUSH EVERYTHING TO THE LEFT OF IT INTO THE SLICE VARIABLE
IF @INDEX !=0
SELECT @SLICE = LEFT(@STRING,@INDEX - 1)
ELSE
SELECT @SLICE = @STRING
-- PUT THE ITEM INTO THE RESULTS SET
INSERT INTO @Results(Items,ID) VALUES(@SLICE,@ID)
-- CHOP THE ITEM REMOVED OFF THE MAIN STRING
SELECT @STRING = RIGHT(@STRING,LEN(@STRING) - @INDEX)
-- BREAK OUT IF WE ARE DONE
set @ID=@ID+1
IF LEN(@STRING) = 0 BREAK
END
RETURN
END
Now try the below procedure
/*
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 1
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 2
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 3
EXEC #usp_Get_City
@No_Of_Rows_To_Group = 4
*/
ALTER PROCEDURE #usp_Get_City
(
@No_Of_Rows_To_Group TINYINT
)
AS
BEGIN
SET NOCOUNT ON
IF @No_Of_Rows_To_Group > 4
BEGIN
RAISERROR ('@No_Of_Rows_To_Group cannot be greater than 4. If you wish to have it then amend the code accordingly', 16, 1);
RETURN;
END
DECLARE @Partition TINYINT
DECLARE @Table TABLE
(
City VARCHAR(50)
)
INSERT INTO @Table (City) VALUES ('A'), ('B'), ('C'), ('D'), ('E'), ('F'), ('G'), ('H')
IF OBJECT_ID('tempdb..#Output') IS NOT NULL DROP TABLE #Output
CREATE TABLE #Output
(
GroupID INT
, City VARCHAR(MAX)
, Col1 VARCHAR(50)
, Col2 VARCHAR(50)
, Col3 VARCHAR(50)
, Col4 VARCHAR(50)
)
IF OBJECT_ID('tempdb..#city_stag') IS NOT NULL DROP TABLE #city_stag
SELECT ROW_NUMBER() OVER(ORDER BY City ASC) AS RowID
,
CASE (ROW_NUMBER() OVER(ORDER BY City ASC) % NULLIF(@No_Of_Rows_To_Group, 0))
WHEN 0 THEN @No_Of_Rows_To_Group
ELSE (ROW_NUMBER() OVER(ORDER BY City ASC) % NULLIF(@No_Of_Rows_To_Group, 0))
END AS GroupID
, City
INTO #city_stag
FROM @Table
SELECT @Partition = COUNT(1)
FROM #city_stag
WHERE GroupID = 1
; WITH cte_groups_stag
AS
(
SELECT RowID
, NTILE(ISNULL(@Partition, 1)) OVER(PARTITION BY GroupID ORDER BY RowID ASC) AS GroupID
, City
FROM #city_stag
)
, cte_unique_groups_stag
AS
(
SELECT DISTINCT GroupID
FROM cte_groups_stag
)
INSERT INTO #Output (GroupID, City)
SELECT GroupID
, City = STUFF(
(
SELECT ',' + City
FROM cte_groups_stag STG1
WHERE STG1.GroupID = STG2.GroupID
FOR XML PATH('')
), 1, 1, '')
FROM cte_unique_groups_stag STG2
UPDATE OUTP
SET OUTP.Col1 = STRNG.items
FROM #Output OUTP
CROSS APPLY ufn_SPLIT_STRING_BY_DELIMITER (OUTP.City, ',') STRNG
WHERE STRNG.ID = 1
UPDATE OUTP
SET OUTP.Col2 = STRNG.items
FROM #Output OUTP
CROSS APPLY ufn_SPLIT_STRING_BY_DELIMITER (OUTP.City, ',') STRNG
WHERE STRNG.ID = 2
UPDATE OUTP
SET OUTP.Col3 = STRNG.items
FROM #Output OUTP
CROSS APPLY ufn_SPLIT_STRING_BY_DELIMITER (OUTP.City, ',') STRNG
WHERE STRNG.ID = 3
UPDATE OUTP
SET OUTP.Col4 = STRNG.items
FROM #Output OUTP
CROSS APPLY ufn_SPLIT_STRING_BY_DELIMITER (OUTP.City, ',') STRNG
WHERE STRNG.ID = 4
DECLARE @Col_Number TINYINT
, @Col_Name NVARCHAR(MAX)
SET @Col_Number = 1
WHILE @Col_Number <= @No_Of_Rows_To_Group
BEGIN
IF @Col_Number = 1 SET @Col_Name = '[Col' + CAST(@Col_Number AS VARCHAR) + ']'
ELSE SET @Col_Name = @Col_Name + ', ' + '[Col' + CAST(@Col_Number AS VARCHAR) + ']'
SET @Col_Number = @Col_Number + 1;
END
EXECUTE ('SELECT ' + @Col_Name + ' FROM #Output')
IF OBJECT_ID('tempdb..#Output') IS NOT NULL DROP TABLE #Output
END
So much code for a simple pivot.
--Create sample data
CREATE TABLE #Table
(
City VARCHAR(50)
)
INSERT INTO #Table (City)
VALUES ('A'), ('B'), ('C'), ('D'), ('E'), ('F'), ('G'), ('H');
--Solution starts here. The first variable can be converted into a parameter when converting the query into a stored procedure.
DECLARE @Columns int = 5;
DECLARE @SQL NVARCHAR(MAX),
@Cols NVARCHAR(MAX);
-- Create code for as many columns as necessary
WITH
E(n) AS(
SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))E(n)
),
cteTally(n) AS(
SELECT TOP( @Columns)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) n
FROM E a, E b
)
SELECT @Cols = STUFF(( SELECT NCHAR(9) + REPLACE( ',MAX( CASE WHEN ColumnOrder = <<N>> THEN City END) AS Col<<N>>', N'<<N>>', N) + NCHAR(10)
FROM cteTally
FOR XML PATH(''), TYPE).value('./text()[1]', 'nvarchar(max)'), 2, 1, N' ');
--Add the columns code to the whole query
--ColumnOrder will assign the cities to each column
--ColumnGroup will assign the cities to independent rows
SET @SQL = N'
WITH CTE AS(
SELECT *,
(ROW_NUMBER() OVER( ORDER BY City) - 1) % @Columns + 1 ColumnOrder,
(ROW_NUMBER() OVER( ORDER BY City) - 1) / @Columns + 1 ColumnGroup
FROM #Table
)
SELECT
' + @Cols
+ N'FROM CTE
GROUP BY ColumnGroup;';
--This is for debugging purposes
--PRINT @SQL;
EXECUTE sp_executesql @SQL, N'@Columns int', @Columns;
GO
--Clean my environment
DROP TABLE #Table;
October 10, 2019 at 5:15 pm
That's indeed a different perspective to solve the same problem. Much appreciated !
October 11, 2019 at 6:07 am
Thank you Brahmanand Shukla!
October 11, 2019 at 6:09 am
Thank you Luis Cazares!
Viewing 8 posts - 1 through 7 (of 7 total)
You must be logged in to reply to this topic. Login to reply