The SQL Below works gr8, but the order, how to set that to put Jan, Feb, Mar order. I realize I need to do this with ordinal month but when I add in the order by I get error 'The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.'
The results look like :
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(year(TransactionDateTime))
FROM Quotations
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query =
'SELECT *
FROM (
SELECT
left(datename(month,TransactionDateTime),3) as [month], year(TransactionDateTime) as [year],
isnull(count(*),0) as Total
FROM quotations
group by left(datename(month,TransactionDateTime),3), year(TransactionDateTime)
) as s
PIVOT
(
SUM(Total)
FOR [year] IN (' + #cols + ')
) AS QuotationResults'
EXECUTE(#query)
As the error says, you can't order a subquery unless there is a reason for the order (TOP, FOR XML etc). The reason for this is that just because you have ordered the subquery there is no reason that this order would be maintained in your outer query. SQL Server is essentially telling you that your ORDER BY is pointless, therefore not valid.
The solution is to simply add a column with month number to your subuquery s, then you can order by it. You would also need to explicitly state your select list to ensure that this new column does not appear in it:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(year(TransactionDateTime))
FROM Quotations
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET #query =
'SELECT [month], ' + #Cols + '
FROM (
SELECT
left(datename(month,TransactionDateTime),3) as [month],
datepart(month,TransactionDateTime) as [monthNum],
year(TransactionDateTime) as [year],
isnull(count(*),0) as Total
FROM quotations
group by left(datename(month,TransactionDateTime),3), datepart(month,TransactionDateTime), year(TransactionDateTime)
) as s
PIVOT
(
SUM(Total)
FOR [year] IN (' + #cols + ')
) AS QuotationResults
ORDER BY QuotationResults.MonthNum;';
EXECUTE(#query);
ADDENDUM
The ISNULL() does not trap the null values because at the point of using ISNULL() they don't exist. COUNT(*) will never return null, so your ISNULL() is actually redundant.
In a very simple example if you have:
TransactionDateTime
----------------------
2015-01-01
2015-02-01
2015-02-01
2014-03-01
To skip ahead one step, after your pivot you will end up with:
Month 2014 2015
------------------------
Jan NULL 1
Feb NULL 2
Mar 1 NULL
So you end up with NULL values, now to go back a step, if you look at the results after your aggregation you have:
Month MonthNum Year Total
-----------------------------------
Jan 1 2015 1
Feb 2 2015 2
Mar 3 2014 1
So there are no rows for Jan or Feb in 2014, therefore SUM(NULL) will yield NULL. I would suggest leaving all the aggregation to the pivot function. So your non dynamic query would look something like:
SELECT pvt.[Month], pvt.[2014], pvt.[2015]
FROM ( SELECT [Month] = LEFT(DATENAME(MONTH, TransactionDateTime), 3),
[MonthNum] = DATEPART(MONTH, TransactionDateTime),
[Year] = DATEPART(YEAR, TransactionDateTime),
Value = 1
FROM Quotations
) AS t
PIVOT
(
COUNT(Value)
FOR [year] IN ([2014], [2015])
) AS pvt
ORDER BY pvt.MonthNum;
And put into dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(DATEPART(YEAR, TransactionDateTime))
FROM Quotations
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET #query =
'SELECT pvt.[Month], ' + #cols + '
FROM ( SELECT [Month] = LEFT(DATENAME(MONTH, TransactionDateTime), 3),
[MonthNum] = DATEPART(MONTH, TransactionDateTime),
[Year] = DATEPART(YEAR, TransactionDateTime),
Value = 1
FROM Quotations
) AS t
PIVOT
(
COUNT(Value)
FOR [year] IN (' + #cols + ')
) AS pvt
ORDER BY pvt.MonthNum;
(
SUM(Total)
FOR [year] IN (' + #cols + ')
) AS QuotationResults
ORDER BY QuotationResults.MonthNum;';
EXECUTE sp_executesql #query;
Related
When I try to run a more advanced SQL query on an ASP page I get this error:
operation not allowed when the object is closed
When I run this code it's working:
...
sql = "SELECT distinct team FROM tbl_teams"
rs.open sql, conndbs, 1, 1
...
But when I run this code (and this code is working if I run it in Microsoft SQL Server Management Studio), I get the error...
...
sql = "DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX), #orderby nvarchar(max), #currentYear varchar(4) select #currentYear = cast(year(getdate()) as varchar(4)) select #cols = STUFF((SELECT ',' + QUOTENAME(year([datefrom])) from tbl_teams group by year([datefrom]) order by year([datefrom]) desc FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'') select #orderby = 'ORDER BY ['+cast(year(getdate()) as varchar(4)) + '] desc' set #query = 'SELECT team, Won = [1], Lost=[2], Draw = [3]' + #cols + ', Total from ( select team, new_col, total from ( select team, dt = year([datefrom]), result, total = count(*) over(partition by team) from tbl_teams ) d cross apply ( select ''dt'', dt union all select ''result'', case when dt = '+#currentYear+' then result end ) c (old_col_name, new_col) ) x pivot ( count(new_col) for new_col in ([1], [2], [3],' + #cols + ') ) p '+ #orderby exec sp_executesql #query"
rs.open sql, conndbs, 1, 1
...
This is a better overview of the query:
DECLARE
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#orderby nvarchar(max),
#currentYear varchar(4)
select #currentYear = cast(year(getdate()) as varchar(4))
select #cols
= STUFF((SELECT ',' + QUOTENAME(year([datefrom]))
from tbl_teams
group by year([datefrom])
order by year([datefrom]) desc
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #orderby = 'ORDER BY ['+cast(year(getdate()) as varchar(4)) + '] desc'
set #query = 'SELECT team, Won = [1],
Lost=[2], Draw = [3]' + #cols + ', Total
from
(
select
team,
new_col,
total
from
(
select team,
dt = year([datefrom]),
result,
total = count(*) over(partition by team)
from tbl_teams
) d
cross apply
(
select ''dt'', dt union all
select ''result'', case when dt = '+#currentYear+' then result end
) c (old_col_name, new_col)
) x
pivot
(
count(new_col)
for new_col in ([1], [2], [3],' + #cols + ')
) p '+ #orderby
exec sp_executesql #query
Do I need to run the query on another way or what is wrong with this code?
This is a common problem caused by row counts being interpreted as output from a Stored Procedure when using ADODB with SQL Server.
To avoid this remember to set
SET NOCOUNT ON;
in your Stored Procedure this will stop ADODB returning a closed recordset, or if for whatever reason you don't want to do this (not sure why as you can always use ##ROWCOUNT to pass the row count back), you can use
'Return the next recordset, which will be the result of the Stored Procedure, not
'the row count generated when SET NOCOUNT OFF (default).
Set rs = rs.NextRecordset()
which returns the next ADODB.Recordset if ADODB has detected one being returned by the Stored Procedure (might be best to check rs.State <> adStateClosed when dealing with multiple ADODB.Recordset objects).
I have the following data output from my database
Observation 1 aug -2015
Improvement suggestion 1 dec -2015
Observation 1 dec -2015
Accident 2 jan -2016
Non Conformity 5 jan -2016
Observation 5 jan -2016
I've tried to figure out how to use PIVOT-table to make this work but cannot make it work. The date is dynamic depending on a date in the database. The output I am looking for is like below. Can someone please point me into right direction?
Look at the fiddle what I've tried so far I know it is using SUM right now, and that is not correct, but I cannot figure out what to use. http://sqlfiddle.com/#!3/0bd0c/4
PS. The data and the image are not related, so don't be fooled by the image. It is just an example
Is this what you were looking for:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(ColumnName)
from tempData
group by ColumnName, name
FOR XML PATH(''), Type
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT Name, ' + #cols + N' from
(
select Name, value, ColumnName
from tempData
) x
pivot
(
SUM(value)
for ColumnName in (' + #cols + N')
) p '
exec sp_executesql #query;
Changing this in your fiddle return the rows as you need it.
Try it like this:
DECLARE #cols AS NVARCHAR(MAX)=
STUFF(
(
SELECT DISTINCT ',[' + ColumnName + ']'
FROM tempData
FOR XML PATH('')
)
,1,1,'');
DECLARE #SqlCmd VARCHAR(MAX)=
'SELECT p.*
FROM
(
SELECT *
FROM tempData
) AS tbl
PIVOT
(
SUM(Value) FOR ColumnName IN(' + #cols +')
) AS p';
EXEC(#SqlCmd);
This question already has answers here:
How to replace (null) values with 0 output in PIVOT
(7 answers)
Closed 3 years ago.
I've been trying to remove NULL values from a pivot table. I've tried several suggested solutions to without luck.
Declare #toName as nvarchar(max)
SELECT *
FROM (SELECT
isnull(protocol,0) as Protocol,
isnull(callCategory,0) as DCRCategory,
isnull(DATEPART(Year, eCreationTime),0) AS Year,
isnull(DATENAME(MONTH, eCreationTime),0) [Month],
isnull(COUNT(1),0) callCategory
FROM DCR_DATA
where ProjectManager = ''' + #toName + '''
GROUP BY protocol, callCategory, YEAR(eCreationTime), DATENAME(MONTH, eCreationTime))
AS MontlyDCRData
PIVOT(SUM(callCategory)
FOR Month IN ([January],[February],[March],[April],[May],
[June],[July],[August],[September],[October],[November],
[December])) AS MNamePivot
Here is an example of what I am returning:
Protocol DCRCategory Year January February March April May June July August September October November December
123 Cat 1 2017 NULL NULL NULL NULL NULL NULL NULL NULL 4 NULL NULL NULL
DECLARE #cols AS NVARCHAR(MAX),#cols1 AS NVARCHAR(MAX),#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(+'ISNULL('+CAST(ColName AS VARCHAR) +' ,0)' ) + ' ' + QUOTENAME(ColName)
FROM TblName
GROUP BY ColName
ORDER BY ColName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SELECT #cols1 = STUFF((SELECT ',' + QUOTENAME(ColName)
from TblName
group by ColName
order by ColName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT ColName1,' + #cols + ' from
(
SELECT ColName1, ColName2, ColName3
FROM TblName
) x
PIVOT
(
SUM(ColName3)
FOR ColName2 IN (' + #cols1 + ')
) p '
EXECUTE(#query);
Not the most elegant solution, but here goes -> Select into a temp table
Declare #toName as nvarchar(max)
SELECT *
INTO #tmpTable
FROM (SELECT isnull(protocol,0) as Protocol
, isnull(callCategory,0) as DCRCategory
, isnull(DATEPART(Year, eCreationTime),0) AS Year
, isnull(DATENAME(MONTH, eCreationTime),0) [Month]
, isnull(COUNT(1),0) callCategory
FROM DCR_DATA
where ProjectManager = ''' + #toName + '''
GROUP BY protocol
, callCategory
, YEAR(eCreationTime)
, DATENAME(MONTH, eCreationTime))
AS MontlyDCRData
PIVOT(SUM( callCategory)
FOR Month IN ([January],[February],[March],[April],[May],
[June],[July],[August],[September],[October],[November],
[December])) AS MNamePivot
and then isnull from there.
SELECT tmp.Protocol, tmp.DCRCategory, tmp.[Year]
, isnull(tmp.January,0)
, ...
, isnull(tmp.December,0)
FROM #tmpTable as tmp
The SELECT statement below is from a stored procedure I am using to retrieve data which is subsequently sent to JSON:
SELECT *
FROM (
SELECT CAST(DateTimeUTC as SmallDateTime) as [DateTime], DataValue, VariableID
FROM DataValues
WHERE SiteID = #siteID and VariableID BETWEEN 9 and 10 and DateTimeUTC >= DATEADD (day, #pastDays, getdate())
) TableDate
PIVOT (SUM(DataValue) FOR VariableID IN ([9],[10])) PivotTable ORDER BY [DateTime]
What I would like to accomplish is to modify the procedure to accept a column range to pivot. In the above example I am only retrieving values for two variables. What if I want to retrieve VariableID 1 through 10, or 1 through 50? There has to be a way to retrieve 1 through 10 in a way other than:
VariableID IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])
The Using PIVOT and UNPIVOT on MSDN doesn't mention a way to specify a range.
I realize that it is possible to use dynamic SQL in a stored procedure, but given my own knowledge of SQL and the knowledge of those who will have to maintain this long term I am hesitant to introduce additional complexity by using dynamic SQL. There is a conceptually similar question on here, see TSQL Pivot Long List of Columns, and it was answered with a dynamic SQL solution.
Unfortunately, the PIVOT function does not have the ability to generate the list of columns, without using dynamic sql.
So your SQL code will be similar to this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(VariableID)
from DataValues
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [DateTime], ' + #cols + ' from
(
SELECT CAST(DateTimeUTC as SmallDateTime) as [DateTime],
DataValue,
VariableID
FROM DataValues
WHERE SiteID = '+cast(#siteID as varchar(10))+'
-- and VariableID BETWEEN 9 and 10 -- remove the variableID filter
and DateTimeUTC >= DATEADD (day, +'cast(#pastDays as varchar(10))+', getdate())
) x
pivot
(
SUM(DataValue)
for VariableID in (' + #cols + ')
) p '
execute(#query)
The key to this is the following code which generates the list of variableIds to become columns:
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(VariableID)
from DataValues
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
This will create the list of all distinct variable id's. This list is then added to the SQL query string to return.
I have the following query which is always giving the error. Could some body help me
to resolve this?
"Incorrect syntax near the keyword '#stqsql'".
My code is:
declare #strsql nvarchar(max)
set #strsql=select merchantid from employee
select *
from
(
Select s.employeeid,
COUNT(*) as TotCount
from Employee s
GROUP BY s.employeeid
)as a
pivot (avg(TotCount) for employeeid IN ('+#stqsql+')) AS NoOfRec
Unfortunately the way you are trying to get dynamic columns does not work. You will have to use something similar to the following:
DECLARE #colsPivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
-- this gets the list of values that you want to pivot
select #colsPivot = STUFF((SELECT distinct ', ' + QUOTENAME(merchantid )
from employee
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
-- since you are use a dynamic list you have to use dynamic sql
set #query = 'SELECT ' + #colsPivot + ' from
(
SELECT s.employeeid,
COUNT(*) as TotCount
FROM Employee s
GROUP BY s.employeeid
) src
pivot
(
avg(TotCount)
for employeeid in (' + #colsPivot + ')
) p '
execute(#query)