Pivot Dynamically, Returned Results from Join of Two Tables

PIVOT dynamically, Returned results from JOIN of two tables

Maybe I am missing something but you should be able to PIVOT the data but you will need to implement row_number() to help generate the columns.

The key will be to use a query similar to:

SELECT  ONE.UserID,
ONE.Episode,
ONE.Value,
TWO.Details,
'Details'
+cast(row_number() over(partition by one.userid, one.episode
order by two.details) as varchar(10)) seq
FROM TABLE1 ONE
INNER JOIN TABLE2 Two
ON ONE.UserID = TWO.UserID
AND ONE.Episode = TWO.Episode

This will create a unique sequence for the new columns names, then you can apply the PIVOT:

select userid, episode,
value,
details1,
details2
from
(
SELECT ONE.UserID,
ONE.Episode,
ONE.Value,
TWO.Details,
'Details'
+cast(row_number() over(partition by one.userid, one.episode
order by two.details) as varchar(10)) seq
FROM TABLE1 ONE
INNER JOIN TABLE2 Two
ON ONE.UserID = TWO.UserID
AND ONE.Episode = TWO.Episode
) d
pivot
(
max(details)
for seq in (Details1, Details2)
) piv;

See SQL Fiddle with Demo. Then you can convert this to dynamic SQL:

DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME('Details'+cast(seq as varchar(10)))
from
(
select
row_number() over(partition by one.userid, one.episode
order by two.details) seq
FROM TABLE1 ONE
INNER JOIN TABLE2 Two
ON ONE.UserID = TWO.UserID
AND ONE.Episode = TWO.Episode
) d
group by seq
order by seq
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')

set @query = 'SELECT userid, episode, value, ' + @cols + '
from
(
SELECT ONE.UserID,
ONE.Episode,
ONE.Value,
TWO.Details,
''Details''
+cast(row_number() over(partition by one.userid, one.episode
order by two.details) as varchar(10)) seq
FROM TABLE1 ONE
INNER JOIN TABLE2 Two
ON ONE.UserID = TWO.UserID
AND ONE.Episode = TWO.Episode
) x
pivot
(
max(details)
for seq in (' + @cols + ')
) p '

execute sp_executesql @query;

See SQL Fiddle with Demo. Giving you the result:

| USERID | EPISODE |     VALUE |  DETAILS1 |  DETAILS2 |
|--------|---------|-----------|-----------|-----------|
| 1 | 1 | VALUE 1-1 | Details 1 | Details 2 |
| 1 | 2 | VALUE 1-2 | Details 1 | Details 2 |

Join Two tables using Dynamic Pivot Query

You have to use dynamic query as below and you can test is by adding more FieldNames

CREATE TABLE #table1(EmpID INT,  
Code VARCHAR(20),
Name VARCHAR(20),
Fld1 VARCHAR(20),
Fld2 VARCHAR(20),
Fld3 VARCHAR(20),
Fld4 VARCHAR(20))

INSERT INTO #table1 VALUES
(1, '1008M','ABC','temp1','temp2','temp3',NULL),
(2, '1039E','XYZ','temp1',NULL,NULL,null),
(3, '1040E','TYS',null,NULL,NULL,'temp6')

CREATE TABLE #table2(EmpID INT, FieldName VARCHAR(20), VALUE INT)
INSERT INTO #table2 VALUES
(1,'FH',1000),
(1,'FB',1220),
(2,'FHRA',3000),
(2,'FB',3000),
(3,'FB',3000)

DECLARE @col VARCHAR(MAX)
DECLARE @sql VARCHAR(MAX)

SELECT @col = COALESCE(@col + ', ','') + QUOTENAME(FieldName)
FROM #table2 GROUP BY FieldName

SELECT @col -- This gives: [FB], [FH], [FHRA]

