Adding ISNULL into a small dynamic header script - sql

We are using this small recursive script to create a string for the dynamic headers of a table - the variable feeds into a piece of dynamic sql.
DROP TABLE IF EXISTS #PivotStep1;
CREATE TABLE #PivotStep1(PlayMonth VARCHAR(25))
INSERT INTO #PivotStep1
values
('01-oct-2016'),
('01-nov-2016'),
('01-dec-2016'),
('01-jan-2017'),
('01-feb-2017');
DECLARE #ColumnName AS nvarchar(MAX)
SELECT #ColumnName = ISNULL(#ColumnName + ',','')
+ QUOTENAME(PlayMonth)
FROM (
SELECT DISTINCT PlayMonth
FROM #PivotStep1
) AS ps
SELECT #ColumnName;
The above gives the following:
[01-dec-2016],[01-feb-2017],[01-jan-2017],[01-nov-2016],[01-oct-2016]
What we want is to surround each column name with ISNULL and AS... So the desired output is the following:
ISNULL([01-dec-2016],0) AS [01-dec-2016],ISNULL([01-feb-2017],0) AS [01-feb-2017],ISNULL([01-jan-2017],0) AS [01-jan-2017],ISNULL([01-nov-2016],0) AS [01-nov-2016],ISNULL([01-oct-2016],0) AS [01-oct-2016]
We would like to keep using the above recursive approach rather than using STUFF and XML.

Use the quotename twice and do other required concatenation.
Try this:
select #ColumnName = ISNULL(#ColumnName + ',', '')
+ 'ISNULL(' + QUOTENAME(PlayMonth) + ', 0) as ' + QUOTENAME(PlayMonth)
from (
select distinct PlayMonth
from #PivotStep1
) as ps
select #ColumnName;
Demo

Related

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

Dynamic Pivot in MS SQL Server

