Group by and aggregate functions in dynamic SQL Pivot Query - sql

I have formulated the following dynamic SQL Query to turn an unkown number of row values (Maschine Names as nvarchar) into columns. The Row Values of the Pivoted Columns should be the sum of downtime and maintenance time (both int) for the particular Maschine.
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME(Maschines)
FROM (SELECT Maschines FROM Rawdata AS p
GROUP BY MASCHINE) AS x;
SET #sql = N'
SELECT ' + STUFF(#columns, 1, 2, '') + '
FROM(
SELECT * from Rawdata
) AS j
PIVOT
(
SUM(maintenance) FOR Maschines IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+')
) AS p;';
EXEC sp_executesql #sql;
The Query returnes the Summed up maintenance time, but once i try to include more than one column in the sum ( like Sum(maintenance+downtime) ) i get an error close to '+'.
In addition, the query returns a pivoted table, but still has the same number of rows, however i need a result that is grouped for the unkown number of columns, thus containing only one row

So unfortunately PIVOT in SQL is fairly limited in that you can only perform a single aggregation of a single column.
Your best option is instead of just doing
Select * from rawdata
as your source, include a column that pre-adds maintenance and downtime together, like
select *, maintenance + downtime as TotalTime from rawdata
and then aggregate it.

Related

How to keep column name after sum aggregation in SQL query

I am querying SQL Server where I have multiple columns with sum aggregation. I'm wondering if there is a way to keep column name after doing sum over each of them.
I want to avoid using aliases immediately after the selected column as their numbers and names change over time.
Example of my result:
My query:
DECLARE #SQLQUERY AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
DECLARE #SumColumns as NVARCHAR(MAX)
SELECT #PivotColumns = COALESCE(#PivotColumns + ',','') + QUOTENAME(description)
FROM [example-base].dbo.table
SELECT #SumColumns = 'sum('+Replace(#PivotColumns,',','),sum(')+')'
SET #SQLQUERY = N'SELECT TOP(100) Weeks, Years, Code,'+#SumColumns+ '
FROM [example-base].dbo.table
group by Weeks,Years,Code'
EXECUTE sp_executesql #SQLQUERY
Is there a way to keep column name on which is done sum?
Be aware of the potential performance issues by building a query with text. Moreover, your result may be not predictable while the number of columns can evolve between two executions.
To add an alias to your columns, you may use this statement :
DECLARE #SumColumns AS nvarchar(MAX);
SELECT #SumColumns = CONCAT(COALESCE(#PivotColumns + ',', ''), 'SUM(', QUOTENAME(description), ') AS ', QUOTENAME(description))
FROM [example-base].dbo.table
...a union..?
SET #SQLQUERY = N'
SELECT TOP(0) Weeks, Years, Code,'+#PivotColumns+ '
FROM [example-base].dbo.table
UNION ALL
SELECT TOP(100) Weeks, Years, Code,'+#SumColumns+ '
FROM [example-base].dbo.table
group by Weeks,Years,Code'

change column names in a dynamic pivot table result

I have a tsql dynamic pivot table query that works fine although I´m not happy with the column names in the final output.
To make it work, the user has to select up to 20 fruit names from a list of 200 fruit names. Then I build the pivot table so I end up with different column names everytime the select is run.
For example:
First time the column names are: apple, orange and pear
Second time is: .orange, banana, kiwi and apple
My question is: Is there a posibility to have static names, like for example: name of the first column always "col_1", second column "col_2" etc?
The select statement is as follows:
DECLARE #idList varchar(800)
DECLARE #sql nvarchar(max)
SELECT #idList = coalesce(#idList + ', ', '') + '['+ltrim(rtrim(id_producto)) +']'
from gestor_val_pos
group by id_producto order by id_producto
SELECT #sql = 'select * from #correlaciones pivot (max (correl)
for codigo2 in (' + #IDlist + ')) AS pvt order by codigo1;'
exec sp_executeSQL #sql
sure.. just created a new variable to hold the column aliases and Row_Number to get the column number.
DECLARE #idList varchar(800)
DECLARE #idListAlias varchar(800)
DECLARE #sql nvarchar(max)
SELECT
#idList = coalesce(#idList + ', ', '') + '['+ltrim(rtrim(id_producto)) +']',
#idListAlias = coalesce(#idListAlias + ', ', '') + '['+ltrim(rtrim(id_producto)) +'] as col_' + CONVERT(VARCHAR(10), ROW_NUMBER() OVER(ORDER BY id_producto))
from gestor_val_pos
group by id_producto order by id_producto
SELECT #sql = 'select ' + #idListAlias + ' from #correlaciones pivot (max (correl)
for codigo2 in (' + #IDlist + ')) AS pvt order by codigo1;'
exec sp_executeSQL #sql
Yes, but it'll make your query significantly more complex.
You'll need to return a list of the possible column names generated from #IDList and convert that into a SELECT clause more sophisticated than your current SELECT *.
When you've got that, use some SQL string splitting code to convert the #IDList into a table of items with a position parameter. Append AS <whatever> onto the end of any you want and use the FOR XML PATH trick to flatten it back, and you've got a SELECT clause that'll do what you want. But, as I said, your code is now significantly more complicated.
As an aside - I really hope that #idList is either completely impossible for any user input to ever reach or hugely simplified from your real code for this demonstration. Otherwise you've got a big SQL injection hole right there.

