SQL Transpose Pivot ? UnPivot? - sql-server

I have a table name table1
Partnumber | Model
12345 | A1
12345 | B2
12345 | C3
I would like my output to be:
Partnumber | Model1 | Model2 | Model3
12345 | A1 | B2 | C3
With the model columns N being dynamic.
Any ideas?

You can use dynamic conditional aggregation:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql =
'SELECT
PartNumber' + CHAR(10) +
STUFF((
SELECT DISTINCT
' ,MAX(CASE WHEN Model = ''' + Model + ''' THEN Model END) AS ' + QUOTENAME(Model) + CHAR(10)
FROM Tbl
FOR XML PATH('')
), 1, 0, '') +
'FROM Tbl
GROUP BY PartNumber;';
PRINT #sql;
EXEC sp_executesql #sql;

Related

Formatting column names and result in a pivoted SQL Select Query

I have a pivoted SQL query that will bring up a table. But I need to format the dynamic column names and the results.
This is the query:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
DECLARE #yearV varchar(max) = '2019'
DECLARE #idclienteV varchar(max) = 1
select #cols = STUFF(
(
SELECT ',' + QUOTENAME([Y].[FechaPpto])
FROM [dbo].[Ingresos_Ppto] [Y]
WHERE
([Y].[IdCliente] = #idclienteV) AND (DATEPART(YEAR, [Y].[FechaPpto]) = #yearV)
GROUP BY [Y].[FechaPpto]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'')
set #query = N'DECLARE #year int = ' + #yearV + ' DECLARE #idc int = ' + #idclienteV + ' SELECT Rubro, ' + #cols + N' FROM
(
SELECT [Y].[Rubro], [Y].[Valor], [Y].[FechaPpto]
FROM [Ingresos_Ppto] [Y]
WHERE
([Y].[IdCliente] = #idc)
AND
(DATEPART(YEAR, [Y].[FechaPpto]) = #year)
) X
PIVOT
(
max(Valor)
FOR FechaPpto in (' + #cols + N')
) p'
EXEC sp_executesql #query;
And this is the table that it'll bring:
Rubro | Jan 1 2019 12:00 AM | Feb 1 2019 12:00 AM | Mar 1 2019 12:00 AM
+--------------------------+---------------------+---------------------+----------------------
Branding | 3280937.00 | 4912017.00 | 2404802.00
+--------------------------+---------------------+---------------------+----------------------
Mercadeo y publicidad | 3372619.00 | 7423175.00 | 8736550.00
+--------------------------+---------------------+---------------------+----------------------
Portal web WP | 9148489.00 | 9172295.00 | 4643597.00
+--------------------------+---------------------+---------------------+----------------------
Portal web WP + ecommerce | 3785304.00 | 9140700.00 | 7425106.00
+--------------------------+---------------------+---------------------+----------------------
Renting tecnológico | 7223406.00 | 7298693.00 | 8768783.00
+--------------------------+---------------------+---------------------+----------------------
So I need to change the dates column name and the result, to get this:
Rubro | Januray 2019 | February 2019 | March 2019
+--------------------------+---------------------+---------------------+----------------------
Branding | $3,280,937.00 | $4,912,017.00 | $2,404,802.00
+--------------------------+---------------------+---------------------+----------------------
Mercadeo y publicidad | $3,372,619.00 | $7,423,175.00 | $8,736,550.00
+--------------------------+---------------------+---------------------+----------------------
Portal web WP | $9,148,489.00 | $9,172,295.00 | $4,643,597.00
+--------------------------+---------------------+---------------------+----------------------
Portal web WP + ecommerce | $3,785,304.00 | $9,140,700.00 | $7,425,106.00
+--------------------------+---------------------+---------------------+----------------------
Renting tecnológico | $7,223,406.00 | $7,298,693.00 | $8,768,783.00
+--------------------------+---------------------+---------------------+----------------------
So far this is what I've tried:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
DECLARE #yearV varchar(max) = '2019'
DECLARE #idclienteV varchar(max) = 1
select #cols = STUFF(
(
SELECT ',' + QUOTENAME(FORMAT(dateadd(month, 0, [Y].[FechaPpto]), 'MMM', 'es-co'))
FROM [dbo].[Ingresos_Ppto] [Y]
WHERE
([Y].[IdCliente] = #idclienteV) AND (DATEPART(YEAR, [Y].[FechaPpto]) = #yearV)
GROUP BY FORMAT(dateadd(month, 0, [Y].[FechaPpto]), 'MMM', 'es-co')
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'')
set #query = N'DECLARE #year int = ' + #yearV + ' DECLARE #idc int = ' + #idclienteV + ' SELECT Rubro, ' + #cols + N' FROM
(
SELECT [Y].[Rubro], [Y].[Valor], [Y].[FechaPpto]
FROM [Ingresos_Ppto] [Y]
WHERE
([Y].[IdCliente] = #idc)
AND
(DATEPART(YEAR, [Y].[FechaPpto]) = #year)
) X
PIVOT
(
max(Valor)
FOR FORMAT(dateadd(month, 0, [Y].[FechaPpto]), 'MMM', 'es-co') in (' + #cols + N')
) p'
EXEC sp_executesql #query;
But I'm getting multiple syntax errors.
How can I format that result?
You need to double the single-quotes in your query string:
FOR FORMAT(dateadd(month, 0, [Y].[FechaPpto]), 'MMM', 'es-co') in
Needs to be:
FOR FORMAT(dateadd(month, 0, [Y].[FechaPpto]), ''MMM'', ''es-co'') in
etc

SQL Dynamic Pivot Table [duplicate]

This question already has answers here:
Efficiently convert rows to columns in sql server
(5 answers)
Closed 6 years ago.
I'm struggling with an error while making a dynamic pivot table
The source data is
JobID | SalesForMonth | YearMonth
7734 | 400 | 2016-12
7734 | 350 | 2017-01
8540 | 444 | 2016-12
8540 | 300 | 2017-01
and aiming for
JobID | 2016-12 | 2017-01
7734 | 400 | 350
8540 | 444 | 300
and I've tried to use a query I found on here to create the column headers. But must admit I don't really understand the 'For XML' line and getting a syntax error there on line 6
DECLARE
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(YearMonth)
FROM v_JobSalesByMonth
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
SELECT #query =
'SELECT * FROM
(SELECT JobID, YearMonth, SalesForMonth
FROM v_JobSalesByMonth) X
PIVOT
(
(JobID, SalesForMonth)
for [YearMonth] in (' + #cols + ')
) P'
I'd also like to stick in a 'total sales' for the jobID column
Any help would be much appreciated
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(YearMonth) From v_JobSalesByMonth Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [JobID],[TotalSales],' + #SQL + '
From (
Select JobID
,TotalSales = sum(SalesForMonth) over (Partition By JobID)
,YearMonth
,SalesForMonth
From v_JobSalesByMonth A
) A
Pivot (sum(SalesForMonth) For [YearMonth] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
EDIT - Dynamically Create View
Since you can't have dynamic SQL in a view, you could have job scheduled (daily or monthly) to drop and re-create the view.
if object_id('vw_SalesByMonth','v') is not null
drop view vw_SalesByMonth;
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(YearMonth) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Create View vw_SalesByMonth
AS
Select [JobID],[TotalSales],' + #SQL + '
From (
Select JobID
,TotalSales = sum(SalesForMonth) over (Partition By JobID)
,YearMonth
,SalesForMonth
From YourTable A
) A
Pivot (sum(SalesForMonth) For [YearMonth] in (' + #SQL + ') ) p'
Exec(#SQL);
Select * from vw_SalesByMonth

SQL Pivot without aggregation

I have a Problem converting rows in columns of a SQL Result.
My structure is like this:
GUID | PropertyName | PropertyValue
abcd | DistinguishedName | cn=abcd...
abcd| CN | cn= GROUP_
abcd| Operation | ADD
1231 | DistinguishedName| cn=123dd
1231 | Cn | cn=ASDGRUOP
1231 | Operation | DEL
There can be n PropertyNames that I dont know before, they are dynamically - i can get them through an SQL, that is not the Problem.
I want to have a structure like this:
GUID | DistinguishedName | CN | Operation
abcd| cn=abcd...| cn= GROUP_ | ADD
1231 | cn=123dd | cn=ADSGROUP | DEL
and so on.
The Column-Headers i get by this SQL:
select #cols = STUFF (( SELECT DISTINCT '],[' + x.ParameterName from ... and parametername in ('PropertyValue','DistinguishedName', 'Operation')
FOR XML PATH ('')),1,2,'') + ']'
I can do this with PIVOT-Function but because i dont have Aggregation, I cant get the right result:
set #query = N'SELECT '+ #cols + ' FROM (
SELECT x.parametervalue, x.parametername
from ... and parametername in (''PropertyValue'',''DistinguishedName'', ''Operation'')
) a
PIVOT (max(a.parametervalue) FOR ParameterName in ( ' + #cols + ')) as pv;'
exec sp_executesql #query;
I get the following result:
GUID | DistinguishedName | CN | Operation | ... other Propertys
abcd | cn=abcd... | cn = GROUP_ |ADD |...
Only 1 Result - not more. But there are like 700 Results from this query, because of the MAX() function i get only one. How can I get a Pivot without Aggregation to get all of the results?
Thank you in Advance!
Here is the dynamic PIVOT query:
DECLARE #sql NVARCHAR(MAX),
#cols NVARCHAR(MAX);
SELECT #cols =
STUFF((
SELECT DISTINCT ',' + QUOTENAME(PropertyName)
FROM #tbl
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
SELECT #sql =
'SELECT GUID, ' + #cols + '
FROM (
SELECT
GUID, PropertyName, PropertyValue
FROM #tbl
) t
PIVOT
(
MAX(PropertyValue)
FOR PropertyName IN(' + #cols + ')
) p ';
PRINT (#sql);
EXEC (#sql);
ONLINE DEMO
Another way to achieve the desired result is to use a dynamic crosstab:
DECLARE #sql NVARCHAR(MAX);
SELECT #sql =
'SELECT
GUID' + CHAR(10) +
(SELECT DISTINCT
' , MAX(CASE WHEN PropertyName = ''' + PropertyName + ''' THEN PropertyValue END) AS ' + QUOTENAME(PropertyName) + CHAR(10)
FROM #tbl
FOR XML PATH('')
) +
'FROM #tbl
GROUP BY GUID;';
PRINT (#sql);
EXEC (#sql);
ONLINE DEMO

In Sybase Show row values as column names and fill this rows with values of another column

I have a table like
ID | TableID | FieldNames | Values
1 | 1 | FirstName | Value 1
2 | 1 | LastName | Value 2
3 | 1 | City | Value 3
4 | 2 | FirstName | Value 4
5 | 2 | LastName | Value 5
6 | 2 | City | Value 6
I need to show it in a grid like below
TableID | FirstName | LastName | City
1 | Value1 | Value2 | Value3
2 | Value4 | Value5 | value6
I know the concept PIVOT will help to get this implemented in MS SQL. But I need to implement this in Sybase. Field names are not fixed.
Do a 3-way self-join:
SELECT t1.Value as FirstName, t2.Value as LastName, t3.Value as City
FROM t t1, t t2, t t3
WHERE t1.ID + 1 = t2.ID
AND t1.ID +2 = t3.ID
With the help of various posts I have figured it out that I can do this using a dynamic case statement, like below.
DECLARE #query VARCHAR(800)
DECLARE #fieldName VARCHAR(255)
DECLARE #LoopCounter INT, #MaxCount INT
SET #query = 'select f.tableID, '
SET #fieldName = ''
SET #LoopCounter = 1
SET #MaxCount = (SELECT COUNT(DISTINCT FieldNames) FROM dbo.Table1)
WHILE (#LoopCounter <= #MaxCount)
BEGIN
SET #fieldName = (SELECT FieldNames FROM dbo.table1 WHERE ID= #LoopCounter)
SET #query = #query + ' max(case when f.FieldNames = ''' + #fieldName +''' then f.[Values] end) AS ''' + #fieldName + ''''
IF(#LoopCounter < #MaxCount)
BEGIN
SET #query = #query + ', '
END
SET #LoopCounter = #LoopCounter + 1
END
SET #query = #query + ' FROM dbo.table1 f GROUP BY f.TableID'
exec (#query)

Simplify Dynamic SQL Pivot Table

I have written a Dynamic Pivot Table Query based on the following. Here is a SQL FIDDLE for reference.
CREATE TABLE TestTable1 ([idnumber] INT, [DataTypeId] INT)
GO
INSERT INTO TestTable1
VALUES (1, 108), (1, 108), (1, 108), (2, 108),
(2, 108), (3, 108), (1, 109),(1, 109),
(1, 110),(2, 110),(1, 111),(4, 108),(4, 108),
(4, 110),(4, 111)
GO
Here is the Dynamic SQL that I wrote
DECLARE #SQL NVARCHAR(MAX),
#Cols NVARCHAR(MAX),
#ColsP NVARCHAR(MAX)
SELECT #Cols = STUFF((select ',
ISNULL([' + CAST([DataTypeId] as varchar(10)) + '], 0) AS ''' + CAST([DataTypeId] as varchar(10)) + ''''
FROM
(
SELECT [DataTypeId] FROM [TestTable1]
GROUP BY [DataTypeId]
HAVING [DataTypeId] <> ''
) AS d
ORDER BY [DataTypeId] FOR XML PATH(''),type).value('.','varchar(max)'),1,2,'')
-- /////////////THIS IS WHAT I WANT REMOVED ////////////////////
SELECT #ColsP = STUFF((select ',
[' + CAST([DataTypeId] as varchar(10)) + ']'
FROM
(
SELECT [DataTypeId] FROM [TestTable1]
GROUP BY [DataTypeId]
HAVING [DataTypeId] <> ''
) AS d
ORDER BY [DataTypeId] FOR XML PATH(''),type).value('.','varchar(max)'),1,2,'')
-- /////////////////////////////////////////////////////////////
SET #SQL = 'SELECT idnumber,' + #Cols + '
FROM
(SELECT idnumber, COUNT([DataTypeId]) AS Total, [DataTypeId] FROM [TestTable1]
GROUP BY idnumber, [DataTypeId]
HAVING [DataTypeId] <> ''''
) p
PIVOT
(
SUM(Total) FOR [DataTypeId] IN (' + #ColsP + ')
) AS pvt
ORDER BY pvt.idnumber'
-- print #SQL
EXECUTE( #SQL)
I get the result that I want:
| IDNUMBER | 108 | 109 | 110 | 111 |
|----------|-----|-----|-----|-----|
| 1 | 3 | 2 | 1 | 1 |
| 2 | 2 | 0 | 1 | 0 |
| 3 | 1 | 0 | 0 | 0 |
| 4 | 2 | 0 | 1 | 1 |
But I am sure it can be done better. I would like to remove where I populate the variable #ColsP - SELECT #ColsP = STUFF((select...")
There should be a way where I can create this dynamic code with just one loop through TestTable1. As you can see, I loop through it twice. Once to read what columns to create for the select statement, and once for the PIVOT table.
Here is the code that is generated by the Dynamic SQL:
SELECT idnumber,
ISNULL([108], 0) AS '108',
ISNULL([109], 0) AS '109',
ISNULL([110], 0) AS '110',
ISNULL([111], 0) AS '111'
FROM
(
SELECT idnumber, COUNT([DataTypeId]) AS Total, [DataTypeId]
FROM [TestTable2]
GROUP BY idnumber, [DataTypeId]
HAVING [DataTypeId] <> ''
) p
PIVOT
(
SUM(Total) FOR [DataTypeId] IN ([108], [109], [110], [111])
) AS pvt
ORDER BY pvt.idnumber
You can shorten your code considerably. First, you can just use count to aggregate the data in the PIVOT. There is no need for the inner count to aggregate the data or the HAVING clause. Finally, you only need to create the list of columns once. You could easily improve the code to:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(DataTypeId)
from TestTable1
group by DataTypeId
order by DataTypeId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= N'SELECT idnumber, ' + #cols + N'
from
(
select idnumber, DataTypeId
from TestTable1
) x
pivot
(
count(DataTypeId)
for DataTypeId in (' + #cols + N')
) p '
exec sp_executesql #query;
See SQL Fiddle with Demo. This gives the same result:
| IDNUMBER | 108 | 109 | 110 | 111 |
|----------|-----|-----|-----|-----|
| 1 | 3 | 2 | 1 | 1 |
| 2 | 2 | 0 | 1 | 0 |
| 3 | 1 | 0 | 0 | 0 |
| 4 | 2 | 0 | 1 | 1 |
Try replacing it with this.
SET NOCOUNT ON
IF OBJECT_ID('TestTable1') IS NOT NULL
DROP TABLE TestTable1
GO
CREATE TABLE TestTable1 ([idnumber] INT, [DataTypeId] INT)
GO
INSERT INTO TestTable1 VALUES
(1, 108),(1, 108),(1, 108),(2, 108),(2, 108),
(3, 108),(1, 109),(1, 109),(1, 110),(2, 110),
(1, 111),(4, 108),(4, 108),(4, 110),(4, 111)
DECLARE
#AllColumns NVARCHAR(MAX)
SELECT #AllColumns = ''
SELECT #AllColumns = #AllColumns +
'[' + CAST(DataTypeId as NVARCHAR)+'],'
FROM TestTable1
GROUP BY DataTypeId
SET #AllColumns = LEFT(#AllColumns,LEN(#AllColumns)-1)
PRINT #AllColumns

Resources