sum in case vs case in sum in SQL server ? - sql-server

Is there any difference between writing :
select
...,
mySum= CASE
WHEN i IS NULL THEN 0 ELSE SUM(i)
END
...
vs
select
...,
mySum= SUM( CASE WHEN i IS NULL THEN 0 ELSE i
END
)
...
Or is it just 100% the same ( logically and traps...)?

On my test data, I get the same results returned, and near identical execution plans:
All the same costs, just two operations reversed.

I think logically there is no difference. But I think you don't need to check for null in this case as aggregate functions ignore nulls
BTW, You can simplify case with a isnull as follows. (Edit: mistakenly added sum(isnull(i,0)) changed to Isnull(sum(i),0))
select Col1, Isnull(sum(i),0) as mySum
from yourTable
group by Col1

Related

TSQL query optimizer view on non-nullable ISNULL()

As part of some dynamic SQL (ick), I've implemented the 'sort NULLs last' solution described here: Sorting null-data last in database query
ORDER BY CASE column WHEN NULL THEN 1 ELSE 0 END, column
My question is: On non-nullable columns that have ISNULL() applied to them, will the query optimizer strip this out when it realises that it will never apply?
It's not clear why your question mentions the ISNULL function when that isn't in your code.
ORDER BY CASE column WHEN NULL THEN 1 ELSE 0 END, column
First of all this code doesn't work, it is equivalent to CASE WHEN column = NULL which is not what you need.
It would need to be
ORDER BY CASE WHEN column IS NULL THEN 1 ELSE 0 END, column
The optimisation question is easy to test.
CREATE TABLE #T
(
X INT NOT NULL PRIMARY KEY
)
SELECT *
FROM #T
ORDER BY X
SELECT *
FROM #T
ORDER BY CASE WHEN X IS NULL THEN 1 ELSE 0 END, X
DROP TABLE #T
The plan shows a sort operation in the second plan indicating that this was not optimised out as you hoped and the pattern is less efficient than ORDER BY X.

Distinct count needed within case when statement

I am creating a query of all people who were screened for smoking status and need a count of unique patients. I am pulling from an encounter table, so the patient could have been asked multiple times. In my case when statement I would like to limit the "Then..." result to something like "Then count distinct patients" but it is giving me an error about aggregates not being allowed within an aggregate. If I remove it, it will then not produce a total as I wish and it's telling me I need it in the group by clause, which I do not want. limit is not an option in sql-server to the best of my knowledge
,count(case when soc.tobacco_user_c in (1, 2, 4, 5) and dmw.SMOKING_CESS_CNSL_YN ='y' then enc.PAT_ID **Here is where I want a unique count of patients** end) Compliant
You can combine DISTINCT with a CASE expression.
Example
SELECT
COUNT(DISTINCT CASE WHEN tobacco = 1 THEN PAT_ID ELSE NULL END)
...
;
I've abbreviated your example to make it easier to read. NULLs will not be included in the final count, so there is no need to worry about off by one errors.
case when soc.tobacco_user_c in (1, 2, 4, 5) and dmw.SMOKING_CESS_CNSL_YN ='y' then COUNT(DISTINCT enc.PAT_ID) ELSE 0 end Compliant
I ended up creating two subqueries and then doing a select count distinct on each of the max columns in those queries to limit the results to one

Iff condition Access to SQL syntax

I have an access query that I have recreate in SQL.
Access:
SELECT Columns ,
IIf([Col1]="X",IIf([COL2]<>"XXXX",1,0)) AS NEWCOL1,
IIf([COL2] Not In ("HHH","GGG"),1,0) AS [NEWCOL2],
IIf(([NEWCOL1]=1) Or ([NEWCOL2]=1),1,0) AS NEWCOL3
FROM [TABLE]
WHERE ((([TABLE].COL2)<>"XXXX")) OR ((([TABLE].COL2)<>"HHH" And ([TABLE].COL2)<>"GGG"));
In SQL :
SELECT Columns ,
"NEWCOL1" =
CASE WHEN ([COL1]='X' AND COL2<> 'XXXX') THEN 1
ELSE 0
END,
"NEWCOL2" =
CASE WHEN COL2 NOT IN ('HHH','GGG') THEN 1
ELSE 0
END ,
IIf(([NEWCOL1]=1) Or ([NEWCOL2]=1),1,0) AS NEWCOL3
FROM [TABLE]
WHERE ((([TABLE].COL2)<>'XXXX')) OR ((([TABLE].COL2)<>'HHH' And ([TABLE].COL2)<>'GGG'));
IIf(([NEWCOL1]=1) Or ([NEWCOL2]=1),1,0) AS NEWCOL3
When I use the Newcol1 and newcol2 it throws an error invalid column how could use them maybe in a nested case or iif statement
If you're in SQL 2012, where IIF() is valid, then it looks like the problem is that you don't have an ELSE value for the first outer case:
IIf([Col1]='X',IIf([COL2]<>'XXXX',1,0){,NEED SOME ELSE VALUE HERE}) AS NEWCOL1,
I don't know why this would work in Access. I guess Access must be more "dummy proof" than SQL Server.
To replace your original first IIF with a CASE, you would do this:
CASE WHEN [Col1]='X' THEN
CASE WHEN [COL2]<>'XXXX' THEN 1 ELSE 0 END
END
By not supplying an ELSE for the first condition, if [Col1] does not equal 'X', the statement will return NULL without raising an error.
To handle your most recent request with a CTE, you could do as below:
WITH cte AS (
SELECT
Columns,
CASE WHEN ([COL1]='X' AND COL2<> 'XXXX') THEN 1 ELSE 0 END AS NEWCOL1,
CASE WHEN COL2 NOT IN ('HHH','GGG') THEN 1 ELSE 0 END AS NEWCOL2
FROM [TABLE]
WHERE ((([TABLE].COL2)<>'XXXX')) OR ((([TABLE].COL2)<>'HHH' And ([TABLE].COL2)<>'GGG'))
)
SELECT *, IIf(([NEWCOL1]=1) Or ([NEWCOL2]=1),1,0) AS NEWCOL3
FROM cte;