Transpose row results to single row

Perhaps my example is too simple but I need to transpose the results from being multiple rows to being multiple columns in a single row. The only issue that the number of returned initial rows may vary so therefore my final number of columns may vary also.
As an example, my returned results from
select name from pets
could be:
Dog
Cat
Fish
Rabbit
And I need each value in a separate column:
Dog Cat Fish Rabbit
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX)
-- First create list of columns that you need in end result
SET #columns = N''
SELECT #columns += N', ' + QUOTENAME(name)
FROM (select distinct name from pets) AS x
-- now create pivot statement as:
SET #sql = N'
SELECT ' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT name
FROM pets
) AS j
PIVOT
(
max(name) FOR name IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;'
EXEC sp_executesql #sql;
DEMO

Table Transform with SQL

Normally when I deal with SQL (rarely) I only use SELECT * FROM statement all the time. My primary job is working with Excel as an analysis. However, I feel my efficiency can be much improved with programming, I have started to learn some programming (VBA for Excel). Today I want to do something more advantage which is trying to transform a table using Microsoft SQL like attached picture below.
SELECT Part_Number as [Part Number], SubPart, Quantity FROM....
Bascially, the Part Number can be as many as 200, SubPart only has 3 type Sub-I, Sub-II, and Sub-III, Quantity can be anything. I need some help to transform the table as shown
Write dynamic T-Sql query as:
DECLARE #columns NVARCHAR(MAX)
,#sql NVARCHAR(MAX)
SET #columns = N''
--Get column names for entire pivoting
SELECT #columns += N', ' + QUOTENAME(SpreadCol)
FROM (select distinct Part_Number as SpreadCol
from test
) AS T
--select #columns
SET #sql = N'
SELECT SubPart, ' + STUFF(#columns, 1, 2, '') + '
FROM
(select SubPart , Part_Number as SpreadCol , Quantity
from test ) as D
PIVOT
(
sum(Quantity) FOR SpreadCol IN ('
+ STUFF(REPLACE(#columns, ', [', ',['), 1, 1, '')
+ ')
) AS Pivot1
'
--select #sql
EXEC sp_executesql #sql
Check Fiddle Demo here..
It seems you need to use Pivoting.
Refer to:
Using PIVOT and UNPIVOT
Simple Way To Use Pivot In SQL Query

Including default values on dynamic pivot

I am pivoting data in a table based on date, but need to return NULL for dates with no data.
Data in [dbo].[Metrics]:
The dynamic pivot SQL I am executing:
DECLARE #cols NVARCHAR(1000);
SELECT #cols =
STUFF((SELECT N'],[' + month_day
FROM (SELECT DISTINCT RIGHT(CONVERT(NCHAR(10), [Date], 126), 2)
FROM [Metrics]) AS O(month_day)
ORDER BY month_day
FOR XML PATH('')
), 1, 2, '') + N']';
DECLARE #sql NVARCHAR(2000);
SET #sql =
N'SELECT [Key], ' + #cols +
N' FROM (SELECT [Key], ' +
N' RIGHT(CONVERT(NCHAR(10), [Date], 126), 2) AS month_day, ' +
N' [Value] ' +
N' FROM [Metrics]) AS O ' +
N'PIVOT ' +
N'(SUM([Value]) FOR month_day IN (' + #cols + N')) AS P;';
EXECUTE(#sql);
...and the result set that dynamic SQL returns:
As you can see, since I do not have data for every day of the month it is simply not returning columns for those days. How can I have it return columns 10-23 and 28-31 with NULL for each cell? It should return columns 1-31 regardless of the actual month (e.g., 31 columns even when there are less than 31 days in a given month).
Any help is appreciated.
Then you don't really need that columns to be generated dynamically, since you always want them from 1 to 31. The easier way would be hardcoding those values on your pivot, really. Anyway, here is the way you can define your columns dynamically and still get all days:
SELECT #cols =
STUFF((SELECT N'],[' + CAST(number AS VARCHAR(2))
FROM (SELECT DISTINCT number
FROM master..[spt_values]
WHERE number BETWEEN 1 AND 31) AS O(number)
ORDER BY number
FOR XML PATH('')
), 1, 2, '') + N']';
And the version you really should be using is this:
SELECT *
FROM (SELECT [Key],
RIGHT(CONVERT(NCHAR(10), [Date], 126), 2) AS month_day,
[Value]
FROM [Metrics]) AS O
PIVOT (SUM([Value]) FOR month_day IN ([01],[02],[03],[04],[05],[06],[07],[08],[09],
[10],[11],[12],[13],[14],[15],[16],[17],[18],
[19],[20],[21],[22],[23],[24],[25],[26],[27],
[28],[29],[30],[31])) AS P;