Dynamic pivot in SQL Server not working as expected - sql

I have the following table:
enter image description here
I'm doing a dynamic PIVOT in SQL_Server and I got the following query:
declare #colunas_pivot as nvarchar(max), #comando_sql as nvarchar(max)
set #colunas_pivot =
stuff((
select
distinct ',' + quotename(datename(year,PLD_Date) + '' + datename(month, PLD_Date))
from TB_Planned
/* where PLD_Date > getdate() */
order by 1
for xml path('')
), 1, 1, '')
print #colunas_pivot
set #comando_sql = '
SELECT * FROM (
SELECT
[PLD_ProjectSapCode],
[PLD_Date],
[PLD_Value]
FROM TB_Planned
) result_pivot
pivot (max(PLD_Value) for PLD_Date in (' + #colunas_pivot + ')) result_pivot
'
print #comando_sql
execute(#comando_sql)
This query results in the following table:
enter image description here
As you can see, I want to PIVOT the column "PLD_Date" and group my columns by month/year, and put the sum of the values ​​corresponding to each month, from the column "PLD_Value.
However, in this result above it is only returning the value of the first day of each month.
How would I group and correctly add the values ​​of the entire month?

Related

Convert Rows to Columns SQL

I am trying to convert rows to columns in SQL server. I am trying to convert the value's a product gets while being tested during quality. I have tried the pivot function but having trouble doing so as the same values do get repeated and it can not be easily sorted into rows. The table I am trying to pivot holds ~30K data row's so hoping to find a dynamic solution for this.
The maximum number of new columns is 30 but sometimes a product doesn't get tested as much so it can be less. The new column would be based off my inspection_unit_number field. Is this possible to achieve in SQL
Current data
What I hope to achieve
Current Attempt
SELECT BATCH , characteristic, [1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30]
from
(
select inspection_lot ,node_number ,characteristic ,inspector ,inspection_unit_number ,start_date ,measured_value ,original_value ,material_no ,batch
from stg.IQC_Tensile_TF
) d
pivot
(
max(measured_value)
for
INSPECTION_UNIT_NUMBER in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30])
) piv;
You will have to go for a dynamic query, check if this will suit your needs.
I created a common table expression to be able to use distinct and then order by in the stuff function:
DECLARE #QUERY NVARCHAR(MAX)
DECLARE #Columns NVARCHAR(MAX)
WITH cte_unique_inspection_unit_number AS
(
SELECT DISTINCT QUOTENAME('TestResults' + CAST(inspection_unit_number AS VARCHAR)) TestResultsN,
inspection_unit_number
FROM IQC_Tensile_TF
)
SELECT #Columns = STUFF((SELECT ', ' + TestResultsN
FROM cte_unique_inspection_unit_number
ORDER BY inspection_unit_number
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,2,''),
#query = 'SELECT batch, node_number, characteristic, ' + #Columns + ' from
(
select batch,
node_number,
characteristic,
measured_value,
''TestResults'' + CAST(inspection_unit_number AS VARCHAR) TestResultsN
from IQC_Tensile_TF
) x
pivot
(
max(measured_value)
for TestResultsN in (' + #Columns + ')
) p '
EXEC(#query)
To view the execution in fiddle:
https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=7898422e4422faacb25d7f3c2285f14a
If you find my answer useful, i would appreciate if you vote up and mark as accepted =D

SQL Pivot - take a changing date from column and make it a row header

I have a basic query that looks like this.
SELECT Database_Name,
FilingDate,
SUM(ISNULL([column1], 0) + ISNULL(column2], 0) +
ISNULL([column3], 0) + ISNULL([column4], 0)) AS Total
FROM SomeTable(NOLOCK)
GROUP BY Database_Name,
FilingDate
ORDER BY Database_Name,
FilingDate DESC
This query outputs results that look like this.
I would like to take the dates returned in the FilingDate column and use them as new column headers with the totals for each database and date being used as the row content. The end result should look like this:
My research suggests that a pivot is the best option but I'm struggling to find the right way to execute it as my dates change each day Any assistance would be appreciated.
If this is MS SQL, you can use a dynamic pivot table. Here is a solution using your query (should work, but I don't have the base data to test it).
SELECT Database_Name,
FilingDate,
SUM( ISNULL(column1 ,0) +
ISNULL(column2],0) +
ISNULL([column3],0) +
ISNULL([column4],0)
) AS Total
INTO #T1
FROM SomeTable(NOLOCK)
GROUP BY Database_Name,
FilingDate
DECLARE #PivotColumnHeaders varchar(MAX)
SELECT #PivotColumnHeaders =
COALESCE(
#PivotColumnHeaders + ',[' + CAST(UC.FilingDate AS NVARCHAR(10)) + ']',
'[' + CAST(UC.FilingDate AS NVARCHAR(10)) + ']'
)
FROM (SELECT FilingDate FROM #T1 GROUP BY FilingDate) UC
DECLARE #PQuery varchar(MAX) = '
SELECT * FROM (SELECT Database_Name, FilingDate, Total FROM #T1 T0) T1
PIVOT (SUM([Total]) FOR FilingDate IN (' + #PivotColumnHeaders + ') ) AS P'
EXECUTE (#PQuery)
DROP TABLE #T1

SQL Server SORT Dynamic Pivot Column Names

I have an dynamic SQL query which is returning the result as desired but the only problem is the resultant pivot columns are not getting selected in correct order.
I am using SQL Server 2012.
Here is the query:
DECLARE #columns NVARCHAR(MAX), #columns_pivot NVARCHAR(MAX), #sql NVARCHAR(MAX);
SELECT #columns_pivot = COALESCE(#columns_pivot + ', ', '') + QUOTENAME(Week_No)
,#columns = COALESCE(#columns + ', ', '') + 'ISNULL(' + QUOTENAME(Week_No) + ',0) AS ' + QUOTENAME(Week_No) + ''
FROM (SELECT DISTINCT TOP 100 PERCENT DATEPART(wk,T_Date) As Week_No
FROM [VISIT].[dbo].[Report]
WHERE DATEPART(m,T_Date) = 5
ORDER BY Week_No DESC) x; // This query returns Column 'Week_No' in order 22,21,20,19,18 (As Desired)
Edit: //But when Order By Clause is removed it returns 21,18,19,22,20 which is the actual output( Not Desired)
SET #sql = '
SELECT ABC, ' + #columns + '
FROM
(Select TOP 100 PERCENT
ABC
,SUM(CASE WHEN Type = A THEN Sum ELSE 0 END) AS Revenue
,DATEPART(wk,T_Date) As Week_No
FROM [VISIT].[dbo].[Report]
GROUP BY ABC
,DATEPART(wk,T_Date)
ORDER BY Week_No DESC) As j
PIVOT(
max(Revenue)
FOR Week_No in (' + #columns_pivot + ')) As p '
Final result returns columns in the order ABC, 21, 18, 19, 22, 20
But I want the result As ABC, 22, 21, 20, 19, 18.
I saw few posts but was not able to figure out what is wrong with my query. Can someone please point what I have to change in the Query to get the desired output. Where exactly do I have to put the ORDER BY clause
In short when the Select Distinct statement returns the column Week_No in Desc Order why the variables #columns and #columns_pivot don't get it in Desc order
Thanks.
I have solved the issue by storing Week_No in a temp table and apply clustered indexing to it.
Here is the code I added:
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
SET NOCOUNT ON;
DECLARE #params nvarchar(max) = :month
SELECT DISTINCT TOP 100 PERCENT DATEPART(wk,Tour_Datum) As Week_No into #temp
FROM [VISITOUR].[dbo].[BI_001_ADM_Report]
WHERE DATEPART(m,Tour_Datum) = #params
--Clustered index is created on week_no in acending order, so that data is physically stored in that way
CREATE CLUSTERED INDEX cix_wekno ON #temp(Week_No ASC)

Convert a row as column and merge two column as its value

I have stuck in a select statement, converting rows into columns. I have tried with PIVOT, i was able to convert the single column. But my requirement is little different. I have explained the requirement below.
I have a table structure as below,
I want to select the data as below,
The values in the table are dynamic, which is not a problem for me to deal with that. But i need a way to get the below result.
Could someone please give me a hint on doing it, may be a way to modify the PIVOT below.
select *
from
(
select TSID,AID,Count,BID
from tbl TS
WHERE TS.TPID = 1
) src
pivot
(
sum(Count)
for AID in (AID1,AID2,AID3)
) piv
Thank you..
You may check this fiddle
EDIT
This will work for not previously known column names
DECLARE #Columns AS VARCHAR(MAX)
DECLARE #SQL AS VARCHAR(MAX)
SELECT #Columns = STUFF(( SELECT DISTINCT ',' + AID
FROM Table1
FOR
XML PATH('')
), 1, 1, '')
SET #SQL = '
;WITH MyCTE AS
(
SELECT TSID,
AID,
STUFF(( SELECT '','' + CONVERT(VARCHAR,[Count] )
FROM Table1 I Where I.TSID = O.TSID
FOR
XML PATH('''')
), 1, 1, '''') AS CountList
FROM Table1 O
GROUP BY TSID,
AID
)
SELECT *
FROM MyCTE
PIVOT
(
MAX(CountList)
FOR AID IN
(
' + #Columns + '
)
) AS PivotTable'
EXEC(#SQL)

change the column name while selecting from dyanamic table

Hi I have attendence query which will generate the attendence report with using PIVOT function
Here's the procedure :
declare #in_date DATETIME
/*Select all the stagign entries related to promotion id and investment type id */
/* also only those staging daat related interface status tracking*/
-- Getting all distinct dates into a temporary table #Dates
SELECT a.date as full_date_of_attendence INTO #Dates
FROM dbo.getFullmonth(#in_date) a
ORDER BY a.date
-- The number of days will be dynamic. So building
-- a comma seperated value string from the dates in #Dates
SELECT #cols = COALESCE(#cols + ',[' + CONVERT(varchar, full_date_of_attendence, 106)
+ ']','[' + CONVERT(varchar, full_date_of_attendence, 106) + ']')
FROM #Dates
ORDER BY full_date_of_attendence
--select #cols
---- Building the query with dynamic dates
SET #qry =
'SELECT * FROM
(SELECT admission_id, attendence_status , date_of_attendence
FROM dbo.tblattendence)emp
PIVOT (MAX(attendence_status) FOR date_of_attendence IN (' + #cols + ')) AS stat'
-- Executing the query
EXEC(#qry)
-- Dropping temporary tables
DROP TABLE #Dates
here is the output of the above query::
admission_id 01 May 2013 02 May 2013 03 May 2013
2 NULL 1 0
3 NULL 1 1
4 NULL 0 0
5 NULL 0 1
Here I want to change the names of the columns as 01,02,03......
and I want the values 1 as 'P' and 0 as 'A'
can anyone would help me to achieve this ??
I would suggest the following changes to your code. If you want a list of the days (1, 2, 3, etc), then you can use the DAY function.
Typically when I get a list of columns dynamically, my preference is using STUFF and FOR XML PATH, I would alter that code to the following:
select #colsPiv = STUFF((SELECT ',' + QUOTENAME(cast(day(full_date_of_attendence) as varchar(2)))
from #Dates
GROUP BY full_date_of_attendence
ORDER BY full_date_of_attendence
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
Then if you want to replace the 0 with an A and a 1 with a P, you will want to create a query to get a list of columns to replace the values:
select #colsSel = STUFF((SELECT ', case when ' + QUOTENAME(cast(day(full_date_of_attendence) as varchar(2)))+'= 1 then ''P'' else ''A'' end as '+QUOTENAME(cast(day(full_date_of_attendence) as varchar(2)))
from #Dates
GROUP BY full_date_of_attendence
ORDER BY full_date_of_attendence
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
Basically, this is creating a select list similar to this:
select case when [1] = 1 then 'P' else 'A' end as [1], ...
Then your final query will be:
SET #qry =
'SELECT admission_id, '+#colsSel +'
FROM
(
SELECT admission_id,
attendence_status ,
day(date_of_attendence) date_of_attendence
FROM dbo.tblattendence
)emp
PIVOT
(
MAX(attendence_status)
FOR date_of_attendence IN (' + #colsPiv + ')
) AS stat'
See SQL Fiddle with Demo
Let's change just the two things you wanted to, i.e.
CONVERT(CHAR(2), full_date_of_attendence, 106) -- use CHAR(2) instead of varchar
CASE attendence_status when 1 then 'P' else 'A' END in the SELECT...
The code with minimal changes. Hope this helps you see how you can make similar changes in future to other code.
declare #in_date DATETIME
/*Select all the stagign entries related to promotion id and investment type id */
/* also only those staging daat related interface status tracking*/
-- Getting all distinct dates into a temporary table #Dates
SELECT a.date as full_date_of_attendence INTO #Dates
FROM dbo.getFullmonth(#in_date) a
ORDER BY a.date
-- The number of days will be dynamic. So building
-- a comma seperated value string from the dates in #Dates
SELECT #cols = COALESCE(#cols + ',', '') + [' +
CONVERT(CHAR(2), full_date_of_attendence, 106) + ']'
FROM #Dates
ORDER BY full_date_of_attendence
--select #cols
---- Building the query with dynamic dates
SET #qry =
'SELECT * FROM
(SELECT admission_id, CASE attendence_status when 1 then 'P' else 'A' END, date_of_attendence
FROM dbo.tblattendence)emp
PIVOT (MAX(attendence_status) FOR date_of_attendence IN (' + #cols + ')) AS stat'
-- Executing the query
EXEC(#qry)
-- Dropping temporary tables
DROP TABLE #Dates