In SQL, what does using parentheses with an OR mean? - sql

Example:
select count(*) from my table
where
column1 is not null
and
(column1 = 4 OR column1 = 5)
Example 2:
select count(*) from my table
where
column1 is not null
and
column1 = 4 OR column1 = 5
In my database with the real column names, I get two different results. The one with the parentheses is right because if I do:
select count(*) from my table
where
column1 is not null
and
column1 = 4
and then
select count(*) from my table
where
column1 is not null
and
column1 = 5
and add them together, I get the right answer...I think. Same as the first example with the parentheses above.
Why do I get different results by changing precedence with the OR test?

It's not Oracle or SQL. It's basic boolean logic. The AND condition is "stronger" (has precedence) than OR, meaning it will be evaluated first:
column1 is not null
and
column1 = 4 OR column1 = 5
Means
column1 is not null
and
column1 = 4
is evaluated first, then OR is applied between this and column1 = 5
Adding parentheses ensures OR is evaluated first and then the AND.
Pretty much like in maths:
2 * 3 + 5 = 6 + 5 = 11
but
2 * (3 + 5) = 2 * 8 = 16
More reading here: http://msdn.microsoft.com/en-us/library/ms190276.aspx

This comes down to whether your expression is parsed as:
(column1 is not null and column1 = 4) OR column1 = 5
or
column1 is not null and (column1 = 4 OR column1 = 5)
See the difference?

Parenthesis matter, (A AND B) OR C ≠ A AND (B OR C)
just like in math: (0 * 1) + 2 ≠ 0 * (1 + 2)
However, you can choose not to use parenthesis : SQL doesn't have operator precedence rules, so it strictly evaluates expressions from left to right. For instance:
true OR false AND false
is false, just like
(true OR false) AND false
while
true OR (false AND false)
is true.

Related

Excluding a value when null is present in the column

I want to filter the table without the row c
column 1
column 2
a
100
b
200
c
50
null
200
Desired output
column 1
column 2
a
100
b
200
null
200
I tried
select *
from table
where column1 <> 'c'
But since I can compare with null, I'm getting the wrong output. How do I deal with this?
You need to handle the null as follows:
select * from table where column1 <> 'c' or column1 is null
Or you can use the coalesce function as follows:
select * from table where coalesce(column1,'cc') <> 'c'
Coalesce will replace the null value in column1 with the value provided as the second argument. I have used the value which is not equal to 'c' so records with column1 as null will pass this condition
ANSI SQL, DISTINCT predicate.
select *
from table
where column1 is distinct from 'c'
However, not supported by all dbms products.

Remove parameters from SQL Query in with Regex / LogStash

A 3rd party system I use logs all SQL queries along with rowcount & response time which I then send to Logstash/Elastic to calculate metrics. As this system doesn't use bind variables, and there are 10's of millions of queries a day, I need to be able to rollup the data, which I can't do if the majority of queries are unique. I need a way to replace the SQL query parameters with '?' as Oracle would do via Cursor Sharing.
i.e.
replace
'SELECT * FROM table_name WHERE id = 123'
with
'SELECT * FROM table_name WHERE id = ?'
I have access to Ruby scripting magic in Logstash, but unfortunately all of the google results for 'sql regex' or similar return results of how to use regular expressions in SQL, not the other way round. Before I go crafting a regular expression parser, I thought I would check in here to see if others have tried to solve a similar problem.
FYI, have looked at implementing a solution using a Ruby SQL AST library such as https://github.com/lfittl/pg_query but plugging Ruby libraries in to Logstash becomes more of a problem of writing a custom Filter plugin to do the work, which may be the answer, but i'm hoping I'm missing something obvious.
I am not a logstash/ruby developer/user, but in terms of regular expression you may try this one:
(=\s\W\w+\W|=\s\d+)
You can test this here
SELECT * FROM Table1 WHERE Column1 = 1
SELECT * FROM Table1 WHERE Column1 = 'abc'
SELECT * FROM Table1 WHERE (Column1 = 'abc' OR Column2 = 1)
SELECT * FROM Table1 WHERE (Column1 = 'abc' AND Column2 = 1) OR Column2 = 'zxy'
SELECT * FROM Table1 WHERE (Column1 = 'abc' AND Column2 = 1) OR Column2 = 'zxy' AND
Column3 = 2
SELECT * FROM Table1 WHERE Column1 = 1 AND Column2 = 2
Expected Results:
Match 1
Full match = 1
Group 1. = 1
Match 2
Full match = 'abc'
Group 1. = 'abc'
Match 3
Full match = 'abc'
Group 1. = 'abc'
Match 4
Full match = 1
Group 1. = 1
Match 5
Full match = 'abc'
Group 1. = 'abc'
Match 6
Full match = 1
Group 1. = 1
Match 7
Full match = 'zxy'
Group 1. = 'zxy'
Match 8
Full match = 'abc'
Group 1. = 'abc'
Match 9
Full match = 1
Group 1. = 1
Match 10
Full match = 'zxy'
Group 1. = 'zxy'
Match 11
Full match = 2
Group 1. = 2
Match 12
Full match = 1
Group 1. = 1
Match 13
Full match = 2
Group 1. = 2
Based on these results you can create a function to replace the value of '= 2' to '= ?'.
Hope that it at least gives you a starting point.

