Don't understand query behaviour - sql

I have a strange behaviour in report builder.
I'm working from an existing dataset and testing my own code in SQL studio before trying in report builder. And I'm lost because I don't understand why the following doesn't work :
SELECT
v_Collection_Alias.Name as CollectionName,
v_Package_Alias.Name as SoftwareName,
'Package' as ApplicationType,
NumberSuccessTable='NumberSuccessTable', sum(case when stat.LastState in (-1,13) then 1 else 0 end) as NumberSuccess,
NumberInProgressTable='NumberInProgress', sum(case when stat.LastState in (8,9) then 1 else 0 end) as NumberInProgress,
NumberUnknownTable='NumberUnknown', sum(case when stat.LastState in (0) then 1 else 0 end) as Unknown,
NumberErrorTable='NumberError', sum(case when stat.LastState in (11) then 1 else 0 end) as NumberError,
NumberOtherTable='NumberOther', sum(case when stat.LastState in (10) then 1 else 0 end) as NumberOther,
'' as LastModifiedby,
'' as Version,
v_Advertisement_Alias.CollectionID as CollectionID,
v_Advertisement_Alias.AdvertisementID as DeploymentID,
'' as CI_ID,
'' as DeploymentTime,
v_Advertisement_Alias.PresentTime as ModificationTime,
'' as AssignmentID
FROM fn_rbac_Advertisement(#UserSIDs) v_Advertisement_Alias
JOIN fn_rbac_ClientAdvertisementStatus(#UserSIDs) stat on v_Advertisement_Alias.AdvertisementID = stat.AdvertisementID
INNER JOIN fn_rbac_Package2(#UserSIDs) v_Package_Alias ON v_Advertisement_Alias.PackageID = v_Package_Alias.PackageID
INNER JOIN fn_rbac_Collection(#UserSIDs) v_Collection_Alias ON v_Advertisement_Alias.CollectionID = v_Collection_Alias.CollectionID
This in report builder is prompting me an error because fn_rbac_Advertisement.Name need a group by clause. Whereas the following is properly working in the original report :
SELECT
v_Collection_Alias.Name as CollectionName,
v_Package_Alias.Name as SoftwareName,
'' as ApplicationType,
'' as NumberSuccess,
'' as NumberInProgress,
'' as NumberUnknown,
'' as NumberErrors,
'' as NumberOther,
'' as LastModifiedby,
'' as Version,
v_Advertisement_Alias.CollectionID as CollectionID,
v_Advertisement_Alias.AdvertisementID as DeploymentID,
'' as CI_ID,
'' as DeploymentTime,
v_Advertisement_Alias.PresentTime as ModificationTime,
'' as AssignmentID,
'' as ApplicationType
FROM fn_rbac_Advertisement(#UserSIDs) v_Advertisement_Alias
INNER JOIN fn_rbac_Package2(#UserSIDs) v_Package_Alias ON v_Advertisement_Alias.PackageID = v_Package_Alias.PackageID
INNER JOIN fn_rbac_Collection(#UserSIDs) v_Collection_Alias ON v_Advertisement_Alias.CollectionID = v_Collection_Alias.CollectionID
and the following returns me what I want in SQL Studio :
Select
NumberSuccessTable='NumberSuccessTable', sum(case when stat.LastState in (-1,13) then 1 else 0 end),
NumberInProgressTable='NumberInProgress', sum(case when stat.LastState in (8,9) then 1 else 0 end),
NumberUnknownTable='NumberUnknown', sum(case when stat.LastState in (0) then 1 else 0 end) AS NumberU,
NumberErrorTable='NumberError', sum(case when stat.LastState in (11) then 1 else 0 end) AS NumberError,
NumberOtherTable='NumberOther', sum(case when stat.LastState in (10) then 1 else 0 end) as NumberOther
From v_ClientAdvertisementStatus stat
Thanks for any help guys ! :)

First Query:
when you use aggregate function like SUM you need to do a GROUP BY on all your columns that Don't have the aggregate function.
Second Query:
this works because you dont have an aggregate function in your select therefor you dont have to use GROUP BY and report builder can handle the sum and group by for you.
Third query:
this query works because you have aggregate function on all your columns so you dont need to GROUP BY.
Always remember the LOGICAL query processing order is as below:
1.FROM
2.WHERE
3.GROUP BY
4.HAVING
5.SELECT
6.ORDER BY
so your GROUP BY happens before SELECT for that reason when you group by you cant include '' in your group by clause because that column does not exists yet.
so it gives you an error.

When using aggregate functions (such as SUM) you must apply a GROUP BY clause to all columns that are not aggregated eg:
GROUP BY v_Collection_Alias.Name,
v_Package_Alias.Name,
v_Advertisement_Alias.CollectionID as CollectionID,
v_Advertisement_Alias.AdvertisementID as DeploymentID,
v_Advertisement_Alias.PresentTime as ModificationTime
Alternatively, if you don't want to maintain a lengthy GROUP BY clause you could remove the SUM functions from the SELECT statement and let the report handle the aggregation, with appropriate grouping in Report Builder.

Related

Sum a column and perform more calculations on the result? [duplicate]

This question already has an answer here:
How to use an Alias in a Calculation for Another Field
(1 answer)
Closed 3 years ago.
In my query below I am counting occurrences in a table based on the Status column. I also want to perform calculations based on the counts I am returning. For example, let's say I want to add 100 to the Snoozed value... how do I do this? Below is what I thought would do it:
SELECT
pu.ID Id, pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END) AS Snoozed,
Snoozed + 100 AS Test
FROM
Prospects p
INNER JOIN
ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE
p.Store = '108'
GROUP BY
pu.Name, pu.Id
ORDER BY
Name
I get this error:
Invalid column name 'Snoozed'.
How can I take the value of the previous SUM statement, add 100 to it, and return it as another column? What I was aiming for is an additional column labeled Test that has the Snooze count + 100.
You can't use one column to create another column in the same way that you are attempting. You have 2 options:
Do the full calculation (as #forpas has mentioned in the comments above)
Use a temp table or table variable to store the data, this way you can get the first 5 columns, and then you can add the last column or you can select from the temp table and do the last column calculations from there.
You can not use an alias as a column reference in the same query. The correct script is:
SELECT
pu.ID Id, pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END)+100 AS Snoozed
FROM
Prospects p
INNER JOIN
ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE
p.Store = '108'
GROUP BY
pu.Name, pu.Id
ORDER BY
Name
MSSQL does not allow you to reference fields (or aliases) in the SELECT statement from within the same SELECT statement.
To work around this:
Use a CTE. Define the columns you want to select from in the CTE, and then select from them outside the CTE.
;WITH OurCte AS (
SELECT
5 + 5 - 3 AS OurInitialValue
)
SELECT
OurInitialValue / 2 AS OurFinalValue
FROM OurCte
Use a temp table. This is very similar in functionality to using a CTE, however, it does have different performance implications.
SELECT
5 + 5 - 3 AS OurInitialValue
INTO #OurTempTable
SELECT
OurInitialValue / 2 AS OurFinalValue
FROM #OurTempTable
Use a subquery. This tends to be more difficult to read than the above. I'm not certain what the advantage is to this - maybe someone in the comments can enlighten me.
SELECT
5 + 5 - 3 AS OurInitialValue
FROM (
SELECT
OurInitialValue / 2 AS OurFinalValue
) OurSubquery
Embed your calculations. opinion warning This is really sloppy, and not a great approach as you end up having to duplicate code, and can easily throw columns out-of-sync if you update the calculation in one location and not the other.
SELECT
5 + 5 - 3 AS OurInitialValue
, (5 + 5 - 3) / 2 AS OurFinalValue
You can't use a column alias in the same select. The column alias do not precedence / sequence; they are all created after the eval of the select result, just before group by and order by.
You must repeat code :
SELECT
pu.ID Id,pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END) AS Snoozed,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END)+ 100 AS Test
FROM
Prospects p
INNER JOIN
ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE
p.Store = '108'
GROUP BY
pu.Name, pu.Id
ORDER BY
Name
If you don't want to repeat the code, use a subquery
SELECT
ID, Name, LeadCount, Working, Uninterested,Converted, Snoozed, Snoozed +100 AS test
FROM
(SELECT
pu.ID Id,pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END) AS Snoozed
FROM Prospects p
INNER JOIN ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE p.Store = '108'
GROUP BY pu.Name, pu.Id) t
ORDER BY Name
or a view

Using TSQL to UNPIVOT simple aggregated totals...is this even possible?

I was trying to perform a simple un-pivoting of some data before it is loaded into Microsoft PowerBI. Since the PowerBI report has to use DirectQuery, using 'Unpivot' in the query editor is not an option. So it seemed that this could probably be done in the intial SQL that gets loaded.
select
sum(case when cw.State = 'WORK' then 1 else null end) [Work]
,sum(case when cw.State = 'OUT' then 1 else null end) [Out]
,sum(case when cw.State = 'POST' then 1 else null end) [Post]
from CurrentWork cw
This code outputs:
Work Out Post
---- --- ----
5 3 21
But I would like the output to be displayed like:
Event Amount
----- ------
Work 5
Out 3
Post 21
I believe I need to use the UNPIVOT TSQL command, but cant figure out the correct way to use it.
Is this even possible, or am I approaching this problem from the wrong direction?
You don't need to do UNPIVOT, you want aggregation :
select status, count(*)
from CurrentWork
group by status;
If above data are aggregated then you can use either subuqery or cte with apply :
with t as (
select sum(case when cw.State = 'WORK' then 1 else null end) [Work]
sum(case when cw.State = 'OUT' then 1 else null end) [Out]
sum(case when cw.State = 'POST' then 1 else null end) [Post]
from CurrentWork cw
)
select tt.Event, tt.[Amount]
from t cross apply
( values ([Work], [Amount]), ([Out], [Amount]), ([Post], [Amount])
) tt(Event, [Amount]);

How to fix "Cannot perform an aggregate function on an expression containing an aggregate or a subquery."

I'm trying to count the number of rows in a table and output the result in a column. Counting the how many rows given in a specific data then display the result in a column. Can someone help me on this? Appreciate your help.
Below is my code:
SELECT DISTINCT BOM.STYLE_ID,
BOM.SEASON_ID,
BOM.Ad_compo_desc,
BOM.CONTENT_CLASS,
COUNT(CASE
WHEN BOM.CONTENT_CLASS = 'ART'
AND BOM.Ad_compo_desc LIKE '%emb%' THEN
(SELECT COUNT(BOM.Ad_compo_desc))
END) AS EMBRO,
COUNT(CASE
WHEN BOM.CONTENT_CLASS = 'ART'
AND BOM.Ad_compo_desc LIKE '%print%' THEN
(SELECT COUNT(BOM.Ad_compo_desc))
END) AS PRINTING
FROM IPLEXSTY_AD_BOM_DTL BOM
WHERE CONTENT_CLASS = 'ART' --AND BOM.Ad_compo_desc IS NOT NULL
GROUP BY BOM.STYLE_ID,
BOM.SEASON_ID,
BOM.CONTENT_CLASS,
BOM.Ad_compo_desc,
BOM.CONTENT_CLASS
And it gives me an error:
Cannot perform an aggregate function on an expression containing an
aggregate or a subquery
I believe you want something like this:
SELECT BOM.STYLE_ID, BOM.SEASON_ID,
SUM(CASE WHEN BOM.Ad_compo_desc LIKE '%emb%' THEN 1 ELSE 0 END) AS EMBRO,
SUM(CASE WHEN BOM.Ad_compo_desc LIKE '%print%' THEN 1 ELSE 0 END) AS PRINTING
FROM IPLEXSTY_AD_BOM_DTL BOM
WHERE CONTENT_CLASS = 'ART' --AND BOM.Ad_compo_desc IS NOT NULL
GROUP BY BOM.STYLE_ID, BOM.SEASON_ID;
Notice that unnecessary columns have been removed from both the SELECT and GROUP BY. Also, the WHERE clause filters out rows other than 'ART'; that filtering is not needed in the CASE.
Are you want to write something below
SELECT BOM.STYLE_ID,
BOM.SEASON_ID,BOM.Ad_compo_desc,
BOM.CONTENT_CLASS,
sum(CASE WHEN BOM.CONTENT_CLASS = 'ART' AND BOM.Ad_compo_desc LIKE '%emb%' THEN 1 else 0 END) AS EMBRO,
sum(CASE WHEN BOM.CONTENT_CLASS = 'ART' AND BOM.Ad_compo_desc LIKE '%print%' THEN 1 else 0 END) AS PRINTING
FROM IPLEXSTY_AD_BOM_DTL BOM
WHERE CONTENT_CLASS = 'ART' --AND BOM.Ad_compo_desc IS NOT NULL
GROUP BY BOM.STYLE_ID,
BOM.SEASON_ID,BOM.CONTENT_CLASS,
BOM.Ad_compo_desc,BOM.CONTENT_CLASS
In your query, I found you used Distinct which is unnecessary because
you use aggregate function. And what you tried in else clause that is not clear

case statement doesn't go to else

I am wondering why the following query doesn't give 'N/A' when there are no rows for ENVIRON='Dev/Int'. It is returning null in the result of the query. I tried doing NVL(COUNT(*)) but that does't work either.
Any thoughts?
Thanks in advance.
SELECT G1.NAME,
(SELECT CASE
WHEN COUNT(*) > 0 AND ticket IS NOT NULL THEN 'Solved'
WHEN COUNT(*) > 0 AND ticket IS NULL THEN 'Done'
ELSE 'N/A'
END
FROM TABLE1
WHERE ENVIRON='Dev/Int' AND G1.NAME=NAME GROUP BY ENVIRON, ticket ) "Dev/Int"
FROM TABLE1 G1 group by G1.NAME
It doesn't give any rows because you are filtering them all out. The case is inside the query. When there are no rows to process, it returns NULL.
I think you just want conditional aggregation. The subqueries don't seem necessary:
SELECT G1.NAME,
(CASE WHEN SUM(CASE WHEN ENVIRON = 'Dev/Int' then 1 else 0 END) > 0 AND ticket IS NOT NULL
THEN 'Solved'
WHEN SUM(CASE WHEN ENVIRON = 'Dev/Int' then 1 else 0 END) > 0 AND ticket IS NULL
THEN 'Done'
ELSE 'N/A'
END) as "Dev/Int"
FROM TABLE1
group by G1.NAME;
EDIT:
Oops, the above left ticket out of the sum(). I think the logic you want has ticket in the sum() condition:
SELECT G1.NAME,
(CASE WHEN SUM(CASE WHEN ENVIRON = 'Dev/Int' AND ticket IS NOT NULL then 1 else 0 END) > 0
THEN 'Solved'
WHEN SUM(CASE WHEN ENVIRON = 'Dev/Int' AND ticket IS NULL then 1 else 0 END) > 0
THEN 'Done'
ELSE 'N/A'
END) as "Dev/Int"
FROM TABLE1
group by G1.NAME;
I'm surprised your original query worked at all and didn't get an error of the sort that subquery returned more than one row.

How to do a SUM() inside a case statement in SQL server

I want to add some calculation inside my case statement to dynamically create the contents of a new column but I get the error:
Column 'Test1.qrank' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
This is the code I'm working on
case
when test1.TotalType = 'Average' then Test2.avgscore
when test1.TotalType = 'PercentOfTot' then (cnt/SUM(test1.qrank))
else cnt
end as displayscore
I did try to group but it didn't work.
Any hints?
The error you posted can happen when you're using a clause in the GROUP BY statement without including it in the select.
Example
This one works!
SELECT t.device,
SUM(case when transits.direction = 1 then 1 else 0 end) ,
SUM(case when transits.direction = 0 then 1 else 0 end) from t1 t
where t.device in ('A','B') group by t.device
This one not (omitted t.device from the select)
SELECT
SUM(case when transits.direction = 1 then 1 else 0 end) ,
SUM(case when transits.direction = 0 then 1 else 0 end) from t1 t
where t.device in ('A','B') group by t.device
This will produce your error complaining that I'm grouping for something that is not included in the select
Please, provide all the query to get more support.
You could use a Common Table Expression to create the SUM first, join it to the table, and then use the WHEN to to get the value from the CTE or the original table as necessary.
WITH PercentageOfTotal (Id, Percentage)
AS
(
SELECT Id, (cnt / SUM(AreaId)) FROM dbo.MyTable GROUP BY Id
)
SELECT
CASE
WHEN o.TotalType = 'Average' THEN r.avgscore
WHEN o.TotalType = 'PercentOfTot' THEN pt.Percentage
ELSE o.cnt
END AS [displayscore]
FROM PercentageOfTotal pt
JOIN dbo.MyTable t ON pt.Id = t.Id
If you're using SQL Server 2005 or above, you can use the windowing function SUM() OVER ().
case
when test1.TotalType = 'Average' then Test2.avgscore
when test1.TotalType = 'PercentOfTot' then (cnt/SUM(test1.qrank) over ())
else cnt
end as displayscore
But it'll be better if you show your full query to get context of what you actually need.