Writing SQL where clause to exclude rows based on two conditions - sql

Hi there I have a database where I would like Select all rows, but excluding records where two conditions are both met. I can't work out how to write the where clause that does this.
To give an example the table is:
Name | Date | Country
I want to select all rows except rows where the Name is Dave and the Date is > 20180101. So rows where the Name is Dave but the Date is <= 20180201 should be included as should rows where the date is > 20180101 and the name is anything but Dave.

This is where De Morgan's Law is helpful. It states !(A && B) can also be expressed !A || !B and vice versa: !A || !B can be !(A && B).
Also that !(A || B) is !A && !B and !A && !B is !(A || B).
For this question you want: NOT ('Dave' AND >'20180101'), which matches the first case above. Applying De Morgan's Law would give you NOT 'Dave' OR <='20180101'.
Use it in an SQL query like this:
SELECT *
FROM [table]
WHERE [Name] <> 'Dave' OR [Date] <= '20180101'

select *
from table
where name <> 'Dave' OR date <= '2018-01-01'
get all the rows that the name is not dave or the date is not X. if both conditions are fail (both dave and X) then that row won't be returned.

Related

How can I make IF without ELSE on SQL WHERE condition?

I`m trying to make a querie that selects users and if user type equals 1 I need to select those with age. My table:
id (int 11) | type (int 11) | email (varchar 25) | age (int 11)
My querie:
SELECT * FROM users WHERE IF(type = 1, age <> 0)
The problem is that I need to have an ELSE condition, but I dont need one in this case. How can I make an IF inside WHERE without else condition?
Thanks
You can do it with CASE:
SELECT * FROM users
WHERE age = CASE WHEN type <> 1 THEN age ELSE 0 END
Q: How do I make IF without ELSE on SQL WHERE condition ?
A: It's not possible; there is always an ELSE. MySQL IF() function has three arguments. It doesn't matter where the IF() function is used, whether it's part of an expression in a WHERE clause, or an expression in the SELECT list.
As an alternative to the MySQL IF() function, we can use a more portable, more ANSI-standard compliant CASE expression. But that doesn't get away from the crux of the question, about avoiding an ELSE. There is always an ELSE with the CASE expression as well. If we omit the ELSE clause, it's the same as if we had specified ELSE NULL.
As an aside (unrelated to the question that was asked), I don't think we should be storing age as an attribute; typically age is the difference between the current date and a date in the past (date of birth, registration date, etc.)
I'm thinking we don't need an IF function in the WHERE clause. (That's specific to MySQL, so this answer assumes that the target DBMS is MySQL, and not some other RDBMS).
We can use a combination of conditions, combined with NOT, AND, OR and parens so specify an order of operations.
Sample data and example output goes a long way to explaining the spec.
id type age email
-- ---- ---- ----------
1 0 0 1#one
2 1 0 2#two
3 0 1 3#three
4 1 1 4#four
5 0 NULL 5#five
6 1 NULL 6#six
7 NULL NULL 7#seven
8 NULL 0 8#eight
9 NULL 1 9#nine
Which of these rows should be returned, and which rows should be excluded?
Here is an example query (MySQL specific syntax) that returns all rows except row id=2 (type=1, age=0)
SELECT u.id
, u.type
, u.age
, u.email
FROM user u
WHERE NOT ( u.type <=> 1 )
OR NOT ( u.age <=> 0 )
If there's a requirement to incorporate IF functions, we can do that, and return an equivalent result:
SELECT u.id
, u.type
, u.age
, u.email
FROM user u
WHERE NOT ( IF( u.type <=> 1 ,1,0) )
OR NOT ( IF( u.age <=> 0 ,1,0) )
^^^ ^^^^^
In the WHERE clause, an expression will be evaluated as a boolean value. A numeric value of 0 is FALSE, a non-zero value is TRUE, and NULL value is (as always) just NULL.
For a row to be returned, we need the expression in the WHERE clause to evaluate to a non-zero value (to evaluate to TRUE).
The third argument of the IF() function is the "else" value; for that value, we can return TRUE, FALSE or NULL. To exclude rows that do not satisfy the type=1 condition, we return either zero or NULL:
WHERE IF(type = 1, age <> 0 ,0 )
^^
or equivalently:
WHERE IF(type = 1, age <> 0 ,NULL )
^^^^^
If we want rows that don't satisfy type=1 condition to be returned, we can return any non-zero value:
WHERE IF(type = 1, age <> 0 ,42 )
^^^
RECAP:
Addressing the question that was asked:
Q: How do I make IF without ELSE on SQL WHERE condition ?
A: There is always an ELSE value with the MySQL IF() function; in the context of the WHERE clause, the value will be evaluated as a boolean: TRUE, FALSE or NULL.
I think you want:
SELECT *
FROM users
WHERE type <> 1 OR age <> 0;
I was in a similar situation and ended up with the following solution:
SELECT * FROM users WHERE IF(type = 1, age <> 0, 1=0)
The else part here is 1 = 0 which is never true, so you don't select anything in that case.

SQL: sum 3 columns when one column has a null value without replacing null with 0?

Given table:
ID ANOTHERID ONE TWO THREE
X1 B1 15 15 -
X1 B2 10 - -
X2 B1 - 20 -
This query:
SELECT SUM (ONE + TWO + THREE) FROM (TABLE)
GROUP BY ID, ANOTHERID
I also tried
select sum(coalesce( ONE + TWO + THREE, ONE + TWO, ONE + THREE, ONE +
THREE))
at least one column has a null value. How can I still add them even if there is a null? As null and 0 have different meanings here (null means not started, 0 means not worked), I dont want to replace null with 0. Thanks
One method is:
SELECT SUM(COALESCE(ONE, 0) + COALESCE(TWO, 0) + COALESCE(THREE, 0))
FROM (TABLE)
GROUP BY ID, ANOTHERID;
Or, if you have at least one non-NULL value in each column:
SELECT SUM(ONE) + SUM(TWO) + SUM(THREE)
The time reporting table(s) should not allow null values, and the employee table should have a hire date field which can be used as criteria in your reporting queries. This will enable you to accurately report what management expects.
This solution worked for me
select
case when coalesce(sum(ONE), sum(TWO), sum(THREE)) is null then null else
sum(nvl(ONE,0) + nvl(TWO,0) + nvl(THREE,0)) end as
TOTALSUM
GROUP BY ID, ANOTHERID;
You might need to add another column in your table that describes the status of the employee (new, old) then make a condition like this:
if emp_status = 'new' then
--some code
working_hours := null;
else
--some code
working_hours : 0;
end if;

What is the difference between NOT and != operators in SQL?

What is the difference between NOT and != operators in SQL? I can't understand the difference. I guess they are same.
NOT negates the following condition so it can be used with various operators. != is the non-standard alternative for the <> operator which means "not equal".
e.g.
NOT (a LIKE 'foo%')
NOT ( (a,b) OVERLAPS (x,y) )
NOT (a BETWEEN x AND y)
NOT (a IS NULL)
Except for the overlaps operator above could also be written as:
a NOT LIKE 'foo%'
a NOT BETWEEN x AND y
a IS NOT NULL
In some situations it might be easier to understand to negate a complete expression rather then rewriting it to mean the opposite.
NOT can however be used with <> - but that wouldn't make much sense though: NOT (a <> b) is the same as a = b. Similarly you could use NOT to negate the equality operator NOT (a = b) is the same as a <> b
This question actually makes a lot more sense than people give it credit for.
Firstly, original SQL not-equal operator was <>, and only later on the C-style != was added as far as I know. I personally always use <> as != looks strange to me, but I'm old school.
Secondly, of course the original asker didn't mean to compare NOT with !=, but rather the difference between NOT a = b vs. a != b. And intuitively there should be a difference, but for all I know there isn't.
To make this all clear, here is an example session run on PostgreSQL (in Oracle you need more weird stuff such as SELECT ... FROM DUAL UNION ..., etc., which I avoid for the sake of brevity):
db=# with tst(a, b) as ( values (1,2), (2,3), (4, null) ) select * from tst;
a | b
---+---
1 | 2
2 | 3
4 |
(3 rows)
db=# with tst(a, b) as ( values (1,2), (2,3), (4, null) ) select * from tst where b = 2;
a | b
---+---
1 | 2
(1 row)
db=# with tst(a, b) as ( values (1,2), (2,3), (4, null) ) select * from tst where b != 2;
a | b
---+---
2 | 3
(1 row)
db=# with tst(a, b) as ( values (1,2), (2,3), (4, null) ) select * from tst where not b = 2;
a | b
---+---
2 | 3
(1 row)
Here we may think that this last query should also have returned the row (4, NULL). But it didn't. In PostgreSQL I can actually inspect this further, as follows:
db=# with tst(a, b) as ( values (1,2), (2,3), (4, null) ) select *, b = 2 as beq2 from tst;
a | b | beq2
---+---+------
1 | 2 | t
2 | 3 | f
4 | |
(3 rows)
You see that the Boolean expression b = 2 is NULL for the case where b is NULL. However, when a Boolean expression is NULL it is treated as false, or rather not true. And when you negate it with NOT, the Boolean value of the expression stays NULL and therefore is still not true.
Unfortunately I know of no other way than to handle NULL cases explicitly, so I have to write:
db=# with tst(a, b) as ( values (1,2), (2,3), (4, null) ) select * from tst where b is null or b = 2;
a | b
---+---
1 | 2
4 |
(2 rows)
So, instead of writing NOT <Boolean expression> you always have to write a IS NULL OR b IS NULL OR ... OR z IS NULL OR f(a, b, ..., z) where a, b, ..., z are variables in the given Boolean expression f(...).
It would be so much easier if instead of just NOT there were the Boolean operators MAYBE and CANNOT. So you could write WHERE MAYBE b = 2 or WHERE CANNOT b = 2 instead of this complicated OR combination of a bunch of IS NULL tests before your actual condition.
!= is a binary operator that returns true if its two arguments are not equal to each other.
NOT is a unary operator, which reverses its argument, a Boolean expression.
For example, this expression: a < 10 is true when a is any value less than 10. This condition can be negated: NOT a < 10. Negating this condition makes it true in the opposite cases, i.e. when a not less than 10. It's the same as a >= 10.
The expression a != 10 is true when a is any value less than 10 or any value greater than 10. This is a completely different case from a condition negated with NOT.
Both NOT operator and != almost serve a similar purpose.Both are used in Where clause of an sql query.
NOT operator shows records when a particular condition is not true.
Example:
SELECT * FROM Employees
WHERE NOT Country='Germany'
will get you records with all employees with countries other than Germany.
The != operator similarly checks if the values of two operands are equal or not, if values are not equal then condition becomes true.
Example:
SELECT * FROM Employees
WHERE Country!='Germany'
will get you all rows with country column having country other than Germany.

Access query to retrieve all three fields not equal

I need to write a query in MS Access where all the three columns should not be equal .
For example there are three columns A B C . Each column should not be equal to each other all should have a separate value.
How can I write such a query?
SELECT a, b, c
FROM my_table
WHERE a<>b AND a<>c AND b<>c
If your fields are non-nullable, all you need to check is that A != B, A != C, and B != C:
SELECT *
FROM test
WHERE A <> B AND A <> C AND B <> C
The same query would be OK if fields are nullable, but NULLs are not considered a valid value.

How do you select using a range of strings in SQL?

I have a table of vehicles with registration numbers, and want to select a subset of them that are between some user-supplied 'from' and 'to' values.
So lets say the table looks like this:
id reg_num
1 DD1111
2 DD1112
3 DE2245
4 EE5678
5 EF6547
The SQL I have so far looks like this:
select *
from vehicles
where reg_num >= 'DD' -- this value is user supplied
and reg_num <= 'DE' -- and so is this one
Which should (by my thinking) return:
1 DD1111
2 DD1112
3 DE2245
But instead, only returns:
1 DD1111
2 DD1112
I imagine that SQL server sees 'DE2245' as greater than 'DE', and so excludes the row.
My question: How do I get SQL server to include all rows that start with 'DE'?
You have to add 'zzzz's at the end as many as necessary to match your column width definition.
select * from vehicles
where reg_num >= 'DD' and reg_num <= 'DE' + 'ZZZZZZZZZZZZ'
where reg_num >= #userValueFrom
and left(reg_num,char_length(#userValueTo) <= #userValueTo
but please note that this where does not utilize any index because of a function on the column in SARG.
If the format is guaranteed, you can simply do:
SELECT *
FROM vehicles
WHERE LEFT(reg_num, 2) BETWEEN 'DD' AND 'DE'
But again, this is supposedly not SARGable - which always baffles me, because surely an index on reg_num can be used...
DE2245 is not less than DE. To make it more clear, DE2245 is less than DE3