I have a table where I have two columns- col1 & col2. The table need to be rolled up such that if a value is in col1 then the adjacent value to col2 gets inserted next to original col1 value. To illustrate, if I have a table like this:
col1 col2
24 670
25 980
26 24
28 1570
28 26
27 5745
27 4654
Then the output should look like this (in no particular order placement):
col1 col_1 col_2 col_3 col_4
24 670 26 28 1570
25 980 NULL NULL NULL
27 4654 5745 NULL NULL
Here 24 from col2 exists in col1 already, so 26 (and other associations 28 and 1570) gets inserted next to 24. Unfortunately, with my current knowledge in sql I was able to get this:
--refrences
-- https://stackoverflow.com/questions/38233002/how-to-create-add-columns-using-a-variable-in-a-loop
-- https://stackoverflow.com/questions/10404348/sql-server-dynamic-pivot-query
declare @max_columns int
declare @counter int
declare @col_header nvarchar(max)= ''
declare @query nvarchar(max)= ''
drop table if exists #tmp_ini
drop table if exists #tmp
drop table if exists #tmp_allcols
create table #tmp_ini (col1 int, col2 int )
create table #tmp_allcols (col1 int, col nvarchar(max), [val] int, [row_id] int)
insert into #tmp_ini values (24, 670),
(25, 980),
(26, 24),
(28, 1570),
(28, 26),
(27, 5745),
(27, 4654)
select * into #tmp from #tmp_ini
insert into #tmp_allcols
select col1,
'col_' cast(t.row_id as nvarchar(max)) as col ,
col2 as val,
t.row_id
from (SELECT * , ROW_NUMBER() OVER ( PARTITION by col1 Order by col2 ) AS row_id FROM #tmp ) t
select @max_columns= max(m.tot) from(
select COUNT(*) as tot from #tmp_allcols group by col1
) m
set @counter=1
while @counter <= @max_columns
begin
set @col_header = 'col_' cast(@counter as nvarchar(50)) ', '
set @counter = @counter 1
end
set @col_header = SUBSTRING(@col_header,1,LEN(@col_header)-1)
set @query = ' select * from ('
set @query = ' select col1, col, val from #tmp_allcols '
set @query = ' ) tmp'
set @query = ' PIVOT ( max(val) for Col in ('
set @query = @col_header
set @query = ' )) AS pvt'
print @query
exec sp_executesql @query
Current output:
col1 col_1 col_2
24 670 NULL
25 980 NULL
26 24 NULL
27 4654 5745
28 26 1570
Any tips or help is appreciated. I have looked at these posts, but couldn't get far SQL Pivot IF EXISTS ; `PIVOT` with `EXISTS` ; `PIVOT` with `EXISTS`
CodePudding user response:
I don't think there is an easy way to do what you need, that's why I skipped the easy way, and did some crazy SQL logic:
--#####################
--#### SCHEMA #########
--#####################
CREATE TABLE Entities(
entity1 int NULL,
entity2 int NULL
)
INSERT INTO Entities VALUES
(24, 670),
(25, 980),
(26, 24),
(28, 1570),
(28, 26),
(27, 5745),
(27, 4654)
--###############################
--########### QUERY #############
--###############################
IF OBJECT_ID('tempdb..#TempRelations') IS NOT NULL
DROP TABLE #TempRelations
IF OBJECT_ID('tempdb..#TempEntities') IS NOT NULL
DROP TABLE #TempEntities
IF OBJECT_ID('tempdb..#FinalRelations') IS NOT NULL
DROP TABLE #FinalRelations
IF OBJECT_ID('tempdb..#FinalTable') IS NOT NULL
DROP TABLE #FinalTable
CREATE TABLE #TempRelations (entity INT NULL)
CREATE TABLE #FinalRelations (id INT, entity INT NULL)
SELECT ROW_NUMBER() OVER (ORDER BY entity1) Id, *
INTO #TempEntities
FROM Entities
DECLARE @entity1 INT, @entity2 INT
DECLARE @count INT = (SELECT COUNT(*) FROM #TempEntities)
DECLARE @relationsCount INT = 1;
DECLARE @i INT = 1
--WHILE THERE ARE STILL ENTITIES
WHILE EXISTS(SELECT 1
FROM #TempEntities
WHERE entity1 IS NOT NULL
OR entity2 IS NOT NULL)
BEGIN
SELECT @entity1 = entity1, @entity2 = entity2
FROM #TempEntities
WHERE Id = @i
DELETE #TempRelations
IF @entity1 IS NOT NULL
BEGIN
INSERT INTO #TempRelations VALUES (@entity1)
INSERT INTO #TempRelations VALUES (@entity2)
END
UPDATE #TempEntities
SET entity1 = NULL,
entity2 = NULL
WHERE Id = @i
--WHILE THERE ARE STILL RELATIONS TO BE TAKEN FROM ENTITIES
WHILE 1=1
BEGIN
SET @i = 1
WHILE @i <= @count
BEGIN
SELECT @entity1 = entity1, @entity2 = entity2
FROM #TempEntities
WHERE Id = @i
--IF entity1 OR entity2 HAS RELATION TO THE CURRENT TEMP RELATIONS, INSERT THE RELATION THEN REMOVE THE ENTITIES
IF @entity1 IS NOT NULL AND ( EXISTS (SELECT 1
FROM #TempRelations
WHERE entity = @entity1
OR entity = @entity2) OR (SELECT COUNT(*) FROM #TempRelations) = 0 )
BEGIN
INSERT INTO #TempRelations VALUES (@entity1)
INSERT INTO #TempRelations VALUES (@entity2)
UPDATE #TempEntities
SET entity1 = NULL,
entity2 = NULL
WHERE Id = @i
END
SET @i = @i 1
END
IF NOT EXISTS (SELECT 1
FROM #TempEntities
WHERE EXISTS (SELECT 1
FROM #TempRelations
WHERE entity = entity1
OR entity = entity2))
BREAK
END
INSERT INTO #FinalRelations
SELECT DISTINCT @relationsCount,
entity
FROM #TempRelations
SET @relationsCount = @relationsCount 1
DELETE #TempRelations
END
SELECT 'col' (CAST(ROW_NUMBER() OVER (PARTITION BY id ORDER BY id) AS VARCHAR)) Col,
id,
entity
INTO #FinalTable
FROM #FinalRelations
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT DISTINCT ',' col
FROM #FinalTable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = N'SELECT ' @cols N' FROM
(
SELECT Col, id, entity
FROM #FinalTable
) x
PIVOT
(
MAX(entity)
FOR Col IN (' @cols N')
) p '
PRINT (@query)
exec sp_executesql @query;