I am trying to do a dynamic pivot on the last two columns that i take from one table and am joining onto the contents of another table. I need the Name values to pivot to the header fields and the Value values to fill in correspondingly underneath. This is my current query:
USE Innovate
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(NAME)
FROM (SELECT DISTINCT NAME FROM Innovate.dbo.Table1 WHERE Name IS NOT NULL) AS ATTRIBUTE_NAME
WHERE Name LIKE 'Suture_-_2nd_Needle_Code'
OR Name LIKE 'Suture_-_Absorbable'
OR Name LIKE 'Suture_-_Antibacterial'
OR Name LIKE 'Suture_-_Armed'
OR Name LIKE 'Suture_-_Barbed'
OR Name LIKE 'Suture_-_Brand_Name'
OR Name LIKE 'Suture_-_C/R_2nd_Needle_Code'
OR Name LIKE 'Suture_-_C/R_Brand_Name'
OR Name LIKE 'Suture_-_C/R_length'
OR Name LIKE 'Suture_-_C/R_Needle_Code'
OR Name LIKE 'Suture_-_Coating'
OR Name LIKE 'Suture_-_Dyed'
OR Name LIKE 'Suture_-_Filament'
OR Name LIKE 'Suture_-_length_inches'
OR Name LIKE 'Suture_-_Looped'
OR Name LIKE 'Suture_-_Material'
OR Name LIKE 'Suture_-_Needle_Code'
OR Name LIKE 'Suture_-_Needle_Shape'
OR Name LIKE 'Suture_-_Needle_Style'
OR Name LIKE 'Suture_-_Noun'
OR Name LIKE 'Suture_-_pleget'
OR Name LIKE 'Suture_-_Popoff'
OR Name LIKE 'Suture_-_Suture_count'
OR Name LIKE 'Suture_-_Suture_size'
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT Table1.Primary_Key, Company_Name, Part_Number, Product_Desc, Innovate_Description, ' + #ColumnName + '
FROM Table1 AS P
LEFT JOIN Table2 AS A ON P.Primary_Key = A.Primary_Key
PIVOT(MAX(A.VALUE)
FOR A.NAME IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXECUTE sp_executesql #DynamicPivotQuery
;
And this is the Result for the Query that I keep getting: Msg 8156,
Level 16, State 1, Line 5 The column 'Primary_Key' was specified
multiple times for 'PVTTable'. Msg 4104, Level 16, State 1, Line 1 The
multi-part identifier "Table1.Primary_Key" could not be bound.
Can anyone help me pivot these columns without the error message? I only specified the Primary_Key in the code once so I do not know how I specified it multiple times and how it is unbound.
Try with the below script..
SET #DynamicPivotQuery =
N'SELECT P.Primary_Key, Company_Name, Part_Number, Product_Desc, Innovate_Description, ' + #ColumnName + '
FROM Table1 AS P
LEFT JOIN Table2 AS A ON P.Primary_Key = A.Primary_Key
PIVOT(MAX(A.VALUE)
FOR A.NAME IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXECUTE sp_executesql #DynamicPivotQuery
you have a couple of things going on with your PIVOT statement that are problematic. First you are attempting to reference table aliases for Table1 and Table2 but those aliases are not available in the final select of a PIVOT the Pivot command is kind of like an outer select and the only table alias that is then available is the pivot alias.
Next pivot's documenation states "You can use the PIVOT and UNPIVOT relational operators to change a table-valued expression into another table" (https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx). Basically that means that you need a single table expression of only the uniquely named columns you want to be involved pivot being passed to the PIVOT command. The later part is likely the issue as Table1 and Table2 probably both have a column Primary_Key so pivot doesn't understand the reference.
To fix you can either move your Table1 & 2. join to an inner select and alias the table or build a cte and use the cte in your command. Here is the former way:
SET #DynamicPivotQuery =
N'SELECT * FROM
(
SELECT Table1.Primary_Key, Company_Name, Part_Number, Product_Desc, Innovate_Description, ' + #ColumnName + '
FROM Table1 AS P
LEFT JOIN Table2 AS A ON P.Primary_Key = A.Primary_Key
) t
PIVOT(MAX(A.VALUE)
FOR NAME IN (' + #ColumnName + ')) AS PVTTable'
Those Name values get hardcoded to calculate that #ColumnName variable.
If the values are hardcoded anyway, then you might as well run the pivot with a join without building a SQL statement to execute.
SELECT A.Company_Name, A.Part_Number, A.Product_Desc, A.Innovate_Description, P.*
FROM (select Primary_Key, Name, Value from Innovate.dbo.Table1) T1
PIVOT(MAX(VALUE) FOR NAME IN (
[Suture_-_2nd_Needle_Code],
[Suture_-_Absorbable],
[Suture_-_Antibacterial],
[Suture_-_Armed],
[Suture_-_Barbed],
[Suture_-_Brand_Name],
[Suture_-_C/R_2nd_Needle_Code],
[Suture_-_C/R_Brand_Name],
[Suture_-_C/R_length],
[Suture_-_C/R_Needle_Code],
[Suture_-_Coating],
[Suture_-_Dyed],
[Suture_-_Filament],
[Suture_-_length_inches],
[Suture_-_Looped],
[Suture_-_Material],
[Suture_-_Needle_Code],
[Suture_-_Needle_Shape],
[Suture_-_Needle_Style],
[Suture_-_Noun],
[Suture_-_pleget],
[Suture_-_Popoff],
[Suture_-_Suture_count],
[Suture_-_Suture_size]
)
) P
LEFT JOIN Innovate.dbo.Table2 AS A ON (A.Primary_Key = P.Primary_Key);
Fair enough, this has a disadvantage that if that [Primary_Key] needs to be the first column, that the P.* should be replaced by those literal column values. Or use the EXEC approach after all.
Anyway, to build name list for that #ColumnName variable, it can be done without all the OR's :
DECLARE #ColumnName NVARCHAR(MAX);
--Get distinct values of the PIVOT Column
SELECT #ColumnName = ISNULL(#ColumnName + ',','') + QUOTENAME(NAME)
FROM Innovate.dbo.Table1
WHERE Name Like 'Suture_-_%'
AND SUBSTRING(Name,10,30) IN (
'2nd_Needle_Code',
'Absorbable',
'Antibacterial',
'Armed',
'Barbed',
'Brand_Name',
'C/R_2nd_Needle_Code',
'C/R_Brand_Name',
'C/R_length',
'C/R_Needle_Code',
'Coating',
'Dyed',
'Filament',
'length_inches',
'Looped',
'Material',
'Needle_Code',
'Needle_Shape',
'Needle_Style',
'Noun',
'pleget',
'Popoff',
'Suture_count',
'Suture_size')
GROUP BY Name;

Assistance with a SQL query parsing JSON

I have a database table called QueueItems that contains the following fields:
SpecificData contains JSON data so we have used the well known parseJSON SQL function that is about on the internet.
So an example of the JSON is below:
{"DynamicProperties":{"IdentificationIndex":"CK","PaymentMethod":"C","Variants1":"010 ZERO BAL,716 ZERO BAL,717 ZERO BAL","Variants2":"CHECK_010,CHECK_716,CHECK_717","CustomerCode":"NO","FreeSelectionField":"NO","FreeSelectionValue":"NO","Variants1Line":"RFFOAVIS","Variants2Line":"ZRFFOUS_C","VendorFrom":"1","VendorTo":"999999999","DueDateCheck":"Yes","PaymentMethodSel":"Yes","PaymentMethodSelOnFail":"No","LineItemsOfPayDocs":"Yes","StartImmediately":"Yes","CreatePaymentMedium":"No","ExportFormat":"HTML Format","ExcludeValues":"Yes"}}
When I run the SQL function parseJSON it returns me the data in this format:
Now, the query I am trying to write is:
SELECT QueueItemID, QueueItemStatus, StartProcessing, EndProcessing, [THEN append each column from parseJSON but this needs to be transposed first]
So far I have managed to transpose the JSON into columns with a single row using:
DECLARE
#Cols AS VARCHAR(MAX) = ''
,#Query AS NVARCHAR(MAX) = ''
,#ParamDef AS NVARCHAR(MAX)
,#Json AS VARCHAR(MAX) = '{"DynamicProperties":{"IdentificationIndex":"CK","PaymentMethod":"C","Variants1":"010 ZERO BAL,716 ZERO BAL,717 ZERO BAL","Variants2":"CHECK_010,CHECK_716,CHECK_717","CustomerCode":"NO","FreeSelectionField":"NO","FreeSelectionValue":"NO","Variants1Line":"RFFOAVIS","Variants2Line":"ZRFFOUS_C","VendorFrom":"1","VendorTo":"999999999","DueDateCheck":"Yes","PaymentMethodSel":"Yes","PaymentMethodSelOnFail":"No","LineItemsOfPayDocs":"Yes","StartImmediately":"Yes","CreatePaymentMedium":"No","ExportFormat":"HTML Format","ExcludeValues":"Yes"}}'
SELECT
#Cols += ',' + Name
FROM
parseJson(#Json)
WHERE
Name NOT IN ('DynamicProperties', '-')
SET #Cols = SUBSTRING(#Cols,2,LEN(#Cols))
SET #Query = N'SELECT *
FROM
(
SELECT [StringValue], [Name]
FROM parseJson(''' + #Json + ''')
) [d]
PIVOT
(
MAX([StringValue])
FOR [Name] IN (' + #Cols + ')
) [piv]'
EXECUTE (#Query)
As you can see the JSON may contain ANY data, so I am dynamically inserting the column names into the PIVOT statement FOR IN.
Now to Join this data as additional columns onto my main query SELECT QueueItemId.. FROM QueueItems I was originally going to put this code in a UDF and call it as part of my main query / stored procedure but I have two issues:
1) The sp_executesql or EXEC statement - not allowed in a UDF
2) To return a table from a UDF I need to define the fields... The fields are dynamic... I could get this working by passing back XML to my stored procedure but I still have issue number 1.
So the question is:
1) Is there any better way or writing this then using a UDF?
2) Is there any other way or transposing the output from parseJSON?
3) Is there any other way of using the PIVOT but not having to specify the columns?
Any help would be much appreciated.
EDIT IN RESPONSE TO M ALI
I know the syntax is wrong and it doesn't work but the query would look something like this:
DECLARE #Cols AS VARCHAR(MAX) = ''
,#Query AS NVARCHAR(MAX) = ''
SELECT
QI.QueueItemID,
QI.QueueItemStatus,
QI.StartProcessing,
QI.EndProcessing,
SD.*
FROM QueueItems AS QI
LEFT JOIN
(
SELECT
#Cols += ',' + Name
FROM
parseJson(QI.SpecificData)
WHERE
Name NOT IN ('DynamicProperties', '-')
SET #Cols = SUBSTRING(#Cols,2,LEN(#Cols))
SET #Query = N'SELECT *
FROM
(
SELECT [StringValue], [Name]
FROM parseJson(''' + QI.SpecificData + ''')
) [d]
PIVOT
(
MAX([StringValue])
FOR [Name] IN (' + #Cols + ')
) [piv]'
EXECUTE (#Query)
) AS SD
And the results would be the columns from QueueItems, joined on the left by columns from the parsed JSON string.
QueueItemID, StartProcessing, EndProcessing, IndentificationIndex, PaymentMethod etc...

Dynamic Pivot - SQL Server

I have a test SQL database the following query:
USE DataBase1
Select Data.MonthDate,
Data.AccountID,
Data.MonthID,
Data.Sales,
Data.AccountName
From Test1 as Data with(nolock)
That I need to pivot based off of the sales column. The problem is the months when I run this query will always change (though there will always be 4 of them) and they need to be ordered left-to-right/oldest-newest in the pivoted result based off of the MonthDate column. The initial return when the query is run looks like this:
And the final result needs to look like this:
I'm using Excel here to demonstrate and I highlighted the 0's because those are technically NULL values but I need them to come back as 0.
I'm using SQL Server Management Studio and the actual database I'll be running this against is over 200,000 rows.
Any thoughts?
Thanks,
Joshua
Use Dynamic Query.
DECLARE #col_list VARCHAR(max)='',
#sel_list VARCHAR(max)='',
#sql NVARCHAR(max)
SELECT DISTINCT #col_list += '[' + Isnull(MonthID, '') + '],'
FROM Test1
ORDER BY MonthID
SELECT #col_list = LEFT(#col_list, Len(#col_list) - 1)
SELECT DISTINCT #sel_list += 'Isnull([' + Isnull(MonthID, '') + '],0) ' + '['+ MonthID + '],'
FROM Test1
ORDER BY MonthID
SELECT #sel_list = LEFT(#sel_list, Len(#sel_list) - 1)
SET #sql ='select Data.AccountID,Data.AccountName,'+ #sel_list+ ' from (
Select
Data.AccountID,
Data.MonthID,
Data.Sales,
Data.AccountName
From Test1 as Data ) A
pivot (sum(Sales) for monthid in('+ #col_list + ')) piv'
--PRINT #sql
EXEC Sp_executesql #sql
Basically you need to dynamically build the PIVOT query and use sp_exec to run it.
SQL Server, out of the box, has no support for dynamic ever-changing columns as the columns need to be defined in the PIVOT query.
Here's an example of how to accomplish this: http://sqlhints.com/tag/dynamic-pivot-column-names/

