Formatting column names and result in a pivoted SQL Select Query - sql-server

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

Related

Replacing null values in dynamic pivot sql query

Image transcription:
FTE/RATE CARD | 2018-01-01 | 2018-02-01 | 2018-03-01 | 2018-04-01 | 2018-05-01 | 2018-06-01 | ...
-------------------------------------------------------------------------------------------------
FTE 3 | NULL | NULL | NULL | 33 | 38 | 40.5 | ...
I have a pivot sql query as below.
I need to replace NULL values with Zeros in the result.
I don't know where do I exactly use the IsNull or Coalesce function in the query.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF
(
(
SELECT distinct ',' + QUOTENAME([MONTH])
FROM [HEADCOUNT]
WHERE [MONTH] BETWEEN '01-012018' AND '12-01-2018'
ORDER BY ',' + QUOTENAME([MONTH])
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''
)
SET #query = 'SELECT [FTE/RATE CARD],' + #cols + ' FROM
(
SELECT [MONTH],[FTE/RATE CARD],[HC]
FROM [HEADCOUNT]
WHERE [CC-LOC] IN ([CC-LOC])
) x pivot (Sum ([HC]) for [MONTH] in (' + #cols + '))p' execute(#query)
You'll need to generate another column list for the select statement, and wrap individual months with ISNULL, like so:
DECLARE #cols AS NVARCHAR(MAX),
#selectCols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #selectCols = STUFF
(
(
SELECT distinct ', ISNULL(' + QUOTENAME([MONTH]) + ', 0) AS ' + QUOTENAME([MONTH])
FROM [HEADCOUNT]
WHERE [MONTH] BETWEEN '01-012018' AND '12-01-2018'
ORDER BY ',' + QUOTENAME([MONTH])
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''
)
SELECT #cols = STUFF
(
(
SELECT distinct ',' + QUOTENAME([MONTH])
FROM [HEADCOUNT]
WHERE [MONTH] BETWEEN '01-012018' AND '12-01-2018'
ORDER BY ',' + QUOTENAME([MONTH])
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''
)
SET #query = 'SELECT [FTE/RATE CARD],' + #selectCols + ' FROM
(
SELECT [MONTH],[FTE/RATE CARD],[HC]
FROM [HEADCOUNT]
WHERE [CC-LOC] IN ([CC-LOC])
) x pivot (Sum ([HC]) for [MONTH] in (' + #cols + '))p' execute(#query)

Transpose columns and rows dynamically

I've tried to use information from similar subject but still can't cope with it.
I've got the data something like this:
+------------+--------------+--------+--------+------------+
| date | business_day | total | number | number_2 |
+------------+--------------+--------+--------+------------+
| 2018-04-03 | 1 | 325857 | 0 | 888 |
| 2018-04-04 | 2 | 196308 | 2 | 829 |
| 2018-04-05 | 3 | 434970 | 1 | 661 |
| 2018-04-06 | 4 | 665941 | 1 | 1135 |
| 2018-04-09 | 5 | 589334 | 0 | 837 |
+------------+--------------+--------+--------+------------+
How can I transpose it when number of rows could be dynamic (inthis case columns no), the final result should be like this:
+--------------+------------+------------+------------+------------+--------------+
| date | 2018-04-03 | 2018-04-04 | 2018-04-05 | 2018-04-06 | 2018-04-09 |
+--------------+------------+------------+------------+------------+--------------+
| business_day | 1 | 2 | 3 | 4 | 5 |
| total | 325857 | 196308 | 434970 | 665941 | 589334 |
| number | 0 | 2 | 1 | 1 | 0 |
| number_2 | 888 | 829 | 661 | 1135 | 837 |
+--------------+------------+------------+------------+------------+--------------+
Could you help me please?
If you need a dynamic query that automatically creates all the columns for all Date values you can use dynamic SQL, but be aware of security problems (SQL injection etc.) of this approach!
Here is a sample script that dynamically generates a query with all the columns for your Date:
if OBJECT_ID('Test') is not null
drop table [dbo].[Test]
CREATE TABLE [dbo].[Test]([date] datetime, business_day int, total int, number int, number_2 int)
insert into [dbo].[Test] select '2018-04-03', 1, 325857, 0, 888
insert into [dbo].[Test] select '2018-04-04', 2, 196308, 2, 829
insert into [dbo].[Test] select '2018-04-05', 3, 434970, 1, 661
insert into [dbo].[Test] select '2018-04-06', 4, 665941, 1, 1135
insert into [dbo].[Test] select '2018-04-09', 5, 589334, 0, 837
declare #dates nvarchar(max)='' --this variable holds all the dates that will become column names
declare #sql nvarchar(max)='' --this variable contains the TSQL dinamically generated
select #dates = #dates + ', [' + CONVERT(char(10), [date],126)+ ']' from [dbo].[Test]
set #dates = RIGHT(#dates, len(#dates)-2)
set #sql = #sql + 'select col as [date], ' + #dates
set #sql = #sql + 'from '
set #sql = #sql + '( '
set #sql = #sql + ' select [date] , col,val,ord '
set #sql = #sql + ' from [dbo].[Test] '
set #sql = #sql + ' CROSS APPLY ('
set #sql = #sql + ' VALUES (''business_day'',business_day, 1), '
set #sql = #sql + ' (''total'',total, 2), '
set #sql = #sql + ' (''number'',number, 3), '
set #sql = #sql + ' (''number_2'',number_2, 4) '
set #sql = #sql + ' )CS (COL,VAL,ORD) '
set #sql = #sql + ') src '
set #sql = #sql + 'pivot ( max(val) for [Date] in (' + #dates + ') ) piv '
set #sql = #sql + 'order by ord'
exec(#sql)
Here is the result of this statement:

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

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