how to convert Row to column dynamically in sql server - sql

Here is my tables structure
Result
id ResultId CategoryId Total Attempted Score
----------------------------------------------------
1 8 1 30 25 20
2 8 2 30 30 19
3 8 3 30 27 21
4 7 1 20 15 10
5 7 2 20 20 15
Category
Id CategoryName
-----------------------
1 General
2 Aptitude
3 Technical
I want data in the below format
For ResultId = 8
Id General Aptitude Technical Total
--------------------------------------------------
8 20 19 21 60
For ResultId = 7
Id General Aptitude Total
-------------------------------------
7 10 15 25
I need a help to fetch the data in above format.
NOTE: The final fetched data contains score from Result table but having a column names from category table and according to CategoryId in Result table. Column name will be dynamic
Tried the below code (just for testing) but didn't work
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME(CategoryName)
FROM ( SELECT DISTINCT CategoryName
FROM [dbo].[Category] c) AS PivotExample
SET #SQLQuery =
N'SELECT DISTINCT ' + #PivotColumns + '
FROM [dbo].[Category] c
PIVOT( SUM(c.Id)
FOR CategoryName IN (' + #PivotColumns + ')) AS P'
EXEC sp_executesql #SQLQuery

My query will give you the expected output. You can check the output in SQL Fiddle
--dynamic with case
DECLARE #Sql NVARCHAR(4000) = NULL
DECLARE #ColumnHeaders NVARCHAR(4000);
SET #ColumnHeaders = STUFF((
SELECT DISTINCT ',' + 'Max(CASE WHEN rn =' + quotename(rn, '''') + ' THEN Score else null end ) as ' + CategoryName + CHAR(10) + CHAR(13)
FROM (
SELECT CategoryName, row_number() OVER (ORDER BY CategoryName) rn
FROM ( SELECT DISTINCT CategoryName FROM Category) t0
) t1
FOR XML PATH('')
,TYPE
).value('.', 'varchar(max)'), 1, 1, '');
--print #ColumnHeaders
SET #sql = N' ;with cte as (
select * , Row_number() Over(Partition by ResultId Order by ResultId ) rn from
Result)
Select ResultId, ' + #ColumnHeaders + ', SUM(Score) Total from cte Group by
ResultId ';
EXECUTE sp_executesql #sql

Related

How to use table variable in SQL Pivot dynamic query

Error: Must declare the table variable "#Temp_FormData".
Query:
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
DECLARE #Temp_FormData TABLE
(
Id UNIQUEIDENTIFIER,
FormId UNIQUEIDENTIFIER,
ContactId UNIQUEIDENTIFIER,
FieldName NVARCHAR(256),
FieldValue NVARCHAR(MAX),
Created DATETIME
)
;WITH TBL AS(
SELECT fe.Id, fe.FormId, fe.ContactId, fd.FieldName, fd.FieldValue, fe.Created
FROM FormEntries fe (NOLOCK)
LEFT JOIN FieldData fd (NOLOCK) ON fd.FormId = fe.Id
)
INSERT INTO #Temp_FormData
SELECT * FROM TBL
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(FieldName)
FROM #Temp_FormData
GROUP BY FieldName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = N'SELECT ' + #cols + N' FROM
(
SELECT FieldValue, FieldName
FROM #Temp_FormData /* Error here. Tried enclosing variable in square brackets*/
) x
PIVOT
(
MAX(FieldValue)
FOR FieldName in (' + #cols + N')
) p'
EXEC sp_executesql #query
If I execute select * from #Temp_FormData this is the result:
Id
FormId
ContactId
FieldName
FieldValue
Created
1
100
null
Name
John
2023-01-26
1
100
null
Age
25
2023-01-26
2
200
5001
Name
Peter
2023-01-20
2
200
5001
Age
30
2023-01-20
Expected Output:
Id
FormId
ContactId
Name
Age
Created
1
100
null
John
25
2023-01-26
2
200
5001
Peter
30
2023-01-20

How to pivot a table this way when there are 3000 columns

I am using SQL Server
I have a table TT that looks like this
TargetID RowID Actual
0001 1 0
0001 2 1
0001 3 1
0002 1 0
0002 2 1
0002 3 0
0003 1 1
0003 2 1
0003 3 0
How can I pivot it is to this
RowID Target0001 Target0002 Target0003
1 0 0 1
2 1 1 1
3 1 0 0
I tried
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0001'
UNION ALL
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0002'
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0003'
But there are 3000 TargetIDs and my method is not good for that
Any idea how to do that?
You can try this...
DECLARE #ColumnsTable TABLE ([ColumnName] VARCHAR(50));
INSERT INTO #ColumnsTable ([ColumnName])
SELECT DISTINCT '[' + CONVERT(VARCHAR(48), [TargetID]) + ']'
FROM TT;
DECLARE #PivotColumns VARCHAR(MAX), #TotalColumn VARCHAR(MAX), #SQL VARCHAR(MAX);
SET #PivotColumns = (SELECT STUFF((SELECT DISTINCT ', ' + CONVERT(VARCHAR(50), [ColumnName])
FROM #ColumnsTable
FOR XML PATH('')), 1, 2, ''));
SET #SQL = 'SELECT RowID,' +#PivotColumns +'
FROM (
SELECT RowID,TargetID,Actual
FROM TT) AS t
PIVOT (MAX([Actual])
FOR [TargetID] IN (' + #PivotColumns + ')) AS p';
EXEC(#SQL);

How to pivot the given structure to expected one?

I have queries results as in the image, similarly for 90 date's, so how to group object and make date's as columns and count to respective date's.
Thanks in advance!!
You can do it using a dynamic crosstab:
SQL Fiddle
DECLARE #sql1 VARCHAR(4000) = ''
DECLARE #sql2 VARCHAR(4000) = ''
DECLARE #sql3 VARCHAR(4000) = ''
SELECT #sql1 =
'SELECT
[Object]' + CHAR(10)
SELECT #sql2 = #sql2 +
' , MAX(CASE WHEN [Date] = CAST(''' + CONVERT(VARCHAR(8), [Date], 112) + ''' AS DATE) THEN [count] END) AS ' + QUOTENAME([Date]) + CHAR(10)
FROM(
SELECT DISTINCT [Date] FROM tbl
)t
ORDER BY [Date]
SELECT #sql3 =
'FROM tbl
GROUP BY [Object]
ORDER BY [Object]'
PRINT(#sql1 + #sql2 + #sql3)
EXEC (#sql1 + #sql2 + #sql3)
RESULT
| Object | 2015-01-01 | 2015-01-02 |
|--------|------------|------------|
| 1 | 10 | 34 |
| 2 | 20 | 46 |
| 3 | 130 | 78 |
| 4 | 40 | 89 |
| 5 | 55 | 45 |
This is the output of the PRINT command:
SELECT
[Object]
, MAX(CASE WHEN [Date] = CAST('20150101' AS DATE) THEN [count] END) AS [2015-01-01]
, MAX(CASE WHEN [Date] = CAST('20150102' AS DATE) THEN [count] END) AS [2015-01-02]
FROM tbl
GROUP BY [Object]
ORDER BY [Object]
You can use SQL Server PIVOT relational operator
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
--Get unique values of pivot column
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME([Date])
FROM (SELECT DISTINCT [Date] FROM [dbo].[PivotExample]) AS PivotExample
--Create the dynamic query with all the values for
--pivot column at runtime
SET #SQLQuery =
N'SELECT ObjectId, ' + #PivotColumns + '
FROM [dbo].[PivotExample]
PIVOT( SUM(COUNT)
FOR [Date] IN (' + #PivotColumns + ')) AS P'
EXEC sp_executesql #SQLQuery

SQL Multiple dynamic queries to one query

I am working in a SQL Server 2008 environment with SQL Server Management Studio 2012.
I have written 3 separate queries
QUERY 1
SQL query SUMS all the STOCK ON HAND from an Inventories table
SELECT StockCode,
Sum(QtyOnHand) AS 'SOH'
FROM InvWarehouse
WHERE StockCode NOT LIKE '%DEM%' AND StockCode NOT LIKE '%REF%' AND StockCode NOT LIKE 'Z%'
GROUP BY InvWarehouse.StockCode
QUERY 2
This query looks at future orders from a Purchase Orders Table and dynamically returns the next/following 12 months
DECLARE
#cols AS NVARCHAR(MAX),
#cols1 AS NVARCHAR(MAX),
#cols2 AS NVARCHAR(MAX),
#cols3 AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST(YEAR([OrderDueDate]) AS NVARCHAR(4)) + RIGHT('00' + CAST(MONTH([OrderDueDate]) AS NVARCHAR(2)),2) AS YearMonth
FROM PorMasterHdr
JOIN PorMasterDetail
ON PorMasterDetail.PurchaseOrder = PorMasterHdr.PurchaseOrder
WHERE DATEDIFF(MONTH, OrderDueDate, DATEADD(m,12,GETDATE())) <= 12 ) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''),
#cols2 = STUFF((SELECT ',ISNULL(' + QUOTENAME(YearMonth) + ',0) AS ' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST(YEAR([OrderDueDate]) AS NVARCHAR(4)) + RIGHT('00' + CAST(MONTH([OrderDueDate]) AS NVARCHAR(2)),2) AS YearMonth
FROM PorMasterHdr
JOIN PorMasterDetail
ON PorMasterDetail.PurchaseOrder = PorMasterHdr.PurchaseOrder
WHERE DATEDIFF(MONTH, OrderDueDate, DATEADD(m,12,GETDATE())) <= 12) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #query = '
SELECT MStockCode, ' + #cols2 + '
FROM (
SELECT MStockCode,
MOrderQty,
CAST(YEAR([OrderDueDate]) AS NVARCHAR(4))+RIGHT(''00''+CAST(MONTH([OrderDueDate]) AS NVARCHAR(2)),2) AS YearMonth
FROM PorMasterHdr
JOIN PorMasterDetail
ON PorMasterDetail.PurchaseOrder = PorMasterHdr.PurchaseOrder
WHERE MStockCode NOT LIKE ''%DEM%'' AND MStockCode NOT LIKE ''%REF%'' AND MStockCode NOT LIKE ''Z%''
) AS X
PIVOT (
SUM(MOrderQty)
FOR YearMonth in (' + #cols + ')
) AS PT'
EXECUTE (#query)
QUERY 3
This query looks at the past 12 month of sales data from a Sales table and dynamically returns the last/previous 12 months
DECLARE
#cols AS NVARCHAR(MAX),
#cols1 AS NVARCHAR(MAX),
#cols2 AS NVARCHAR(MAX),
#cols3 AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST([TrnYear] AS NVARCHAR(4)) + RIGHT('00' + CAST([TrnMonth] AS NVARCHAR(2)),2) AS YearMonth
FROM ArTrnDetail
WHERE DATEDIFF(MONTH, InvoiceDate, GETDATE()) <= 12 ) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''),
#cols2 = STUFF((SELECT ',ISNULL(' + QUOTENAME(YearMonth) + ',0) AS ' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST([TrnYear] AS NVARCHAR(4)) + RIGHT('00' + CAST([TrnMonth] AS NVARCHAR(2)),2) AS YearMonth
FROM ArTrnDetail
WHERE DATEDIFF(MONTH, InvoiceDate, GETDATE()) <= 12) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #query = '
SELECT StockCode, ' + #cols2 + '
FROM (
SELECT StockCode,
QtyInvoiced,
CAST([TrnYear] AS NVARCHAR(4))+RIGHT(''00''+CAST([TrnMonth] AS NVARCHAR(2)),2) AS YearMonth
FROM ArTrnDetail
WHERE StockCode NOT LIKE ''%DEM%'' AND StockCode NOT LIKE ''%REF%'' AND StockCode NOT LIKE ''Z%''
) AS X
PIVOT (
SUM(QtyInvoiced)
FOR YearMonth in (' + #cols + ')
) AS PT'
EXECUTE (#query)
The results for each query are correct. Now how do I combine them into one query. So that they return
STOCKCODE | Past 12 Month Sales Per Month | Stock On Hand | Future Purchases
Helicopters | 1 4 5 2 3 4 6 1 3 2 3 2| 15 | 2 3 5 4 6 7 8 4 3 2 8 5
Jam | 2 5 6 4 8 5 8 5 7 2 1 2| 30 | 4 5 6 5 8 7 0 1 2 1 1 4
Frogs | 2 3 2 4 8 5 4 6 8 2 1 3| 7 | 5 7 8 8 6 7 4 0 1 2 1 2
STOCK CODE for the above is the same information from the different tables eg. Helicopters in Inventory is the same as Helicopters in Purchase Orders.
I would suggest the following:
Rewrite #query2 to result in two columns: StockCode and Sales. Instead of selecting each month as a seperate column, concatenate each month in a VARCHAR. You already wrote a variable for #cols for selecting the columns seperately. Keep that for pivoting. Write a variable (#SelSales) to concatenate the results for each month in a VARCHAR and use that in your selection for the Sales column.
Rewrite #query3 to result in two columns: StockCode and Purchases (similar to 1.)
Put your #query1 in a NVARCHAR(MAX) variable (the one selecting the stock).
Write a #query to combine them all.
TSQL outline for #query:
DECLARE #query NVARCHAR(MAX);
SET #query=N'
SELECT
COALESCE(stock.StockCode,sales.StockCode,purchases.StockCode) AS StockCode,
COALESCE(sales.Sales,''0 0 0 0 0 0 0 0 0 0 0 0'') AS Sales,
COALESCE(stock.SOH,0) AS Stock,
COALESCE(purchases.Purchases,''0 0 0 0 0 0 0 0 0 0 0 0'') AS Purchases
FROM
('+#query1+') AS stock
FULL JOIN ('+#query2+') AS sales ON sales.StockCode=stock.StockCode
FULL JOIN ('+#query3+') AS purchases ON purchases.StockCode=stock.StockCode';
EXEC(#query);

Pivot with dynamic columns

There are 4 tables:
Suppl, fields: (Code_name, Code_name_arch, Tasknum, Pki_num, Group_eng, Name, Descr, Cost, Quan, shop);
Maker, fields : (Code_maker, Code_maker_arch, Code_name, provider);
Arrival, fields: (Code_arr, Code_maker, quan_arr);
Accounts, fields: (Code_acc, Code_maker, num_acc, quan_acc, summ)
my query is:
ALTER Procedure [dbo].[pr_tblz] As
Set NOCOUNT ON
Declare #task VARCHAR(4000)
Select #task = coalesce(#task + ',[' + [Tasknum] + ']',
'[' + [Tasknum] + ']')
FROM [db_pki].[dbo].[Suppl]
Group BY [Tasknum]
Order BY [Tasknum]
Declare #query VARCHAR(8000)
Set #query='
Alter View Amountzz As
SELECT Shop, Name, Desc, Group_eng as GI, '+ #task +', quan_arr As specif, quan_acc As acns
FROM
(
select
Shop, Name, Desc, Group_eng, Tasknum, Quan, quan_arr, quan_acc
from [db_pki].[dbo].[Suppl] as deliveries
LEFT JOIN [db_pki].[dbo].[Maker] ON (deliveries.[Code_name] = [db_pki].[dbo].[Maker].[Code_name])
LEFT JOIN [db_pki].[dbo].[Arrival] ON ([db_pki].[dbo].[Maker].[Code_maker] = [db_pki].[dbo].[Arrival].[Code_maker])
LEFT JOIN [db_pki].[dbo].[Accounts] ON ([db_pki].[dbo].[Maker].[Code_maker] = [db_pki].[dbo].[Accounts].[Code_maker])
)date_to_pivot
PIVOT
(
Max([Quan])
For [Tasknum]
IN (' + #task + ')
)AS p'
Execute (#query)
result:
Shop Name Descr GI n1 n2 n3 ... specif acns
1 name1 1 5 4 1 1
2 10 name2 2 3 8 2 2
3 name3 3 501 11 3 3
1 8 name1 1 5 16 7 10
a 2 10 name2 2 3 3 5 6
5 name1 1 2 5 6 3
How can I get the following result?
Shop Name Descr GI n1 n2 n3 ... specif acns
1 8 name1 1 5 4 16 8(1+7) 11
a 2 10 name2 2 3 8 3 7 8(2+6)
3 name3 3 501 11 3 3
5 name1 1 2 5 6 3
n1, n2, n3,... - Tasknum
Not tested. Try aggregating the values of Shop, specif and acns in the subselect that pulls the columns before the pivoting, like this (the necessary changes are highlighted in bold):
ALTER Procedure [dbo].[pr_tblz] As
Set NOCOUNT ON
Declare #task VARCHAR(4000)
Select #task = coalesce(#task + ',[' + [Tasknum] + ']',
'[' + [Tasknum] + ']')
FROM [db_pki].[dbo].[Suppl]
Group BY [Tasknum]
Order BY [Tasknum]
Declare #query VARCHAR(8000)
Set #query='
Alter View Amountzz As
SELECT Shop, Name, Desc, Group_eng as GI, '+ #task +', quan_arr As specif, quan_acc As acns
FROM
(
select
MAX(Shop ) OVER (PARTITION BY Name) AS Shop,
Name, Desc, Group_eng, Tasknum, Quan,
SUM(quan_arr) OVER (PARTITION BY Name) AS quan_arr,
SUM(quan_acc) OVER (PARTITION BY Name) AS quan_acc
from [db_pki].[dbo].[Suppl] as deliveries
LEFT JOIN [db_pki].[dbo].[Maker] ON (deliveries.[Code_name] = [db_pki].[dbo].[Maker].[Code_name])
LEFT JOIN [db_pki].[dbo].[Arrival] ON ([db_pki].[dbo].[Maker].[Code_maker] = [db_pki].[dbo].[Arrival].[Code_maker])
LEFT JOIN [db_pki].[dbo].[Accounts] ON ([db_pki].[dbo].[Maker].[Code_maker] = [db_pki].[dbo].[Accounts].[Code_maker])
)date_to_pivot
PIVOT
(
Max([Quan])
For [Tasknum]
IN (' + #task + ')
)AS p'
Execute (#query)