Group Pivot results - sql

After a longtime of googling and figuring out how to put my data in a decent pivot table with dynamic row headers I got to this point.
The only thing I can't figure out is how to group the results by [Location] and how to replace NULL by 'zero' / 0?
To replace NULL by 0 I tried ISNULL() and COALESCE() in this line, but it doesnt change the NULL:
SELECT COALESCE(ROUND(CAST([Remaining Quantity] AS decimal (2,0)), 1),0) AS [Remaining QuantityRound], *
or
SELECT ISNULL(ROUND(CAST([Remaining Quantity] AS decimal (2,0)), 1),0) AS [Remaining QuantityRound], *
The SQL query I have now:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
declare #item varchar(max);
declare #open varchar(max);
set #item = 291557
set #open = 1
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME([Size])
FROM (SELECT DISTINCT [Size] FROM [Table] WHERE [Item] = #item AND [Open] = #open) AS Items
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
'SELECT [Location], ' + #ColumnName + '
FROM
(SELECT ROUND(CAST([Quantity] AS decimal (2,0)), 1) AS [QuantityRound], * FROM [Table]
WHERE [Item] = ''' + #item + ''' AND [Open] = ''' + #open + ''') x
PIVOT(SUM([QuantityRound])
FOR [Size] IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
Result:
Location S M L
001 1 NULL NULL
001 NULL 1 NULL
002 NULL NULL 2
002 NULL 1 NULL
What I would like to achieve:
Location S M L
001 1 1 0
002 0 1 2

Remove the * in the subquery:
(SELECT ROUND(CAST([Quantity] AS decimal (2,0)), 1) AS [QuantityRound], SIZE, LOCATION FROM [Table]
The extra columns are causing the extra rows.

Related

Replace null values with 0 in PIVOT

I tried to convert the (null) values with 0 (zeros) output in PIVOT function but have no success.
Below is the table and the syntax I've tried:
SELECT DISTINCT isnull([DayLoad],0) FROM #Temp1
Data in the table #Temp1:
zone dayB templt cid DayLoad
other 10 other 1 2020-05-28
other 10 other 1 2020-05-29
other 10 other 1 2020-05-30
other 10 other 1 2020-05-31
other 4 other 1 2020-06-02
other 10 other 1 2020-06-02
other 10 other 1 2020-06-01
My request:
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + CONVERT(NVARCHAR, [DayLoad], 106) + ']',
'[' + CONVERT(NVARCHAR, [DayLoad], 106) + ']')
FROM (SELECT DISTINCT [DayLoad] FROM #Temp1) PV
ORDER BY [DayLoad]
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT *
into #temptable
FROM
(
SELECT
''''+[zone]+'' '' + ''''+convert(varchar(50),[dayB])+''''+''+'' +'' ''+(case when [templt]=''Прочее'' then '''' else [templt] end)+'''' as [zone/dayB]
,[DayLoad]
,[cid]
,[dayB]
,[zone]
FROM #Temp1
) x
PIVOT
(
sum([cid])
FOR [DayLoad] IN ('+ #cols + ')
) p
select *
from #temptable
order by [zone],[dayB]
drop table #temptable
'
EXEC(#query)
DROP TABLE #Temp1
Please refer below example, when you have nulls and empty string, right option is to go with case statement,
SELECT DISTINCT case when ([DayLoad] is null or [DayLoad] = '') then 0 else [DayLoad] end FROM #Temp1

Using pivot in SQL Server not returning desired output

I have two tables like this:
**tblTagDescription**
and **tblDataLog**
Now I want to show record of any specific group and in one there might be same group for multiple id in tbltagdescription. And id of tbltagdescription is foreign key for tblDataLog as TagDescID.
Here 'Group1' has 10 ID as from 1 to 10. and there might be multiple record for these ID (from 1 to 10) in tbldatalog. I want these ID from 1 to as columns. For this I used pivot:
DECLARE #COlsID NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
DECLARE #Group NVARCHAR(50) = 'Group1'
IF OBJECT_ID('tempdb..##MYTABLE') IS NOT NULL
DROP TABLE ##MYTABLE
SELECT
#COlsID = COALESCE(#ColsID + '],[','') + CONVERT(NVARCHAR(5), z.TagDescID)
FROM
(SELECT DISTINCT TOP 50 tblDataLog.TagDescID
FROM tblDataLog
INNER JOIN tblTagDescription ON tblDataLog.TagDescID = tblTagDescription.ID
ORDER BY tblDataLog.TagDescID) z
SET #COlsID='[' + #COlsID + ']'
SET #SQL='select [DATE],SHIFT, ' + #COlsID + ' into ##MYTABLE from ( select [Date], Value,
(CASE
WHEN ((DATEPART(hour,[DATE]))>6 and (DATEPART(hour,[DATE]))<14) THEN ''A''
WHEN ((DATEPART(hour,[DATE]))>=14 and (DATEPART(hour,[DATE]))<22) THEN ''B''
WHEN ((DATEPART(hour,[DATE]))>=22 or (DATEPART(hour,[DATE]))<6) THEN ''C''
END )AS SHIFT
from tblDataLog )d pivot(max(Value) for TagDescID in (' + #COlsID + ')) piv;'
EXEC (#SQL)
Now when I execute this statement, I get an error:
Invalid column name 'TagDescID'
but there is this column in tbldatalog. How to solve this query?
You need TagDescID column in subquery.
DECLARE #COlsID NVARCHAR(MAX) = ''
DECLARE #COlsAlias NVARCHAR(MAX) = ''
DECLARE #SQL NVARCHAR(MAX)
DECLARE #Group NVARCHAR(50) = 'Group1'
SELECT
#COlsID = #ColsID + ',' + z.TagDescID,
#COlsAlias = #COlsAlias + ',' + z.TagDescID + ' AS ' + z.ReportTag
FROM
(SELECT DISTINCT TOP 50 tblDataLog.TagDescID ID, QUOTENAME(CONVERT(NVARCHAR(5), tblDataLog.TagDescID )) TagDescID, QUOTENAME(tblTagDescription.ReportTag) ReportTag
FROM tblDataLog
INNER JOIN tblTagDescription ON tblDataLog.TagDescID = tblTagDescription.ID
ORDER BY tblDataLog.TagDescID
) z
SET #COlsID= STUFF(#COlsID,1,1,'')
SET #COlsAlias= STUFF(#COlsAlias,1,1,'')
SET #SQL='select [DATE],SHIFT, ' + #COlsAlias + ' into ##MYTABLE from ( select [Date], Value, TagDescID,
(CASE
WHEN ((DATEPART(hour,[DATE]))>6 and (DATEPART(hour,[DATE]))<14) THEN ''A''
WHEN ((DATEPART(hour,[DATE]))>=14 and (DATEPART(hour,[DATE]))<22) THEN ''B''
WHEN ((DATEPART(hour,[DATE]))>=22 or (DATEPART(hour,[DATE]))<6) THEN ''C''
END )AS SHIFT
from tblDataLog )d pivot(max(Value) for TagDescID in (' + #COlsID + ')) piv;'
EXEC (#SQL)

Turn Columns into Rows partitioned

I have a table like this:
CFL52_ID CFL52_PRICE CFL51_MILEAGE
------------ --------------- -----------------
1 100000.00 10000
1 200000.00 20000
2 800000.00 10000
2 900000.00 20000
I want to pivot the columns into rows. Mileage was the column title and Price the value. Turning into something like this:
CFL52_ID [10000] [20000]
------------ --------------- -----------------
1 100000.00 200000.00
2 800000.00 900000.00
Notice that id was grouping, prices are pivoted and mileage turning into columns title, Mileage are dynamic -could increment- .
I've tried with no success with this:
SELECT [10000],[20000]
FROM ( SELECT
CFL52_PRICE as indicatorvalue,
CFL51_MILEAGE as indicatorname
FROM [TFL52_PRICES_M] p
INNER JOIN [TFL51_PARAM_MILEAGE] k ON CFL52_CFL51_CODIGO = CFL51_CODIGO
WHERE CFL52_DATES = '2018-07-01 00:00:00.000' AND CFL52_DATEEN= '2018-07-02 00:00:00.000') as a
pivot
(
max(indicatorvalue) for indicatorname in ([10000],[20000])
) p
just use conditional aggregation instead. You also should use aliases to indicate which table a column belongs to.
select CFL52_ID
, [10000] = MAX(case when CFL51_MILEAGE = 10000 then CFL52_PRICE end)
, [20000] = MAX(case when CFL51_MILEAGE = 20000 then CFL52_PRICE end)
FROM [TFL52_PRICES_M] p
INNER JOIN [TFL51_PARAM_MILEAGE] k ON CFL52_CFL51_CODIGO = CFL51_CODIGO
WHERE CFL52_DATES = '2018-07-01 00:00:00.000'
AND CFL52_DATEEN= '2018-07-02 00:00:00.000'
group by CFL52_ID
To manage dynamic titles you need dynamic TSQL:
if OBJECT_ID('dbo.test') is null
create table dbo.test(CFL52_ID varchar(50), CFL52_PRICE decimal(18,2), CFL51_MILEAGE int)
--populate test table
insert into dbo.test values
(1, 100000.00, 10000),
(1, 200000.00, 20000),
(2, 800000.00, 10000),
(2, 900000.00, 20000)
declare #columns nvarchar(max)='' --holds all the numbers that will become column names
declare #sql nvarchar(max)='' --contains the TSQL dinamically generated
--generate dynamic column names
select #columns = #columns + ', [' + cast(CFL51_MILEAGE as varchar(max))+ ']'
from dbo.test
group by CFL51_MILEAGE
--remove first (unnecessary) comma
set #columns = RIGHT(#columns, len(#columns)-2)
--build dynamic TSQL query
set #sql = #sql + ' select piv.[CFL52_ID], ' + #columns
set #sql = #sql + ' from ( '
set #sql = #sql + ' select [CFL52_ID], [CFL52_PRICE], [CFL51_MILEAGE] '
set #sql = #sql + ' from dbo.test '
set #sql = #sql + ' ) src '
set #sql = #sql + ' pivot ( '
set #sql = #sql + ' max([CFL52_PRICE]) '
set #sql = #sql + ' for [CFL51_MILEAGE] in ('+#columns+') '
set #sql = #sql + ' ) piv '
--execute dynamic TSQL query
exec(#sql)
output:
If you add more rows to your input table with CFL51_MILEAGE values of 10000 and 20000, then input and output tables will be:
If you add more rows to your input table introducing new CFL51_MILEAGE values (other than 10000 or 20000), then input and output tables will be:

Dynamic pivot null to 0

I've got a stored procedure that is taking data from one table, running through my dynamic pivot stored procedure, and outputting into the page. The problem is, there is a substantial number of null entries. When I process this data on the page, I'm needing to add each of the fuel quantities per TerminalID. The issue arises when it hits the null entries. I don't want to have the procedure read through every row and column to convert null to 0, and was hoping to do so in the SP.
For Testing, I've made this script:
DECLARE #QUERY NVARCHAR(MAX)
, #Soucecolumn VARCHAR(MAX)
, #BeginningDate VARCHAR(MAX)
, #EndingDate VARCHAR(MAX)
, #CompanyID VARCHAR(2)
SET NOCOUNT ON;
SET #BeginningDate = CONVERT(VARCHAR(30), CAST('2004-01-01' AS DATE));
SET #EndingDate = CONVERT(VARCHAR(30), CAST('2007-01-01' AS DATE));
SET #CompanyID = CONVERT(INT, '2');
SET #Soucecolumn = STUFF((
SELECT DISTINCT ', \[' + CAST(FuelTypeID AS VARCHAR(4)) + '\]'
FROM tt_Manifest_Fuel_Distribution
FOR XML PATH ('')), 1, 1, '')
SET #QUERY = '(
SELECT ManifestID, TerminalID, ' + #Soucecolumn + '
FROM (
SELECT mfd.ManifestID, m.TerminalID, mfd.FuelTypeID, mfd.FuelQuantity
FROM tt_Manifest_Fuel_Distribution mfd, tt_Terminals t, tt_Fuel_Types ft, tt_Manifests m
WHERE mfd.FuelTypeID=ft.FuelTypeID
AND m.ManifestID=mfd.ManifestID
AND m.CompanyID= ' + #CompanyID + '
AND m.ManifestInsertDate BETWEEN ''' + #BeginningDate + ''' AND ''' + #EndingDate +
'''
) up
PIVOT (
MAX(FuelQuantity) FOR \[FuelTypeID\] IN (' + #Soucecolumn + ')
) AS pvt)'
EXEC sp_executesql #QUERY
Sample data is:
>ManifestID TerminalID 3 6 4 2 1 5
>417 1 NULL NULL NULL NULL NULL 2478
>421 1 NULL NULL NULL NULL 3458 NULL
>508 1 NULL NULL NULL NULL NULL 2471
>826 1 NULL NULL NULL NULL NULL 7464
>832 1 NULL NULL NULL NULL 3482 NULL
>833 1 1001 NULL NULL NULL 1492 NULL
>844 1 NULL NULL NULL NULL 2498 NULL
>870 1 NULL NULL NULL NULL 5991 2503
>872 1 NULL NULL NULL NULL 3494 NULL
>2 2 NULL NULL 5514 NULL NULL 2505
>43 2 NULL NULL NULL NULL 7011 NULL
>46 2 1005 NULL NULL NULL 5007 2510
>60 2 NULL NULL 3502 NULL NULL 4513
>63 2 NULL NULL 4505 NULL NULL 3008
>69 2 NULL NULL 4008 NULL 4508 NULL
>78 2 1007 NULL NULL NULL 5022 NULL
>79 2 NULL NULL 2505 NULL NULL NULL
I've tried placing ISNULL(,0) around the mfd.FuelQuantity, and around the #Sourcecolumn. mfd.FuelQuantiity seemed to have no change, while the #Sourcecolumn error-ed out claiming that the ISNull() required 2 arguments.
Am I looking at this in the wrong way?
I'd strongly suggest moving away from deprecated implicit joins.
You need to incorporate ISNULL() into each item in the #sourcecolumn list in the SELECT clause. The reason it threw an error is because your entire list of columns was wrapped in one statement: ISNULL(col1,col2,col3...,0) you need ISNULL(col1,0),ISNULL(col2,0)...
I'd suggest making a separate sourcecolumn variable for use in your SELECT.
Something like:
SET #Sourcecolumn2 = STUFF((SELECT distinct ',ISNULL(\[' + CAST(FuelTypeID as varchar(4)) + ',0)\]as '+ CAST(FuelTypeID as varchar(4)) +' FROM tt_Manifest_Fuel_Distribution
FOR XML PATH('')),1,1,'')
So ultimately:
![Declare #QUERY NVARCHAR(MAX),
#Soucecolumn VARCHAR(MAX),
#Sourcecolumn2 VARCHAR(MAX),
#BeginningDate VARCHAR(MAX),
#EndingDate VARCHAR(MAX),
#CompanyID VARCHAR(2)
SET NOCOUNT ON;
SET #BeginningDate = convert(varchar(30), cast('2004-01-01' as date));
SET #EndingDate = convert(varchar(30), cast('2007-01-01' as date));
SET #CompanyID = convert(int, '2');
SET #Soucecolumn = STUFF((SELECT distinct ', \[' + CAST(FuelTypeID as varchar(4)) + '\]' FROM tt_Manifest_Fuel_Distribution
FOR XML PATH('')),1,1,'');
SET #Sourcecolumn2 = STUFF((SELECT distinct ',ISNULL(\[' + CAST(FuelTypeID as varchar(4)) + ',0)\] as '+ CAST(FuelTypeID as varchar(4))+' FROM tt_Manifest_Fuel_Distribution
FOR XML PATH('')),1,1,'');
SET #QUERY = '(SELECT ManifestID, TerminalID, ' + #Sourcecolumn2 + ' FROM (
SELECT mfd.ManifestID, m.TerminalID, mfd.FuelTypeID, mfd.FuelQuantity
FROM tt_Manifest_Fuel_Distribution mfd, tt_Terminals t, tt_Fuel_Types ft, tt_Manifests m
WHERE mfd.FuelTypeID=ft.FuelTypeID
AND m.ManifestID=mfd.ManifestID
AND m.CompanyID= ' + #CompanyID + '
AND m.ManifestInsertDate BETWEEN ''' + #BeginningDate + ''' AND ''' + #EndingDate +
''' ) up PIVOT (MAX(FuelQuantity) FOR \[FuelTypeID\] IN (' + #Soucecolumn + ')) AS pvt)'
exec sp_executesql #QUERY][1]
Consider the below table
Here is the sample data
SELECT * INTO #TEMP
FROM
(
SELECT '01/JAN/2014' [DATE],'A' NAME,100 MARKS
UNION ALL
SELECT '02/JAN/2014' [DATE],'A' NAME,120
UNION ALL
SELECT '02/JAN/2014' [DATE],'B' NAME,130
UNION ALL
SELECT '03/JAN/2014' [DATE],'B' NAME,115
UNION ALL
SELECT '01/JAN/2014' [DATE],'C' NAME,123
UNION ALL
SELECT '01/JAN/2014' [DATE],'C' NAME,134
UNION ALL
SELECT '03/JAN/2014' [DATE],'C' NAME,146
UNION ALL
SELECT '04/JAN/2014' [DATE],'C' NAME,149
)TAB
Now select the distinct names to a variable for pivot
DECLARE #cols NVARCHAR (MAX)
SET #cols = SUBSTRING((SELECT DISTINCT ',['+NAME+']'
FROM #TEMP GROUP BY NAME FOR XML PATH('')),2,8000)
Now you need another variable to apply the NULL to zero logic
DECLARE #NulltoZeroCols NVARCHAR (MAX)
SET #NulltoZeroCols = SUBSTRING((SELECT DISTINCT ',ISNULL(['+NAME+'],0) AS ['+NAME+']'
FROM #TEMP GROUP BY NAME FOR XML PATH('')),2,8000)
Now pivot the query using both variables
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT DATE,' + #NulltoZeroCols + ' FROM
(
SELECT [DATE],NAME,MARKS FROM #TEMP
) x
PIVOT
(
SUM(MARKS)
FOR [NAME] IN (' + #cols + ')
) p
;'
EXEC SP_EXECUTESQL #query
Finally your result is as below

How to create Sql Server 2008 Cross Tab Dynamic Query?

I have 3 tables:
Locations: LocationID, LocationName
Defect: DefectID, DefectType
Feedback: feedbackID, DefectID, LocationID
I need cross tab report in the below format: Number below the locations column is the total number of defects for that location. Locations can be any number. it should be dynamic..
DefectID DefectType NewYork NewJersey Texas Houston
1 Defect1 0 10 3 6
2 Defect2 0 0 9 10
3 Defect3 8 8 4 6
I have a SQL query which is hardcoded. Also, it's not displaying the DefectID..
select
DefectType,
[1] as NewYork,
[4] as NewJersy,
[5] as Texas,
[6] as Houston
from (select
Defect.DefectID,
Defect.DefectType,
Location.LocationID
from Feedback
inner join Locations on (Feedback.LocationID= Location.LocationID)
inner join DefectType on (Feedback.DefectID= Defect.DefectID)
) p
pivot
( count (DefectID) for LocationID in ( [1], [4], [5],[6] ) ) as pvt
order by pvt.DefectType;
You can't dynamically build the cross tab values with SQL Server PIVOT and pure static SQL. You have to build the SQL dynamically, either with some external scripting language building the query, or with T-SQL using execute
DECLARE #locationID int, #LocationName nvarchar(50),
#columnList nvarchar(max), #idList nvarchar(max), #sql nvarchar(max) ;
DECLARE location_cursor CURSOR
FOR SELECT locationID, LocationName FROM Locations
SET #columnList = '';
SET #idList = '';
OPEN location_cursor
FETCH NEXT FROM vendor_cursor
INTO #locationID, #LocationName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #columnList = #columnList + ', [' + #locationID + '] as [' + #locationName + ']'
SET #idList = #idList + '[' + #locationID + '],'
END
CLOSE location_cursor
SET #sql = 'select DefectType' + #columnList + ' from (select Defect.DefectID, Defect.DefectType, Location.LocationID from Feedback inner join Locations on (Feedback.LocationID= Location.LocationID)
inner join DefectType on (Feedback.DefectID= Defect.DefectID)
) p pivot
( count (DefectID) for LocationID in (' + left(#idList,len(#idList)-1) + ') ) as pvt order by pvt.DefectType'
EXECUTE (#sql)
I haven't tested this, obviously, but it should work (with minor tweaks possibly needed).