-- Now setting this @col variable in the Dynamic SQL.
SET @sql = '
select EmpID, Code, Name,Fld1,Fld2,Fld3,Fld4, ' + @col + '
from (select a.EmpID, Code, Name,Fld1,Fld2,Fld3,Fld4, b.FieldName, b.value
from #table1 a
join #table2 b on a.empid=b.empid)p
PIVOT(MAX (VALUE) FOR FieldName IN ( ' + @col + ' )
) AS pvt
'

PRINT @sql
EXEC (@sql)

OUTPUT:

EmpID   Code    Name    Fld1    Fld2    Fld3    Fld4    FB      FH      FHRA
1 1008M ABC temp1 temp2 temp3 NULL 1220 1000 NULL
2 1039E XYZ temp1 NULL NULL NULL 3000 NULL 3000
3 1040E TYS NULL NULL NULL temp6 3000 NULL NULL

Dynamically looping multiple tables to inner join clause

Since you are adding your static beginning string to the variable each time, you are getting your current results. Add another variable to store the moving target, and move the static bit out of the WHILE loop.

    DECLARE @max int = 20
DECLARE @SQL VARCHAR(MAX)
DECLARE @TableName VARCHAR(50)
DECLARE @id int = 1
Declare @Param1 varchar(20) ='p1', @param2 varchar(20) ='p2'
DECLARE @qry NVARCHAR(MAX)
DECLARE @SQL2 VARCHAR(MAX) = ' '
Declare @strcolumns nvarchar(max) = 'select c1, c2, c3, ...c30 from primarytable '
---------------
select @max = MAX(Id) from @Table
WHILE (@id <= @max)
BEGIN
SELECT @TableName = TableName FROM @Table WHERE Id = @id
SET @SQL = 'INNER JOIN '+ @TableName + ' ON p.' + @Param1 + ' = ' + @TableName + '.'+ @param1 + ' AND p.' + @Param2 + ' = ' + @TableName + '.'+ @param2 + ' '
SET @id = @id +1
SET @SQL2 = @SQL2 + @SQL
END
SET @strcolumns = @strcolumns + @SQL2
PRINT (@strcolumns)

How to join two dynamic pivot(table data after pivot) in SQL Server

Honestly, this should be something for your presentation layer not the SQL layer; especially as you want merged cells (a concept that does not exist in SQL).

I would personally switch over to conditional aggregation, rather than the restrictive PIVOT operator, and then do something like this:

DECLARE @SQL nvarchar(MAX),
@CRLF nchar(2) = NCHAR(13) + NCHAR(10);

DECLARE @Delimiter nchar(3) = N',' + @CRLF;

WITH DateRanges AS(
SELECT DISTINCT
DATEADD(MONTH, DATEDIFF(MONTH,0,CreatedOn),0) AS StartDate,
DATEADD(MONTH, DATEDIFF(MONTH,0,CreatedOn)+1,0) AS EndDate
FROM dbo.TblDemoData)
SELECT @SQL = N'SELECT DD.AgentID,' + @CRLF +
STRING_AGG(N' COUNT(CASE WHEN DD.CreatedOn >= ' + QUOTENAME(CONVERT(varchar(8),DR.StartDate,112),'''') + N' AND DD.CreatedOn < ' + QUOTENAME(CONVERT(varchar(8),DR.EndDate,112),'''') + N' THEN 1 END) AS ' + QUOTENAME(CONCAT(YEAR(DR.StartDate),'-',DATENAME(MONTH,DR.StartDate),N'-Audits')) + N',' + @CRLF +
N' SUM(CASE WHEN DD.CreatedOn >= ' + QUOTENAME(CONVERT(varchar(8),DR.StartDate,112),'''') + N' AND DD.CreatedOn < ' + QUOTENAME(CONVERT(varchar(8),DR.EndDate,112),'''') + N' THEN AchievedScore* 1. ELSE 0 END) / ' + @CRLF +
N' SUM(CASE WHEN DD.CreatedOn >= ' + QUOTENAME(CONVERT(varchar(8),DR.StartDate,112),'''') + N' AND DD.CreatedOn < ' + QUOTENAME(CONVERT(varchar(8),DR.EndDate,112),'''') + N' THEN MaximumScore ELSE 0 END) AS ' + QUOTENAME(CONCAT(YEAR(DR.StartDate),'-',DATENAME(MONTH,DR.StartDate),N'-Score')),@Delimiter) WITHIN GROUP (ORDER BY DR.StartDate) + @CRLF +
N'FROM dbo.TblDemoData DD' + @CRLF +
N'GROUP BY DD.AgentId;'
FROM DateRanges DR;

--PRINT @SQL; Your debugging best friend

EXEC sys.sp_executesql @SQL;

Note this doesn't give the columns in the order you asked, however, the order of the columns is meaningless, and (again) should be controlled in your presentation layer.

How to perform a left join in my dynamic pivot query so it still returns rows

Maybe I'm misunderstanding your example, but I don't think the LEFT JOIN and PIVOT combo is your problem, I worked up a simple test case (forgive the shortened names) and it works fine

with cteCust as (
SELECT * FROM (VALUES ('Alice', 25), ('Bob', 22)
, ('Chris', 27), ('Doug', 21), ('Eve', 28)
) as Cust (Part, Age)
), cteTempCount as (
SELECT *
FROM (VALUES
('Alice', 'US', 5), ('Alice', 'EU', 2), ('Alice', 'GB', 4)
, ('Chris', 'GB', 3), ('Chris', 'JP', 6)
, ('Doug', 'JP', 7), ('Doug', 'EU', 4), ('Doug', 'EU', 9)
, ('Eve', 'US', 1), ('Eve', 'GB', 2), ('Eve', 'JP', 5)
) as TC(Part, Regn, RCount)
)
SELECT Part, Age, US, EU
FROM (SELECT u.Part, u.age, Regn, RCount FROM cteCust as U
LEFT OUTER JOIN cteTempCount as TC on U.Part = TC.Part) as X
PIVOT (SUM(RCount) FOR Regn in (US, EU)) as P;

gives results

Part    Age US  EU
Alice 25 5 2
Bob 22 NULL NULL
Chris 27 NULL NULL
Doug 21 NULL 13
Eve 28 1 NULL

which is giving a results row even for users with no matches in the desired regions (Chris) or no matches in TempCount whatsoever (Bob).

Is it possible some of the RegionNames aren't making good column names?

Or maybe whatever is consuming this output is having trouble with NULL values?

Maybe @cols is malformed?

Pivot while changing data types (dynamically)

If you must convert the datatype (could really be a presentation layer thing), then a dynamic conditional aggregation should do the trick.

Example

Declare @SQL nvarchar(max) ='
Select U.*' +
(
Select concat(',',quotename(fullname),'=max(case when attributeid=',id,' then try_convert(',type,',value) end)')
From customattributes
For XML Path ('')
)+'
From users U
Join customattributesvalues V on U.ID=V.userid
Group By U.ID
,U.FirstName
,U.LastName
,U.active
,U.datecreated
,U.username
,U.email
'
--print @SQL
Exec(@SQL)

Results

Sample Image

The Generated SQL Looks Like This

Select U.*
,[Hire Date]=max(case when attributeid=1 then try_convert(date,value) end)
,[Employee ID]=max(case when attributeid=2 then try_convert(int,value) end)
,[Supervisor]=max(case when attributeid=3 then try_convert(nvarchar(50),value) end)
,[Assigned Ship]=max(case when attributeid=4 then try_convert(nvarchar(50),value) end)
,[Job Title]=max(case when attributeid=5 then try_convert(nvarchar(50),value) end)
From #users U
Join #customattributesvalues V on U.ID=V.userid
Group By U.ID
,U.FirstName
,U.LastName
,U.active
,U.datecreated
,U.username
,U.email

Dynamic pivot table with multiple columns in sql server

Please use this (If you are getting Collation issue, please change all the 3 INT datatypes):

STATIC code:

SELECT HEADER, [A_1],[B_2],[C_3],[D_4],[E_5],[F_6]        
FROM
(SELECT DECK,HEADER, VALUE FROM REPORT
UNPIVOT
(
VALUE FOR HEADER IN ([JIB_IN],[REV],[REV_INSIGHT],[JIB_OUT],[CREATION])
) UNPIV
) SRC
PIVOT
(
SUM(VALUE)
FOR DECK IN ([A_1],[B_2],[C_3],[D_4],[E_5],[F_6])
) PIV

Using Dynamic SQL:

DECLARE @COLSUNPIVOT AS NVARCHAR(MAX),
@QUERY AS NVARCHAR(MAX),
@COLSPIVOT AS NVARCHAR(MAX)

SELECT @COLSUNPIVOT = STUFF((SELECT ','+QUOTENAME(C.NAME)
FROM SYS.COLUMNS AS C
WHERE C.OBJECT_ID = OBJECT_ID('REPORT') AND C.NAME <> 'DECK'
FOR XML PATH('')), 1, 1, '')

SELECT @COLSPIVOT = STUFF((SELECT ',' + QUOTENAME(DECK)
FROM REPORT T FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')

SET @QUERY
= 'SELECT HEADER, '+@COLSPIVOT+'
FROM
(
SELECT DECK,HEADER,VALUE FROM REPORT
UNPIVOT
(
VALUE FOR HEADER IN ('+@COLSUNPIVOT+')
) UNPIV
) SRC
PIVOT
(
SUM(VALUE)
FOR DECK IN ('+@COLSPIVOT+')
) PIV'

EXEC(@QUERY)


Related Topics



Leave a reply



Submit