SELECT <A OR B> FROM my_table WHERE A=5 OR B=5;

SELECT <A OR B> FROM my_table WHERE A=5 OR B=5;
Say the values (A,B) are:
1,5
2,5
5,3
5,4
The result of SELECT should be
1
2
3
4
In other words I need the value from the other column (other than the one found by WHERE).
You could try this:
SELECT
CASE WHEN a = 5 THEN b
ELSE a
END AS AorB
FROM my_table
WHERE a = 5 OR b = 5
In case there's no CASE in your SQL dialect, another possible solution comes to my mind, which, however, does not retain the order of the rows.
SELECT b AS AorB FROM my_table WHERE a = 5
UNION ALL
SELECT a AS AorB FROM my_table WHERE b = 5
Please note that this query explicitly allows for duplicate values in the result set! If you want to see distinct values, you should omit the ALL.
Assuming both A and B are numeric values, I wonder which solution will work faster, using CASE, UNION (both are general solutions) or a query like this one:
SELECT (A + B - 5) AS OtherValue
FROM Table
WHERE A = 5
OR B = 5;
Assuming your database supports case expressions, and assuming both A and B are of the same data type - this should work:
SELECT CASE WHEN A = 5 THEN B ELSE A END AS OtherValue
FROM Table
WHERE A = 5
OR B = 5;
If you are looking for a very readable query then use UNION:
select a from my_table where b = 5
union
select b from my_table where a = 5;
I think this will work:
SELECT CASE WHEN a = 5 THEN b ELSE a END val FROM #tmpAll WHERE 5 IN (a, b)

Comparing rows in MS access tables

I have such values in these two table. I want to check duplicates in TableA and TableB
TABLEA
StaffName Shift Hrs
ABC 1 12
DEF 23
XYZ 2
TABLEB
StaffN Sft Hrs
ABC 1 12
DEF 23
XYZ 2
However when i do a
SELECT * FROM TABLEA
WHERE NOT EXISTS
(SELECT * FROM TABLEB.StaffN = TABLEA.StaffName AND
TABLEB.Sft = TABLEA.Shift AND
TABLEB.Hrs = TABLEA.Hrs);
Why would i be returned of DEF and XYZ? is it becuz of the empty value? And how can i change my select statment to check if both value empty, its the same.
Yes, you were right to suspect Nulls.
Consider the 3 values in the "DEF" rows: DEF; Null; and 23. A human might say those rows are duplicates because all 3 values are the same in both tables.
However, the subquery asks the db engine to consider whether TABLEB.Sft = TABLEA.Shift. And there is the problem ... a Null can never be equal to anything, not even another Null.
See whether this Immediate window session clarifies the situation.
? 1 = 1
True
? 1 = 2
False
? 1 = Null
Null
? Null = Null
Null
So when both fields are Null, the comparison TABLEB.Sft = TABLEA.Shift is evaluated as Null. The db engine will only include rows where the comparison is True, so those "DEF" rows are excluded. And the same logic explains why the "XYZ" rows are excluded.
You need a comparison which returns True when both TABLEB.Sft and TABLEA.Shift are Null, and also returns True when both contain the same non-Null values.
(TABLEB.Sft Is Null AND TABLEA.Shift Is Null)
OR
(TABLEB.Sft = TABLEA.Shift)
Try this query:
SELECT *
FROM
TABLEA AS a
INNER JOIN TABLEB AS b
ON a.StaffName = b.StaffN
WHERE
(
(a.Shift Is Null AND b.Sft Is Null)
OR
(a.Shift = b.Sft)
)
AND
(
(a.Hrs Is Null AND b.Hrs Is Null)
OR
(a.Hrs = b.Hrs)
);
You could wrap your where criteria around the NZ function to see if that helps, like:
NZ(TABLEB.Hrs,0) = NZ(TABLEA.Hrs,0)

Selecting filtered rows with SQL

I am constructing an SQL statement with some parameters. Finally, an SQL statement is created like
"select * from table where column1 = "xyz"".
But I also need the rows which are filtered with this statement. In this case they're rows which are not "xyz" valued in column1. More specifically, I am looking for something like INVERSE(select * from table where ...). Is it possible?
Edit: My bad, I know I can do it with != or operator. Here the case is, select statement may be more complex (with some ANDs and equal, greater operators). Let's assume a table has A,B,C and my SQL statement brings only A as result. But I need B and C while I only have the statement which brings A.
select * from table where column1 != 'xyz' or column1 is null;
If you want the other ones, do it like this:
select * from table where column1 <> "xyz"
column1 <> (differs from) "xyz"
To check if something is no equal you can use <> or even !=
SELECT *
FROM yourTable
WHERE <> 'xyz'
OR
SELECT *
FROM yourTable
WHERE != 'xyz'
Many database vendors support (see list) both versions of the syntax.
If you're retrieving both result sets at about the same time, and just want to process the xyz ones first, you could do:
select *,CASE WHEN column1 = "xyz" THEN 1 ELSE 0 END as xyz from table
order by CASE WHEN column1 = "xyz" THEN 1 ELSE 0 END desc
This will return all of the rows in one result set. Whilst xyz = 1, these were the rows with column1 = 'xyz'.
It was :
"select * from table where rowId NOT IN (select rowId from table where column1 = "xyz")
I needed a unique rowId column to achieve this.