Aggregate Function Error on an Expression

What could be wrong with this query:
SELECT
SUM(CASE
WHEN (SELECT TOP 1 ISNULL(StartDate,'01-01-1900')
FROM TestingTable
ORDER BY StartDate Asc) <> '01-01-1900' THEN 1 ELSE 0 END) AS Testingvalue.
The get the error:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
As koppinjo stated what your current (broken) query is doing is checking if you have a NULL-value (or StartDate = '01-01-1900') in your table, return either a 1 or a 0 depending on which, and then attempting to SUM that single value.
There are 2 different logical things you want.
Either getting the amount of rows that has a StartDate or checking if any row is missing StartDate.
SELECT --Checking if there is a NULL-value in table
(
CASE WHEN
(SELECT TOP 1 ISNULL(StartDate,'01-01-1900')
FROM TestingTable
ORDER BY StartDate Asc) <> '01-01-1900' THEN 1
ELSE 0
END
) AS TestingValue
SELECT SUM(TestingValue) TestingValue --Give the count of how many non-NULLs there is
FROM
(
SELECT
CASE WHEN
ISNULL(StartDate,'01-01-1900') <> '01-01-1900' THEN 1
ELSE 0
END AS TestingValue
FROM TestingTable
) T
Here is a SQL Fiddle showing both outputs side by side.
Hard to say, but you probably want something like this:
SELECT
SUM(TestingValue)
FROM
(SELECT
CASE
WHEN ISNULL(StartDate,'01-01-1900') <> '01-01-1900'
THEN 1
ELSE 0
END AS TestingValue
FROM TestingTable) t
As your original query is written now, your subquery will return 1 value overall, so your sum would be 1 or 0 always, not to mention it is illegal. To get around that, this SQL will apply the case statement to every row in the TestingTable and insert the result into a derived table (t), then the 'outer' select will sum the results. Hope this helps!

SQL Server 2005 Order BY with an expression

Is there a possibility to order the result by an ORDER clause that contains an expression, something like
SELECT colX0 FROM tbp_name ORDER BY (colX1 IS NOT NULL)
or also a more complex expression ?
UPDATE:
In the meanwhile I have found a possibility to solve the above problem:
ORDER BY (case WHEN colX1 IS NULL THEN 1 ELSE 0 END ) ASC
however the question remains, if there is a possibility to order direct by an expression.
No, SQL Server does not support direct conversion of an expression to true/false.
IMHO, one reason is the 3-valued logic. This has 3 outcomes, not 2, of either column is NULL. The NULL is first in SQL generally, always Server but can be specified last in other RDBMS.
ORDER BY (colX1 = colX2)
Using CASE mitigates this and removes ambiguity
ORDER BY
CASE
WHEN colX1 = colX2 THEN 1
WHEN colX1 <> colX2 THEN 2
ELSE 3 NULL case
END
You have to use CASE as per your update, as well ensuring datatypes match (or at least implicitly convertable) in WHEN clauses.
you can use
ORDER BY CASE WHEN condition= 1 THEN 1 ELSE 2 END
you can order by the ordinal position of the column, if you want to SEE the data that you're sorting by... for example, if you want to order by the 1st column, just say 'ORDER BY 1'. Here is an example where I add an expression in the select clause.. and then I order by it in the order by clause
SELECT colX0,
(case WHEN colX1 IS NULL THEN 1 ELSE 0 END )
FROM tbp_name
ORDER BY 2
You'd need to put it in your select first
SELECT
colX0,
CASE WHEN colX1 IS NOT NULL THEN 0 ELSE 1 END AS [COMPUTED1]
FROM tbp_name
ORDER BY COMPUTED1
It's something like that anyway off the top of my head.
http://www.tizag.com/sqlTutorial/sqlcase.php

Resources