Case expression degrading performance in Correlated Sub query - sql

SUM((CASE WHEN ([t2].[__measure__8] > 1) THEN (CASE WHEN [t2].[__measure__8] = 0 THEN NULL ELSE (CAST([t2].[__measure__9] as float) / [t2].[__measure__8]) END) ELSE [t2].[__measure__9] END))
when I use this calculation above Correlated Subquery which has a group by too . Its is taking more than 5 minutes to receive the result set. Moreover, the inner subset of query retrieves in just 4 sec. Am I missing something else?
What is the best way to write this case expression?

Sub case statement looks like unnecessary. You can remove CASE WHEN [t 2].[_ measure _8] = 0
SUM(CASE
WHEN ([t2].[__measure__8] > 1) THEN (CAST([t2].[__measure__9] as float) / [t2].[__measure__8])
ELSE [t2].[__measure__9]
END )

Since you only have 1 condition on your case statement, I suggest to use iif function instead.
sum(
iif([t2].[__measure__8] > 1,
iif([t2].[__measure__8] = 0, null, cast([t2].[__measure__9] as float)/[t2].[__measure__8]),
[t2].[__measure__9])
)
Try to break down your query by using subqueries, remove the sum() first then check the improvements.

Related

how to use is null with case statements

Hi can any one say me how to write query in sql server management studio for statement =>if(isnull("GROSS_SF")=1 or "GROSS_SF"=0,0,"GROSS_SALES"/"GROSS_SF")
by using case statement :
CASE
WHEN ("GROSS_SF"=1 or "GROSS_SF"=0) isnull then 0
else "GROSS_SALES"/"GROSS_SF"
end/* i am getting error if i write it like this */
thanks in advance
If you want to avoid a divide by zero, while at the same time also handling NULL, then you may try the following logic:
CASE WHEN COALESCE(GROSS_SF, 0) = 0
THEN 0
ELSE GROSS_SALES / GROSS_SF END AS output
To be clear, the above logic would return a zero value should either the GROSS_SF be zero or NULL.
I think what are you looking for is IFNULL, look here https://www.w3schools.com/sql/sql_isnull.asp
You are close. You can do:
(CASE WHEN "GROSS_SF" IS NULL OR "GROSS_SF" = 0 THEN 0
ELSE "GROSS_SALES" / "GROSS_SF"
END)
In SQL, divide by zero errors are often avoided using NULLIF():
"GROSS_SALES" / NULLIF("GROSS_SF", 0)
However, that returns NULL rather than 0. If you really want zero:
COALESCE("GROSS_SALES" / NULLIF("GROSS_SF", 0), 0)
This is a bit shorter to write and almost equivalent to your version (this returns 0 if "GROSS_SALES" is NULL, which your version does not).
You can use NULLIF() to prevent arithmetic divide by zero errors :
select . . . ,
GROSS_SALES / nullif(GROSS_SF, 0)
from table t;
However, this would return null instead of error, if you want to display 0 instead then you need to use isnull() (MS SQL Specific) or coalesce() (SQL Standard).
So, you can express it :
select . . . ,
isnull(GROSS_SALES / nullif(GROSS_SF, 0), 0)
from table t;

Switching fields in WHERE clause SQL 2005 [duplicate]

