SQL Optimization - Redundant Boolean Logic - sql

DB is Oracle 11g, if that matters. I have a query with a long where clause - consisting of three medium sized subblocks (that must all evaluate to true, obviously). However, for two of the subblocks, every single statement is just connected by AND. So, for each such subblock, the preceding AND, one opening parenthesis, and one closing parenthesis are redundant. would removing these improve the execution time of my query?
select * from tbl
where 1=1 AND (x OR y OR (w AND z))
AND (a AND b AND c)
AND (d AND e AND f);
versus
select * from tbl
where 1=1 AND (x OR y OR (w AND z))
AND a AND b AND c
AND d AND e AND f;
obviously these queries return the same records, but what is the difference in execution time?

It appears you have code something like:
where (a = 1 and b = 2 and c = 3)
and (d = 4 and e = 5 and f = 6)
and (g = 7 or h = 8 or i = 9)
In that case it is of course true that some of the parentheses are redundant i.e. it can be rewritten as:
where a = 1 and b = 2 and c = 3
and d = 4 and e = 5 and f = 6
and (g = 7 or h = 8 or i = 9)
However there will be no difference in performance between the two queries because they are semantically identical.

Related

A join that "allocates" from available rows

I have a table X I want to update according to the entries in another table Y. The join between them is not unique. However, I want each entry in Y to update a different entry in X.
So if I have table X:
i (unique) k v
---------- ---------- ----------
p 100 b
q 101 a
r 202 x
s 301 a
and table Y:
k (unique) v
---------- ----------
0 a
1 b
2 a
3 c
4 a
I want to end up with table X like:
i k v
---------- ---------- ----------
p 1 b
q 0 a
r 202 x
s 2 a
The important result here is that the two rows in X with v = 'a' have been updated to two distinct values of k from Y. (It doesn't matter which ones.)
Currently, this result is achieved by an extra column and a program roughly like:
UPDATE X SET X.used = FALSE;
for Yk, Yv in Y:
UPDATE X
SET X.k = Yk,
X.used = TRUE
WHERE X.i IN (SELECT X.i FROM X
WHERE X.v = Yv AND NOT X.used
LIMIT 1);
In other words, the distinctness is achieved by "using up" the rows in Y. This doesn't scale well.
(I'm using SQLite3 and Python, but don't let that limit you.)
This can be solved by using rowids to pair up the results of a join. Window functions aren't necessary. (Thanks to xQbert for pointing me in this direction.)
First, we sort the two tables by v to make tables with rowids in a suitable order for the join.
CREATE TEMPORARY TABLE Xv AS SELECT * FROM X ORDER BY v;
CREATE TEMPORARY TABLE Yv AS SELECT * FROM Y ORDER BY v;
Then we can pick out the minimum rowid for each value of v in order to create a "zip join" for that value, pairing up the rows.
SELECT i, Yv.k, Xv.v
FROM Xv JOIN Yv USING (v)
JOIN (SELECT v, min(Xv.rowid) AS r FROM Xv GROUP BY v) AS xmin USING (v)
JOIN (SELECT v, min(Yv.rowid) AS r FROM Yv GROUP BY v) AS ymin
ON ymin.v = Xv.v AND Xv.rowid - xmin.r = Yv.rowid - ymin.r;
The clause Xv.rowid - min.x = Yv.rowid - min.y is the trick: it does a pairwise match of rows with the same value of v, essentially allocating one to the other. The result:
i k v
---------- ---------- ----------
q 0 a
s 2 a
p 1 b
It's then a simple matter to use the result of this query in an UPDATE.
WITH changes AS (<the SELECT above>)
UPDATE X SET k = (SELECT k FROM changes WHERE i = X.i)
WHERE i IN (SELECT i FROM changes);
The temporary tables could be restricted to the common values of v and possibly indexed on v if the query is large.
I'd welcome refinements (or bugs!)

Querying speeds

In a large database, is it faster to perform a query such as
select * from table where a = 1 and b = 2 or b = 3 or b = 4;
or
select * from table where a = 1 and b = 2;
select * from table where a = 1 and b = 3;
select * from table where a = 1 and b = 4;
You should rely on the DBMS to do the optimisation for you. If the second were quicker, then the DBMS would do it that way anyway.
Go with the first (but put parentheses round the b conditions, as Tommy suggests).
As others have pointed out, the queries are not equivalent. I assume the first query should read:
select * from table where a = 1 and (b = 2 or b = 3 or b = 4);
For clarity I would suggest:
select * from table where a = 1 and b in (2, 3, 4);
In general this will perform better than asking three different queries as in the second alternative.

Order of operations for SQL?

Suppose I have this statement:
SELECT * FROM MyTable WHERE a = 1 or b = 2 and c = 3
Does that mean: (a = 1) OR (b = 2 AND c = 3) or does it mean (a = 1 or b = 2) AND c = 3? Can I change what it means, i.e. execute the OR before the AND or is this not possible?
From Technet:
When more than one logical operator is used in a statement, AND
operators are evaluated first. You can change the order of evaluation
by using parentheses.
So yes, it means (a = 1) OR (b = 2 AND c = 3).
You can force the behavior you want by writing the parentheses as you did above: (a = 1 OR b = 2) AND c = 3

Searching Multiple Rows at a time through a single SQL query

I have a table whose data is in this manner.
A B C
---------
0 6 2
0 3 4
1 0 2
1 1 4
I wrote a SQL query -
select A
from Table
where (B = 6 and C = 2) AND (B = 3 and C = 4).
Obviously it returned zero results since this query would search in the same row. Please help me with writing a better one to produce results such that it can check two rows with a single statement.
EDIT:
I am not looking for 'OR' statement. I need to find an element of A such that it has two corresponding rows AND each of the rows has elements 6,2 and 3,4 present in columns B,C correspondingly.
PS.
(I don't have the option of writing two queries and then finding the common elements of two set.)
Many thanks in advance
I guess you want something like this
select A
from YourTable
where (B = 6 and C = 2) or
(B = 3 and C = 4)
group by A
having count(distinct B) >= 2
Try here:
https://data.stackexchange.com/stackoverflow/q/123711/
Use OR instead of AND
select A from Table where (B=6 and C=2) OR (B=3 and C=4).
If you want the onlu result use DISTINCT
select DISTINCT A from Table where (B=6 and C=2) OR (B=3 and C=4).
If you need to check the equality of A, use this:
select t1.A
from Table t1
JOIN Table t2 ON t1.A = t2.A
where T1.B=6 and t1.C=2 AND t2.B=3 and t2.C=4
As you see - using AND again
Are you trying to get this??
SELECT A
FROM Table
WHERE (B = 6 AND C = 2) OR (B = 3 AND C = 4)
This would return the A column for all four rows again.
If not: WHAT exactly are you trying to select?
IF you want just two rows, one with A = 0, one with A = 1, then use DISTINCT:
SELECT DISTINCT A
FROM Table
WHERE (B = 6 AND C = 2) OR (B = 3 AND C = 4)
Maybe:
select A
from Table
where (B = 6 and C = 2)
INTERSECT
select A
from Table
(B = 3 and C = 4)

T-SQL AND logic

I have table TABLE1 with columns A, B and C. I need to get all rows from the table where columns A, B and C are not all equal to 1, e.g.,
WHERE NOT (A = 1 AND B = 1 AND C = 1)
This works. However, I need to do this in a fashion that only uses AND and OR statements. I had expected this to work:
WHERE A != 1
AND B != 1
AND C != 1
However, this only returns rows where no row = 1, i.e, too few rows.
Using MS SQL 2008.
WHERE (A <> 1 OR B <> 1 OR C <> 1)