I have SQL with params passed by user. User can pass some id or -1, what mean "nevermind".
It's possible to write query like below, but can using index?
SELECT *
FROM table
WHERE
((col1 = :param1) OR (:param1 = -1))
AND ((col2 = :param2) OR (:param2 = -1))
AND col3 STARTING 'A'
col1, col2 and col3 are indexed. In FB 2.x using OR never use index for col1 or col2. In FB 3 only if first (or second) conditions exist that can use index (PLAN index and natural), but with last condition stop using index for col1/2.
How can I write SQL without using PLAN and can use index for col1/2 and stay possibility to "off" condition by passing -1?
You can rewrite a condition like
(col1 = :param1) OR (:param1 = -1)
as
col1 = case when :param1 = -1 then col1 else :param1 end
Since it's a single expression, you can also safely delete all the brackets, which makes the query easier to read.
Related
I was trying to work with CASE WHEN in Postgresql in order to evaluate something and then do another thing. However, I need not only to check two things but they must be checked in consecutive order.
Example, let's say I have 3 columns: col1, col2 and col3.
I want to check first if col1 is greater than 0. After checking this I want to check if col2 is greater than 0. If that's the case, I will create another column which will be the sum of all of them. However, I can't do this:
select case when col1>0 and col2>0 then col1+col2+col3 end as...
I need to do something like this:
select case when col1>0 then (case when col2>0 then col1+col2+col3) else NULL end as...
But that doesn't work. So, what can I do?
You were close. You can do:
select
case when col1 > 0 then case when col2 > 0 then col1 + col2 + col3 end
else NULL end as my_column1
You had missed the inner end.
By the way, a CASE expression evaluates to NULL when no when clause is matched. Therefore, else NULL is redundant.
Use case: I am going to be using SQL Server to retrieve values from a large table (1,000,000+ rows) where many different columns can be used as filter criteria, some more frequently used than others.
Questions
Would it be faster to utilize short-circuiting in the WHERE clause so that less comparisons are done?
Should the most commonly used criteria be filtered first to do even less comparisons?
Should the most commonly used criteria be indexed?
Example
No short circuiting
SELECT value
FROM AssignmentTable
WHERE (criteriaOne = <criteriaOneValue> OR criteriaOne IS NULL)
AND (criteriaTwo = <criteriaTwoValue> OR criteriaTwo IS NULL)
AND (criteriaThree = <criteriaThreeValue> OR criteriaThree IS NULL)
AND ... for all criteria (roughly 15)
With short circuiting
SELECT value
FROM AssignmentTable
WHERE 1 =
CASE
WHEN (criteriaOne = <criteriaOneValue> OR criteriaOne IS NULL) THEN
CASE
WHEN (criteriaTwo = <criteriaTwoValue> OR criteriaTwo IS NULL) THEN
CASE
WHEN (criteriaThree = <criteriaThreeValue> OR criteriaThree IS NULL) THEN 1
ELSE 0
END
ELSE 0
END
ELSE 0
END
The pattern for doing this without dynamic SQL in SQL Server is to use OPTION (RECOMPILE) to prune the un-needed predicates before the query optimizer generates a query plan.
EG:
SELECT value
FROM AssignmentTable
WHERE (Column1 = #column1 OR #column1 IS NULL)
AND (Column2 = #column2 OR #column2 IS NULL)
AND (Column3 = #column3 OR #column3 IS NULL)
AND ... for all criteria (roughly 15)
OPTION (RECOMPILE)
See the classic Dynamic Search Conditions in T-SQL for a complete discussion of the alternatives.
We have some large data warehouse tables. Our transformation code makes sure all the numeric measure columns use zero instead of NULL for T-SQL math.
(CASE WHEN NULL or '' then 0)
In the last transformation section we need to turn all the zeros back to NULL for use in Analysis Services MDX. Trying to make sure the No Data lines are not showing in the reports. Previously set to 0 for SQL math because 1 + NULL = NULL in SQL and we want it to equal 1
where as 1 + NULL in Analysis Services = 1.
It is taking about two hours to do the back conversion with individual set statements for each column/measure. I am looking for a way to do it in one read through the table/file instead of thirty-three table scans.
Will the following work and will it be faster?
UPDATE MyTable
SET Col1 = NULLIF(Col1, 0),
Col2 = NULLIF(Col2, 0),
Col3 = NULLIF(Col3, 0)
WHERE (Col1 = 0
OR Col2 = 0
OR Col3 = 0)
How do you know that it will not be fast enough? did you do any bench marking? You can as well use CASE condition like below but per MSDN Spec both are same. Which states that:
NULLIF is equivalent to a searched CASE expression in which the two
expressions are equal and the resulting expression is NULL.
UPDATE MyTable
SET Col1 = CASE WHEN Col1 = 0 THEN NULL END,
Col2 = CASE WHEN Col2 = 0 THEN NULL END,
Col3 = CASE WHEN Col3 = 0 THEN NULL END
WHERE (Col1 = 0 OR Col2 = 0 OR Col3 = 0)
I'm doing a bunch of sum queries: SELECT col1 + col2 + col3 + ...
Some of the values in some of the columns are null. I'm checking for them by doing
SELECT CASE WHEN col1 is not null and col2 is not null and ...
I'm wondering if there is a more concise syntax for accomplishing this task.
Well, since the sum of any number and null is null, you can do something like the following (with the obvious ... filled in):
select big_honking_sum
from(
select col1+col2+col3+...+coln as big_honking_sum
from some_table
)sum
where big_honking_sum is not null;
Use COALESCE function if you can accept that a NULL value will be treated as 0.
SELECT COALESCE(Col1,0)
+ COALESCE(Col2,0)
+ COALESCE(Col3,0)
AS TOTAL FROM table;
Perhaps you could consider using 0 as default value instead of NULL if applicable.
You can have that even simpler:
SELECT foo
FROM ...
WHERE (-1 IN (col1, col2, col3)) IS NOT NULL
The IN expression will return NULL if (and only if) there is at least one NULL value among the tested values (and no match). So the whole expression evaluates to TRUE if (and only if) there is no NULL.
Edit: I have to correct myself! A positive match would stop the evaluation and return TRUE, even if a NULL is among the values. So you need a value that is guaranteed not to be in the set. Like 0 where all values are > 0 or -1 where all values are positive or you cannot use this expression for the purpose.
In SQL:
I want to check. From single table,
if(field1 = 1 and DATE_SUB(CURDATE(),INTERVAL 7 DAY) <= field2) then
return count(*)
else
return "false"
thanks
v.srinath
SELECT CASE WHEN (field1 = 1 AND DATE_SUB(CURDATE(),INTERVAL 7 DAY) <= field2)
THEN COUNT(*)
ELSE 0
END
FROM SomeTable
CASE
WHEN field1 = 1
AND DATE_SUB(CURDATE(),INTERVAL 7 DAY) <= field2
THEN COUNT(*)
ELSE 'false'
END
Note that this only works in the SELECT list, or HAVING clause, and you should have a GROUP BY clause on field1 and field2. The reason is that COUNT() is an aggregate function. Because field1 and field2 appear outside an aggregatee function, aggregation for those fields must be enforced with a GROUP BY clause. Most RDBMS-es won't allow you to write this unless there is an appropriate GROUP BY clause, but MySQL will, and will silently return rubbish for those fields.
BTW - I think you should probably not write "false" but simply FALSE - right now both legs of the choice have a different type (boolean vs string). The db will probably coerce types, but it is still bad practice. You should ensure that the expression for each brach of the CASE expression have the same data type.