SQL Pivot Table

Hey guys I have tried to convert this query into a pivot table and I have searched everywhere but can't seem to apply it. The following is my code.
SELECT ClientPartner, ClientManager, WIPAmount
FROM tblTranWIP
WHERE (WIPDate>={ts '2013-04-01 00:00:00'}
AND WIPDate<{ts '2014-03-31 00:00:01'})AND TransTypeIndex=1 AND ContIndex<900000
The following screenshot shows my output. I basically want to have Managers and Partners as the First Column and Row and then have WipAmount be in the middle as ClientPartners and Managers match id's The screen shot shows it easier than I explain. The link is a screenshot, the system won't let me add embed images
There is at least one way I know of to get the information you are looking for, and that is to use a PIVOT:
SELECT pvt.*
FROM
(
SELECT ClientPartner, ClientManager, WIPAmount FROM tblTranWIP
) AS tranwip
PIVOT
(
SUM(WIPAmount) FOR ClientPartner IN ([46], [58], [177], [207])
) AS pvt
Here is a SQL Fiddle.
this was a great help, upon reading many tutorials. I came up with the following code for what I needed, which was a dynamic pivot as I don't know every single column code.
DECLARE #sql varchar(MAX)
DECLARE #columns TABLE (COL varchar(50))
declare #columnscsv varchar(MAX)
insert into #columns
select distinct ClientPartner from tblTranWip
select #columnscsv = COALESCE(#columnscsv + '],[','') + COL from #columns
set #columnscsv = '[' + #columnscsv + ']'
SET #sql = 'Select ClientManager ' + #columnscsv + ' FROM(SELECT ClientPartner, ClientManager, WipAmount FROM tblTranWip) a
PIVOT (MAX(WipAmount) for ClientPartner in (' + #columnscsv + ')) AS PVT ORDER by ClientManager'
EXEC (#sql)