This is my code:
declare #sql as varchar(max)
set #sql = 'insert into #TempReport select [Country],' + #cols + '
from
(
select
C.Country_Name AS [Country],
D.GSL_Name AS [GSL],
SUM(A.Allocation) AS Total
from Work_Information A
inner join Sales B
on A.Sales_ID = B.Sales_Id
inner join Countries C
on A.Country_ID = C.Country_ID
inner join tbl_GSL D
on A.GSL_ID = D.GSL_ID AND A.Sales_ID = D.Sales_Id
where A.Sales_Id = 1
group by A.Country_ID,C.Country_Name, A.Sales_ID, A.GSL_ID,D.GSL_Name
) P Pivot (sum(Total) for [GSL] in (' + #Cols + ')) as Pvt'
exec(#sql)
declare #SumCols as varchar(max)
select #SumCols = stuff(
(select ', Sum(' + quotename(GSLName) + ')' + REPLACE(GSLName,' ','') from
(
select TOP 200 GSL_Name as GSLName
from tbl_GSL GSL
where Sales_Id = 1
ORDER BY GSL_Name
) GlobalSales
ORDER BY GSLName FOR XML PATH('')
),1,2,'')
declare #sql3 as varchar(1000)
set #sql3 = 'select Country,' + #SumCols + ' from #TempReport group by Country WITH ROLLUP'
exec(#sql3)
I have posted only the relevant part of the code because the stored procedure is very long.
The code is working fine. I am using WITH ROLLUP clause to add Grand Total row and columns in the last. The last line of the code does adds a row at the last but I also want to add last column as Grand Total. Also, the last row added by ROLLUP should have first column value as Grand Total.
Related
I have a table that looks like this:
Agent_id
break_id
time
1
1
15
1
2
12
1
2
12
I used pivot to get this structure:
Agent_id
1
2
1
15
24
The problem is that I need to get the count for the pivoted columns, in the example I need to have a structure like this:
Agent_id
1
2
count1
count2
1
15
24
1
2
And I'm not sure on how to do it ... this is the query so far.
DECLARE #COLUMNS VARCHAR(MAX)
DECLARE #QUERY nVARCHAR(MAX)
SELECT #COLUMNS = COALESCE(#COLUMNS + ', ','') + QUOTENAME([break_id])
FROM
(SELECT DISTINCT [break_id] FROM test) AS B
ORDER BY B.[break_id]
SET #QUERY = '
SELECT agent_id,
'+#COLUMNS+'
FROM (
SELECT TOP (1000)
agent_id,break_id,time_inbreak
FROM test
) as pivotData
PIVOT (
SUM(time_inbreak)
FOR break_id IN ('+#COLUMNS+')
) as pivotResult
'
EXEC sp_executesql #QUERY
Any help is greatly appreciated
Unfortunately, PIVOT can only pivot a single column. But we can do multiple columns using conditional aggregation SUM(CASE WHEN... and COUNT(CASE WHEN...:
DECLARE #COLUMNS VARCHAR(MAX)
DECLARE #QUERY nVARCHAR(MAX)
SELECT #COLUMNS = COALESCE(#COLUMNS + ', ','') +
'Sum' + QUOTENAME([break_id]) +
' = SUM(CASE WHEN break_id = ' + break_id + ' THEN time_inbreak END), Count' +
QUOTENAME([break_id]) + ' = COUNT(CASE WHEN break_id = ' + break_id + ' THEN 1 END)'
FROM
(SELECT DISTINCT [break_id] FROM test) AS B
ORDER BY B.[break_id]
OPTION (MAXDOP 1);
SET #QUERY = '
SELECT agent_id,
'+#COLUMNS+'
FROM (
SELECT TOP (1000)
agent_id,break_id,time_inbreak
FROM test
) as pivotData
GROUP BY agent_id;
';
PRINT #QUERY
EXEC sp_executesql #QUERY
I must say, I'm not sure how safe it is to aggregate the columns like that, especially in the face of parallelism. Preferably use STRING_AGG or FOR XML PATH(''), TYPE. At the very least I have added OPTION (MAXDOP 1) to prevent parallelism
I'm trying to replace NULL values with 0 in the following query, however without luck. I know I need to create another column list for the SELECT statement, but I've been unsuccessful so far.
How do I do this properly?
My query:
DECLARE #SQL AS NVARCHAR(MAX);
DECLARE #COLS AS NVARCHAR(MAX);
SELECT #COLS = ISNULL(#COLS + ',', '') + QUOTENAME(ItemCategoryName)
FROM
(
SELECT DISTINCT
ItemCategoryName
FROM sgdw.fact.RetailSales RS
JOIN SGDW.dim.Item I ON RS.ItemID = I.ItemID
WHERE RS.CalendarID >= '20190101'
AND storeid = '92'
AND DATALENGTH(ItemCategoryName) != 0
) AS Properties;
SET #SQL = N'SELECT *
FROM
(
SELECT RetailSalesAmountIncludingVATDKK,
ItemCategoryName,
ReceiptCode
FROM
(
SELECT *
FROM sgdw.fact.RetailSales
WHERE storeid = ''92''
UNION ALL
SELECT *
FROM sgdw.fact.RetailSales_hist
WHERE CalendarID >= ''20190101''
AND storeid = ''92''
) RS
JOIN SGDW.dim.Item I ON RS.ItemID = I.ItemID
JOIN SGDW.dim.Store S ON S.StoreID = RS.StoreID
) SourceTable
PIVOT (Sum(RetailSalesAmountIncludingVATDKK) FOR [ItemCategoryName] IN (' + #COLS + ')) as PivotTable';
EXEC sp_executesql
#SQL;
If you use SQL Server (starting with 2008), Azure SQL Database, Azure SQL Data Warehouse, Parallel Data Warehousen you can bye COALESCE method
like
SELECT #COLS_SUM = #COLS_SUM + 'COALESCE(' + QUOTENAME(finmonth) + ',0)+'
FROM (SELECT DISTINCT finmonth FROM YOUR_TABLE ) AS tmp
SELECT #COLS_SUM = ','+ SUBSTRING(#COLS_SUM, 0, LEN(#COLS_SUM)) +' AS TOTAL'
for more info check https://www.codeproject.com/Questions/1391827/Dealing-with-nulls-dynamic-columns-in-pivot
Append This
SELECT #COLS = 'ISNULL('+REPLACE(#COLS,',',',0 ) , ISNULL(') + ',0 )'
After this block
SELECT #COLS = ISNULL(#COLS + ',', '') + QUOTENAME(ItemCategoryName)
FROM
(
SELECT DISTINCT
ItemCategoryName
FROM sgdw.fact.RetailSales RS
JOIN SGDW.dim.Item I ON RS.ItemID = I.ItemID
WHERE RS.CalendarID >= '20190101'
AND storeid = '92'
AND DATALENGTH(ItemCategoryName) != 0
) AS Properties;
I want to get dynamic pivot result into temp table by which I can send it back to C# code using select query.
DECLARE #partymasterid bigint = 2;
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SELECT
#cols = STUFF((SELECT DISTINCT '[' + CAST(dbo.InventoryProductMaster.Name AS nvarchar(max)) + '],'
FROM dbo.InventoryBiltyMaster
INNER JOIN dbo.InventoryPartyProductPriceMaster ON dbo.InventoryBiltyMaster.InventoryPartyProductPriceMasterID = dbo.InventoryPartyProductPriceMaster.ID
INNER JOIN dbo.InventoryProductMaster ON dbo.InventoryPartyProductPriceMaster.InventoryProductMasterID = dbo.InventoryProductMaster.ID
WHERE dbo.InventoryBiltyMaster.PartyMasterID = #partymasterid
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 0, '');
SELECT #query =
'
SELECT
*
FROM
(SELECT
Count(dbo.InventoryBiltyMaster.ID) AS BiltyCount,
--dbo.InventoryBiltyMaster.InventoryProductMasterID,
--dbo.InventoryBiltyMaster.VehicleMasterID,
dbo.InventoryProductMaster.Name,
dbo.VehicleMaster.VehicleNumber
FROM
dbo.InventoryBiltyMaster
INNER JOIN dbo.InventoryPartyProductPriceMaster ON dbo.InventoryBiltyMaster.InventoryPartyProductPriceMasterID = dbo.InventoryPartyProductPriceMaster.ID
INNER JOIN dbo.InventoryProductMaster ON dbo.InventoryPartyProductPriceMaster.InventoryProductMasterID = dbo.InventoryProductMaster.ID
INNER JOIN dbo.VehicleMaster ON dbo.InventoryBiltyMaster.VehicleMasterID = dbo.VehicleMaster.ID
WHERE
dbo.InventoryBiltyMaster.PartyMasterID = ' + CAST(#partymasterid as nvarchar(50)) + '
GROUP BY
dbo.InventoryBiltyMaster.InventoryProductMasterID,
dbo.InventoryProductMaster.Name,
dbo.InventoryBiltyMaster.VehicleMasterID,
dbo.VehicleMaster.VehicleNumber
)
AS S
PIVOT
(
MAX(BiltyCount)
FOR [Name] IN (' + LEFT(#cols, LEN(#cols)-1) + ')
)AS pvt';
EXEC SP_EXECUTESQL #query
Here may be result have 3 column or 5 columns or 10-15 columns. it depends on the result of query. and this result i want on temp table for use more this data.
I want to use it in a stored procedure to send back result into ASP.NET MVC 5 with Entity Framework
You can't store inside a # (temp table) without defining it in the parent scope when using sp_executesql. In your case you have a dynamic pivot and you don't know what and how many columns are going to be there.
sp_executesql runs in a different session (sp_executesql creates its own session) and temp tables are session specific.
For your scenario you can use ## (global temp table). You can change your query like following.
SELECT #query =
'
SELECT
* into ##temp
FROM
(SELECT
Count(dbo.InventoryBiltyMaster.ID) AS BiltyCount,
--dbo.InventoryBiltyMaster.InventoryProductMasterID,
--dbo.InventoryBiltyMaster.VehicleMasterID,
dbo.InventoryProductMaster.Name,
dbo.VehicleMaster.VehicleNumber
FROM
dbo.InventoryBiltyMaster
INNER JOIN dbo.InventoryPartyProductPriceMaster ON dbo.InventoryBiltyMaster.InventoryPartyProductPriceMasterID = dbo.InventoryPartyProductPriceMaster.ID
INNER JOIN dbo.InventoryProductMaster ON dbo.InventoryPartyProductPriceMaster.InventoryProductMasterID = dbo.InventoryProductMaster.ID
INNER JOIN dbo.VehicleMaster ON dbo.InventoryBiltyMaster.VehicleMasterID = dbo.VehicleMaster.ID
WHERE
dbo.InventoryBiltyMaster.PartyMasterID = ' + CAST(#partymasterid as nvarchar(50)) + '
GROUP BY
dbo.InventoryBiltyMaster.InventoryProductMasterID,
dbo.InventoryProductMaster.Name,
dbo.InventoryBiltyMaster.VehicleMasterID,
dbo.VehicleMaster.VehicleNumber
)
AS S
PIVOT
(
MAX(BiltyCount)
FOR [Name] IN (' + LEFT(#cols, LEN(#cols)-1) + ')
)AS pvt';
EXEC SP_EXECUTESQL #query
--now you can use ##temp
Note: use of global temp table can lead to unpredictable behavior if it gets updated from multiple sessions, you may think of giving a unique name for each session.
I have a question about the parametrization of a SQL query I created. The original query looks like this:
DECLARE #portfolios AS NVARCHAR(MAX)
DECLARE #Abfrage AS NVARCHAR(MAX)
SET #portfolios = STUFF((SELECT '[' + PortfolioName + '], ' FROM tblPortfolio ORDER BY PortfolioName FOR XML PATH('')),1,5,NULL)
SELECT #portfolios = LEFT(#portfolios,(LEN(#portfolios)-1))
SET #Abfrage ='
SELECT Description, ' + #portfolios + '
FROM
( SELECT tblReturnType.Description, tblPortfolio.PortfolioName, tblReturnAbs.Value
FROM
tblReturnType INNER JOIN ((tblReturnAbs INNER JOIN tblPortfolio ON tblReturnAbs.PortfolioNo = tblPortfolio.PortfolioNo) INNER JOIN tblInstrument ON tblReturnAbs.InstrumentNo =tblInstrument.InstrumentNo) ON (tblReturnType.ReturnType = tblReturnAbs.ReturnType)
WHERE (tblReturnAbs.Date BETWEEN ''01/01/17'' AND Dateadd("d",-1,''01/31/17'')) AND tblInstrument.Currency = ''EUR''
) AS x
PIVOT
(
sum(x.Value)
For x.PortfolioName in (' + #portfolios + ')
) AS piv
ORDER BY Description
'
Execute (#Abfrage)
It creates an output where a variable number of portfolios is displayed as columns, with different parts of portfolio earnings displayed under each portfolio name. Now, i would like to use parameters to be able to dynamically change the "WHERE" clause. At the same time, it should be possible to access the query from a frontend (which is yet to be decided). To achieve this, i tried to transform the query in a stored procedure like this:
ALTER PROCEDURE [dbo].[Return_Attribution]
#Startdate datetime,
#EndDate datetime,
#Currency nvarchar(10)
AS
BEGIN
DECLARE #portfolios AS NVARCHAR(MAX)
DECLARE #Abfrage AS NVARCHAR(MAX)
SET #portfolios = STUFF((SELECT '[' + PortfolioName + '], ' FROM tblPortfolio ORDER BY PortfolioName FOR XML PATH('')),1,5,NULL)
SELECT #portfolios = LEFT(#portfolios,(LEN(#portfolios)-1))
SET #Abfrage ='SELECT Description, ' + #portfolios + '
FROM
(SELECT tblReturnType.Description, tblPortfolio.PortfolioName, tblReturnAbs.Value
FROM tblReturnType
INNER JOIN ((tblReturnAbs
INNER JOIN tblPortfolio ON tblReturnAbs.PortfolioNo = tblPortfolio.PortfolioNo)
INNER JOIN tblInstrument ON tblReturnAbs.InstrumentNo =tblInstrument.InstrumentNo) ON (tblReturnType.ReturnType = tblReturnAbs.ReturnType)
WHERE (tblReturnAbs.Date >= '+ #Startdate +' AND tblReturnAbs.Date <= Dateadd("d",-1,'+ #EndDate +')) AND tblInstrument.Currency = '+ #Currency +'
) AS x
PIVOT
(
sum(x.Value)
For x.PortfolioName in (' + #portfolios + ')
) AS piv
ORDER BY Description
'
Execute (#Abfrage)
END
Now, if I try to execute this procedure and enter parameters, I get the following error:
Msg 8152, Level 16, State 10, Procedure Return_Attribution, Line 16
String or binary data would be truncated.
I did a bit of research and I found out that this error normally occurs when a string is too long to fit into a data field. As it only occurs when I used the parametrized procedure, I suppose I made a mistake in either defining or using the parameters. Any help would be appreciated.
Thank you for the reply, but again im already using dynamic fields.
For example:
DECLARE #columns VARCHAR(8000)
SELECT #columns = COALESCE(#columns + ',[' + cast(QuestionName as varchar) + ']', '[' + cast(QuestionName as varchar)+ ']')
FROM Answer A
INNER JOIN Question Q ON A.QuestionID = Q.QuestionID
INNER JOIN Customer C ON A.CustomerID = C.CustomerID
GROUP BY Q.QuestionName
SET #columns = '[CustomerID],' + #columns
DECLARE #query VARCHAR(8000)
SET #query = 'Some PIVOT query without aggregation'
EXECUTE(#query)
But i want to arrange the fields in #columns..i want 1 field to be 1st:
#columns = [f1],[f2],[f3].
What i want is like this:
#columns = [f2],[f1],[f3] :
Because one of the fetch data..is constant in my report..
and should be place in the beginning or the first row to be displayed.