Does MySQL "lazy evaluate" when having queries inside IF (conditional) statements? [duplicate] - mysql

I need to query data from a second table, but only if a rare set of conditions in the primary table is met:
SELECT ..., IF(a AND b AND c AND (SELECT 1 FROM tableb ...)) FROM tablea ...
a, b, and c conditions are almost always false, so my thinking is the subquery will never execute for most rows in the result set and thus be way faster than a join. But that would only true if the IF() statement short circuits.
Does it?
Thanks for any help you guys can provide.

The answer is YES.
The IF(cond,expr_true,expr_false) within a mysql query is short-circuited.
Here a test, using #variables to prove the fact:
SET #var:=5;
SELECT IF(1 = 0, (#var:=#var + 1), #var ); -- using ':=' operator to modify 'true' expr #var
SELECT IF(1 = 1, #var, (#var:=#var + 1) ); -- using ':=' operator to modify 'false' expr #var
SELECT #var;
The result is '5' from all three SELECT queries.
Had the IF() function NOT short circuited, the result would be a '5' from SELECT #1, and '6' from SELECT #2, and a '7' from the last "select #var".
This is because the 'true' expression is NEVER executed, in select #1 and nor is the false expression executed for select #2.
Note the ':=' operator is used to modify an #var, within an SQL query (select,from, and where clauses). You can get some really fancy/complex SQL from this. I've used #vars to apply 'procedural' logic within a SQL query.
-- J Jorgenson --

With J. Jorgenson's help I came up with my own test case. His example does not try to short circuit in the condition evaluation, but using his idea I came up with my own test and verified that MySQL does indeed short-circuit the IF() condition check.
SET #var:=5;
SELECT IF(1 = 0 AND (#var:=10), 123, #var); #Expected output: 5
SELECT IF(1 = 1 AND (#var:=10), #var, 123); #Expected output: 10
On the second example, MySQL is properly short-circuiting: #var never gets set to 10.
Thanks for the help J. Jorgenson!

It depends.
IF doesn't short-circuit such that it can be used to avoid truncation warnings with GROUP_CONCAT, for example in:
set ##group_concat_max_len = 5;
select if(true or #var:=group_concat('warns if evaluated'), 'actual result', #var);
the result will be 'actual result' but you'll get a warning:
Warning (Code 1260): Row 1 was cut by GROUP_CONCAT()
which is the same warning you get with less trivial GROUP_CONCAT expressions, such as distinct keys, and without the IF at all.

Try it in the SQL analyzer. If you want to be on the safe side and not have to trust the database to work one way (and not to change that behavior ever in new versions), just make two queries and do the IF programmatically.

Related

SQL WHERE condition, why the result using 'a != 7 and b !=836' is not equal ' !(a=7 and b=836)'?

There are three records like:
a=7 , b=836
a=8 , b=836
a=7 , b=839
I want to get the result without (a=7 and b=836).
the result is empty when execute the sql like select * from test where a!=7 and b!=836,
but it got the correct result by using select * from test where a<>7 or b<>836.
I have two questions:
Why 'a!=7 and b!=836' not equal !(a=7 and b=836)?
What's the different between the two conditions ?
'a!=7 and b!=836' and 'a<>7 or b<>836'
The difference is in your logic operators, and and or, in combination with parentheses.
select * from test where a!=7 and b!=836
This query would select where BOTH of your statements return true, that is both a is not 7, and b is not 836. This would return nothing, there is no record where both of these are true.
select * from test where !(a=7 and b=836)
When you put the parentheses around your and, you move the logic around. That statement means that all records NOT matching any record where both a is 7 and b is 836. So inside the parenthesis it matches the first record, then it inverts that selection.
select * from test where a<>7 or b<>836
The <> is the same as != (Link to documentation). But in this query you use or. So it will match any record where a is not 7, AND any record that is not 836. So would match both second and third row.
For more reading material, take a look at the De Morgan's Laws. !(a and b) equals !a or !b. More explanation here and here.
Actually != and <> exactly the same. See the documentation.
<> is sql standard, != non-standard.
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#operator_not-equal

IF STATEMENT IN MYSQL INSERT is not working

Please, I do not understand why it is not working:
SET #key = (SELECT customer.key_stamp FROM customer WHERE customer.key_stamp = "0000");
CASE WHEN (#key > 0) THEN
INSERT INTO transaction
(transaction.to, transaction.key, transaction.type, transaction.cost_bitcoin, transaction.quantity)
VALUES ("0000", "f5rwsd", 2, 0.0075,
(500000 +
(
(SELECT bonus.amount
FROM bonus
WHERE 500000 BETWEEN bonus.min_quantity AND bonus.max_quantity
) / 500000
) * 100))
END;
I tried the CASE STATEMENT, but it still does not work, and i can't understand the issue.
Please help.
CASE is not a valid MySQL statement. (It's not a valid statement outside the context of a MySQL stored program.)
"Why it is not work"... is because it's not valid SQL.
A CASE expression can be used in a SQL statement where an expression is allowed. (In SQL, an expression returns a value of a particular datatype.)
As an example of using a CASE expression in a SQL statement, something like this:
SELECT CASE WHEN #key > 0 THEN 'somevalue' ELSE 'othervalue' END AS foo
in fact now i use just an sql functions, and it work now, all work, switch case mysql, if else statement mysql, and loops too, like while do, or LOOP ITERATE, all work, just need use it in sql functions, or sql procedure.

Syntax Error While Using Case Statement in Spark SQL

I want to run a case statement that runs different SELECT Statements Based on a condition in Spark SQL but not able to get the Syntax Right .
My SQL statement looks like this
registerTable(sql="SELECT CASE WHEN typedKeyword > '' THEN (SELECT * FROM `temp.sdf0` WHERE originalKeyword > ''AND keyword > '' AND deviceType = 'devicetype' ) ELSE (SELECT * FROM `temp.tes` WHERE originalKeyword > ''AND keyword > '' ) END ",alias="temp.test")
I don't know if CASE statement is supported in spark SQL so How can one achieve this
I've worked in nearly a dozen SQL and SQL-like dialects and I've never seen syntax like that. It looks like there's a core misunderstanding of what SQL clauses are supposed to do what.
The SELECT clause is for describing a projection of scalar elements. In many dialects you can issue subqueries - sometimes even correlated subqueries - in this clause, but you always must apply an operator that converts the result into a scalar value. For example:
SELECT (CASE WHEN EXISTS (SELECT foo FROM tbl) THEN 1 ELSE 0 END)
Here the "EXISTS" operator converts the uncorrelated subquery into a scalar boolean, so it's legal.
The lack of a FROM clause in the top level query is also a big red flag. It, or some analogue like Oracle's "dual", is legal in about half of the dialects I've seen, but if what you're trying to do is dynamically switch between two queries, you're almost certainly doing it wrong. juergen is right - what you probably meant to do is use 2 different queries.

Is there a special character in mySql that would return always true in WHERE clauses?

Is there a character, say, $,
SELECT * FROM Persons WHERE firstName='Peter' AND areaCode=$;
such that the statement would return the same as
SELECT * FROM Persons WHERE firstName='Peter'
i.e. areaCode=$ would always return always true and, thus, effectively “turns of” the criteria areaCode=...
I’m writing a VBA code in Excel that fetches some rows based on a number of criteria. The criteria can either be enabled or disabled. A character like $ would make the disabling so much easier.
instead of disabling it, pass it through to your query as NULL and use COALESCE:
SELECT *
FROM Persons
WHERE firstName='Peter'
AND areaCode = COALESCE(<your parameter>, areaCode);
%
See Wildcards
You could use NULL for this purpose:
AND (areaCode = ? OR ? IS NULL)
I think you could use something like
SELECT * FROM Persons WHERE firstName=firstName
of course without quotes
From your question I assume that you actually want the ability to include or exclude the where clause, in which case you need to use or.
SELECT *
FROM Persons
WHERE ( 1 = 2
OR ( firstName = 'Peter'
AND < more conditions if needed >
)
)
In this example 1 <> 2 so the only condition evaluated is firstName = 'Peter'. If you then want to ignore the where clause you change 2 to 1. As 1 = 1 this is evaluated for every row and the rest of the conditions will be ignored.

IsNumeric in SQL Server JOIN

My problem seems to be very simple but I'm stuck here. I have a table which has an "nvarchar" column called "SrcID" and I store both numbers and strings in that. Now, when I try to check for "IsNumeric" on that column in a "Join" condition, something like below,
ISNUMERIC(SrcID) = 1 AND SrcID > 15
I am getting the following error:
Msg 245, Level 16, State 1, Line 47
Conversion failed when converting the nvarchar value 'Test' to data type int.
Amazingly, when I remove the check "SrcID > 15", my query is running properly. Should I include anything else in this statement?
Please help me in fixing the issue. Thanks in advance!!
You can't count on the order in which a database will evaluate filtering expressions. There is a query optimizer that will evaluate your SQL and build a plan to execute the query based on what it perceives will yield the best performance.
In this context, IsNumeric() cannot be used with an index, and it means running a function against every row in the table. Therefore, it will almost never provide the best perceived performance. Compare this with the SrcID > 15 expression, which can be matched with an index (if one exists), and is just a single operator expression even if one doesn't. It can also be used to filter down the number of potential rows where the IsNumeric() function needs to run.
You can likely get around this with a view, a subquery, a CTE, a CASE statement, or a computed column. Here's a CTE example:
With NumericOnly As
(
SELECT <columns> FROM MyTable WHERE IsNumeric(SrcID) = 1
)
SELECT <columns> FROM NumericOnly WHERE SrcID > 15
And here's a CASE statement option:
SELECT <columns> FROM MyTable WHERE CASE WHEN IsNumeric(SrcIC) = 1 THEN Cast(SrcID As Int) ELSE 0 END > 15
The filters in a WHERE clause are not evaluated in any particular order.
This is a common misconception with SQL Server - the optimizer will check whichever conditions it thinks it can the fastest/easiest, and try to limit the data in the most efficient way possible.
In your example, you probably have an index on SrcID, and the optimizer thinks it will be quicker to FIRST limit the results to where the SrcID > 15, then run the function on all those rows (since the function will need to check every single row otherwise).
You can try to force an order of operations with parentheses like:
WHERE (ISNUMERIC(SrcID) = 1) AND SrcID > 15
Or with a case statement:
WHERE CASE WHEN ISNUMERIC(SrcID) = 1 THEN SrcID > 15 ELSE 1=0 END