I am creating a SQL query in which I need a conditional where clause.
It should be something like this:
SELECT
DateAppr,
TimeAppr,
TAT,
LaserLTR,
Permit,
LtrPrinter,
JobName,
JobNumber,
JobDesc,
ActQty,
(ActQty-LtrPrinted) AS L,
(ActQty-QtyInserted) AS M,
((ActQty-LtrPrinted)-(ActQty-QtyInserted)) AS N
FROM
[test].[dbo].[MM]
WHERE
DateDropped = 0
--This is where i need the conditional clause
AND CASE
WHEN #JobsOnHold = 1 THEN DateAppr >= 0
ELSE DateAppr != 0
END
The above query is not working. Is this not the correct syntax or is there another way to do this that I don't know?
I don't want to use dynamic SQL, so is there any other way or do I have to use a workaround like using if else and using the same query with different where clauses?
Try this
SELECT
DateAppr,
TimeAppr,
TAT,
LaserLTR,
Permit,
LtrPrinter,
JobName,
JobNumber,
JobDesc,
ActQty,
(ActQty-LtrPrinted) AS L,
(ActQty-QtyInserted) AS M,
((ActQty-LtrPrinted)-(ActQty-QtyInserted)) AS N
FROM
[test].[dbo].[MM]
WHERE
DateDropped = 0
AND (
(ISNULL(#JobsOnHold, 0) = 1 AND DateAppr >= 0)
OR
(ISNULL(#JobsOnHold, 0) != 1 AND DateAppr != 0)
)
You can read more about conditional WHERE here.
Try this one -
WHERE DateDropped = 0
AND (
(ISNULL(#JobsOnHold, 0) = 1 AND DateAppr >= 0)
OR
(ISNULL(#JobsOnHold, 0) != 1 AND DateAppr != 0)
)
To answer the underlying question of how to use a CASE expression in the WHERE clause:
First remember that the value of a CASE expression has to have a normal data type value, not a boolean value. It has to be a varchar, or an int, or something. It's the same reason you can't say SELECT Name, 76 = Age FROM [...] and expect to get 'Frank', FALSE in the result set.
Additionally, all expressions in a WHERE clause need to have a boolean value. They can't have a value of a varchar or an int. You can't say WHERE Name; or WHERE 'Frank';. You have to use a comparison operator to make it a boolean expression, so WHERE Name = 'Frank';
That means that the CASE expression must be on one side of a boolean expression. You have to compare the CASE expression to something. It can't stand by itself!
Here:
WHERE
DateDropped = 0
AND CASE
WHEN #JobsOnHold = 1 AND DateAppr >= 0 THEN 'True'
WHEN DateAppr != 0 THEN 'True'
ELSE 'False'
END = 'True'
Notice how in the end the CASE expression on the left will turn the boolean expression into either 'True' = 'True' or 'False' = 'True'.
Note that there's nothing special about 'False' and 'True'. You can use 0 and 1 if you'd rather, too.
You can typically rewrite the CASE expression into boolean expressions we're more familiar with, and that's generally better for performance. However, sometimes is easier or more maintainable to use an existing expression than it is to convert the logic.
The problem with your query is that in CASE expressions, the THEN and ELSE parts have to have an expression that evaluates to a number or a varchar or any other datatype but not to a boolean value.
You just need to use boolean logic (or rather the ternary logic that SQL uses) and rewrite it:
WHERE
DateDropped = 0
AND ( #JobsOnHold = 1 AND DateAppr >= 0
OR (#JobsOnHold <> 1 OR #JobsOnHold IS NULL) AND DateAppr <> 0
)
Often when you use conditional WHERE clauses you end upp with a vastly inefficient query, which is noticeable for large datasets where indexes are used. A great way to optimize the query for different values of your parameter is to make a different execution plan for each value of the parameter. You can achieve this using OPTION (RECOMPILE).
In this example it would probably not make much difference, but say the condition should only be used in one of two cases, then you could notice a big impact.
In this example:
WHERE
DateDropped = 0
AND (
(ISNULL(#JobsOnHold, 0) = 1 AND DateAppr >= 0)
OR
(ISNULL(#JobsOnHold, 0) <> 1 AND DateAppr <> 0)
)
OPTION (RECOMPILE)
Source Parameter Sniffing, Embedding, and the RECOMPILE Options
This seemed easier to think about where either of two parameters could be passed into a stored procedure. It seems to work:
SELECT *
FROM x
WHERE CONDITION1
AND ((#pol IS NOT NULL AND x.PolicyNo = #pol) OR (#st IS NOT NULL AND x.State = #st))
AND OTHERCONDITIONS

Conditional WHERE clause in SQL Server

I am creating a SQL query in which I need a conditional where clause.
It should be something like this:
SELECT
DateAppr,
TimeAppr,
TAT,
LaserLTR,
Permit,
LtrPrinter,
JobName,
JobNumber,
JobDesc,
ActQty,
(ActQty-LtrPrinted) AS L,
(ActQty-QtyInserted) AS M,
((ActQty-LtrPrinted)-(ActQty-QtyInserted)) AS N
FROM
[test].[dbo].[MM]
WHERE
DateDropped = 0
--This is where i need the conditional clause
AND CASE
WHEN #JobsOnHold = 1 THEN DateAppr >= 0
ELSE DateAppr != 0
END
The above query is not working. Is this not the correct syntax or is there another way to do this that I don't know?
I don't want to use dynamic SQL, so is there any other way or do I have to use a workaround like using if else and using the same query with different where clauses?
Try this
SELECT
DateAppr,
TimeAppr,
TAT,
LaserLTR,
Permit,
LtrPrinter,
JobName,
JobNumber,
JobDesc,
ActQty,
(ActQty-LtrPrinted) AS L,
(ActQty-QtyInserted) AS M,
((ActQty-LtrPrinted)-(ActQty-QtyInserted)) AS N
FROM
[test].[dbo].[MM]
WHERE
DateDropped = 0
AND (
(ISNULL(#JobsOnHold, 0) = 1 AND DateAppr >= 0)
OR
(ISNULL(#JobsOnHold, 0) != 1 AND DateAppr != 0)
)
You can read more about conditional WHERE here.
Try this one -
WHERE DateDropped = 0
AND (
(ISNULL(#JobsOnHold, 0) = 1 AND DateAppr >= 0)
OR
(ISNULL(#JobsOnHold, 0) != 1 AND DateAppr != 0)
)
To answer the underlying question of how to use a CASE expression in the WHERE clause:
First remember that the value of a CASE expression has to have a normal data type value, not a boolean value. It has to be a varchar, or an int, or something. It's the same reason you can't say SELECT Name, 76 = Age FROM [...] and expect to get 'Frank', FALSE in the result set.
Additionally, all expressions in a WHERE clause need to have a boolean value. They can't have a value of a varchar or an int. You can't say WHERE Name; or WHERE 'Frank';. You have to use a comparison operator to make it a boolean expression, so WHERE Name = 'Frank';
That means that the CASE expression must be on one side of a boolean expression. You have to compare the CASE expression to something. It can't stand by itself!
Here:
WHERE
DateDropped = 0
AND CASE
WHEN #JobsOnHold = 1 AND DateAppr >= 0 THEN 'True'
WHEN DateAppr != 0 THEN 'True'
ELSE 'False'
END = 'True'
Notice how in the end the CASE expression on the left will turn the boolean expression into either 'True' = 'True' or 'False' = 'True'.
Note that there's nothing special about 'False' and 'True'. You can use 0 and 1 if you'd rather, too.
You can typically rewrite the CASE expression into boolean expressions we're more familiar with, and that's generally better for performance. However, sometimes is easier or more maintainable to use an existing expression than it is to convert the logic.
The problem with your query is that in CASE expressions, the THEN and ELSE parts have to have an expression that evaluates to a number or a varchar or any other datatype but not to a boolean value.
You just need to use boolean logic (or rather the ternary logic that SQL uses) and rewrite it:
WHERE
DateDropped = 0
AND ( #JobsOnHold = 1 AND DateAppr >= 0
OR (#JobsOnHold <> 1 OR #JobsOnHold IS NULL) AND DateAppr <> 0
)
Often when you use conditional WHERE clauses you end upp with a vastly inefficient query, which is noticeable for large datasets where indexes are used. A great way to optimize the query for different values of your parameter is to make a different execution plan for each value of the parameter. You can achieve this using OPTION (RECOMPILE).
In this example it would probably not make much difference, but say the condition should only be used in one of two cases, then you could notice a big impact.
In this example:
WHERE
DateDropped = 0
AND (
(ISNULL(#JobsOnHold, 0) = 1 AND DateAppr >= 0)
OR
(ISNULL(#JobsOnHold, 0) <> 1 AND DateAppr <> 0)
)
OPTION (RECOMPILE)
Source Parameter Sniffing, Embedding, and the RECOMPILE Options
This seemed easier to think about where either of two parameters could be passed into a stored procedure. It seems to work:
SELECT *
FROM x
WHERE CONDITION1
AND ((#pol IS NOT NULL AND x.PolicyNo = #pol) OR (#st IS NOT NULL AND x.State = #st))
AND OTHERCONDITIONS

CASE equivalent of a nested IIF statement

Can anyone please decode the following nested IIF to a CASE statement in SQL.. I know IIF is allowed in SQL Server 2012 but I find it hard to get an easy grasp of a nested IIF logic.. following is my nested IIF statement
IIF(IIF(TABLE_A.Col_1 = 0, TABLE_A.Col_2 + (2*TABLE_A.Col_3), TABLE_A.Col_1)<=.5, 'A', 'B') AS Result
Any help is much appreciated.
This should be the equivalent:
CASE
WHEN
CASE
WHEN TABLE_A.Col_1 = 0
THEN TABLE_A.Col_2 + (2*TABLE_A.Col_3)
ELSE TABLE_A.Col_1
END <= .5
THEN 'A'
ELSE 'B'
END As Result
CASE
WHEN
(CASE
WHEN TABLE_A.Col1= 0
THEN TABLE_A.Col2_2 + (2*TABLE_A.Col3)
ELSE TABLE_A.Col1
END) <=0.5
THEN 'A'
ELSE 'B'
END
AS result
I think this is what it boils down to in one CASE expression:
CASE
WHEN TABLE_A.Col_1 = 0 AND TABLE_A.Col_2 + (2*TABLE_A.Col_3) <= .5 THEN 'A'
WHEN TABLE_A.Col_1 <> 0 AND TABLE_A.Col_1 <= .5 THEN 'A'
ELSE 'B'
END
This is old now, and there are other answers that already work, but for fun it is possible to write this as a functional expression without any CASE statements at all, like this:
char(65 + ceiling(ceiling(COALESCE(NULLIF(TABLE_A.Col_1, 0), TABLE_A.Col_2 + (2*TABLE_A.Col_3))) - .5 / 10000000000000))
There is a very small chance that the functional approach will perform noticeably better on large sets with good indexing.
Here's my proof-of-concept test script:
http://sqlfiddle.com/#!3/a95b3/2

Specify order of (T)SQL execution

I have seen similar questions asked elsewhere on this site, but more in the context of optimization.
I am having an issue with the order of execution of the conditions in a WHERE clause. I have a field which stores codes, most of which are numeric but some of which contain non-numeric characters. I need to do some operations on the numeric codes which will cause errors if attempted on non-numeric strings. I am trying to do something like
WHERE isnumeric(code) = 1
AND CAST(code AS integer) % 2 = 1
Is there any way to make sure that the isnumeric() executes first? If it doesn't, I get an error...
Thanks in advance!
The only place order of evaluation is guaranteed is CASE
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS integer) % 2
END = 1
Also just because it passes the isnumeric test doesn't guarantee that it will successfully cast to an integer.
SELECT ISNUMERIC('$') /*Returns 1*/
SELECT CAST('$' AS INTEGER) /*Fails*/
Depending upon your needs you may find these alternatives preferable.
Why not simply do it using LIKE?:
Where Code Not Like '%[^0-9]%'
Btw, either using my solution or using IsNumeric, there are some edge cases which might lead one to using a UDF such as 1,234,567 where IsNumeric will return 1 but Cast will throw an exception.
Why not use a CASE statement to say something like:
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS int) % 2 = 1
ELSE /* What ever else if not numeric */ END
You could do it in a case statement in the select clause, then limit by the value in an outer select
select * from (
select
case when isNum = 1 then CAST(code AS integer) % 2 else 0 end as castVal
from (
select
Case when isnumeric(code) = 1 then 1 else 0 end as isNum
from table) t
) t2
where castval = 1