Consider the table Audit, and AuditStatus.
Where auditId in AuditStatus is a foreign key, mapping the pk of table Audit.
table Audit
id | auditName |
1 | test |
2 | fooTest |
3 | barTest |
table AuditStatus
id | auditId | status |
11 | 1 | started |
12 | 1 | completed |
13 | 2 | started |
How can I only select the entries of table Audit, which do not have a AuditStatus.status 'completed'.
The result in this case would be:
2 | fooTest |
3 | barTest |
I have updated the question and the result example, to make it more clear. The relation Audit -> AuditStatus is a one to many. And I want to exclude the Audits which have a refrerence to an AuditStatus with status 'complete'
You should post your attempted query into your question, not as comment. Anyway, your query is actually correct but your condition is incorrect. Let's inspect your query:
SELECT *
FROM Audit a
WHERE NOT EXISTS (
SELECT s.auditId
FROM AuditStatus s
WHERE a.id = s.auditId AND s.status != 'completed'
);
You're suppose to find where the status is not complete, which is true in the subquery but the problem here is you're doing a NOT EXISTS which negates the correct result you're getting from the subquery.
This is what your subquery will return:
id
auditId
status
11
1
started
13
2
started
Then when your NOT EXIST negates the auditId being returned, you'll get this result instead:
id
auditName
3
barTest
Which is correct according to the condition; auditId=3 wasn't returned in the subquery. What you need to modify is actually very simple, you just need to make the subquery return status = completed as true then NOT EXISTS will return any Audit.Id that doesn't match with the correlated subquery. Therefore:
SELECT *
FROM Audit a
WHERE NOT EXISTS (
SELECT s.auditId
FROM AuditStatus s
WHERE a.id = s.auditId AND s.status = 'completed'
);
And that's it, you should be getting the result you looking for.
Demo fiddle
maybe use a left join like below which only joins on Audit Status on Fk as well as status constraint
SELECT *
FROM Audit A
LEFT JOIN AuditStatus ATS
ON A.id= ATS.auditId AND ATS.Status ='completed'
WHERE AS.auditId IS NULL
Related
I have 2 tables - reservation:
id | some_other_column
----+------------------
1 | value
2 | value
3 | value
And second table - reservation_log:
id | reservation_id | change_type
----+----------------+-------------
1 | 1 | create
2 | 2 | create
3 | 3 | create
4 | 1 | cancel
5 | 2 | cancel
I need to select only reservations NOT cancelled (it is only ID 3 in this example).
I can easily select cancelled with a simple WHERE change_type = cancel condition, but I'm struggling with NOT cancelled, since the simple WHERE doesn't work here.
SELECT *
FROM reservation
WHERE id NOT IN (select reservation_id
FROM reservation_log
WHERE change_type = 'cancel')
OR:
SELECT r.*
FROM reservation r
LEFT JOIN reservation_log l ON r.id = l.reservation_id AND l.change_type = 'cancel'
WHERE l.id IS NULL
The first version is more intuitive, but I think the second version usually gets better performance (assuming you have indexes on the columns used in the join).
The second version works because LEFT JOIN returns a row for all rows in the first table. When the ON condition succeeds, those rows will include the columns from the second table, just like INNER JOIN. When the condition fails, the returned row will contain NULL for all the columns in the second table. The WHERE l.id IS NULL test then matches those rows, so it finds all the rows that don't have a match between the tables.
Just for completeness (and I honestly believe it fits better), I encourage you to use a simple NOT EXISTS.
SELECT * FROM reservation R
WHERE NOT EXISTS (
SELECT 1 FROM reservation_log
WHERE reservation_id = R.id
AND change_type = 'cancel'
);
I have 2 tables - reservation:
id | some_other_column
----+------------------
1 | value
2 | value
3 | value
And second table - reservation_log:
id | reservation_id | change_type
----+----------------+-------------
1 | 1 | create
2 | 2 | create
3 | 3 | create
4 | 1 | cancel
5 | 2 | cancel
I need to select only reservations NOT cancelled (it is only ID 3 in this example).
I can easily select cancelled with a simple WHERE change_type = cancel condition, but I'm struggling with NOT cancelled, since the simple WHERE doesn't work here.
SELECT *
FROM reservation
WHERE id NOT IN (select reservation_id
FROM reservation_log
WHERE change_type = 'cancel')
OR:
SELECT r.*
FROM reservation r
LEFT JOIN reservation_log l ON r.id = l.reservation_id AND l.change_type = 'cancel'
WHERE l.id IS NULL
The first version is more intuitive, but I think the second version usually gets better performance (assuming you have indexes on the columns used in the join).
The second version works because LEFT JOIN returns a row for all rows in the first table. When the ON condition succeeds, those rows will include the columns from the second table, just like INNER JOIN. When the condition fails, the returned row will contain NULL for all the columns in the second table. The WHERE l.id IS NULL test then matches those rows, so it finds all the rows that don't have a match between the tables.
Just for completeness (and I honestly believe it fits better), I encourage you to use a simple NOT EXISTS.
SELECT * FROM reservation R
WHERE NOT EXISTS (
SELECT 1 FROM reservation_log
WHERE reservation_id = R.id
AND change_type = 'cancel'
);
I have 2 tables - reservation:
id | some_other_column
----+------------------
1 | value
2 | value
3 | value
And second table - reservation_log:
id | reservation_id | change_type
----+----------------+-------------
1 | 1 | create
2 | 2 | create
3 | 3 | create
4 | 1 | cancel
5 | 2 | cancel
I need to select only reservations NOT cancelled (it is only ID 3 in this example).
I can easily select cancelled with a simple WHERE change_type = cancel condition, but I'm struggling with NOT cancelled, since the simple WHERE doesn't work here.
SELECT *
FROM reservation
WHERE id NOT IN (select reservation_id
FROM reservation_log
WHERE change_type = 'cancel')
OR:
SELECT r.*
FROM reservation r
LEFT JOIN reservation_log l ON r.id = l.reservation_id AND l.change_type = 'cancel'
WHERE l.id IS NULL
The first version is more intuitive, but I think the second version usually gets better performance (assuming you have indexes on the columns used in the join).
The second version works because LEFT JOIN returns a row for all rows in the first table. When the ON condition succeeds, those rows will include the columns from the second table, just like INNER JOIN. When the condition fails, the returned row will contain NULL for all the columns in the second table. The WHERE l.id IS NULL test then matches those rows, so it finds all the rows that don't have a match between the tables.
Just for completeness (and I honestly believe it fits better), I encourage you to use a simple NOT EXISTS.
SELECT * FROM reservation R
WHERE NOT EXISTS (
SELECT 1 FROM reservation_log
WHERE reservation_id = R.id
AND change_type = 'cancel'
);
I have 2 tables - reservation:
id | some_other_column
----+------------------
1 | value
2 | value
3 | value
And second table - reservation_log:
id | reservation_id | change_type
----+----------------+-------------
1 | 1 | create
2 | 2 | create
3 | 3 | create
4 | 1 | cancel
5 | 2 | cancel
I need to select only reservations NOT cancelled (it is only ID 3 in this example).
I can easily select cancelled with a simple WHERE change_type = cancel condition, but I'm struggling with NOT cancelled, since the simple WHERE doesn't work here.
SELECT *
FROM reservation
WHERE id NOT IN (select reservation_id
FROM reservation_log
WHERE change_type = 'cancel')
OR:
SELECT r.*
FROM reservation r
LEFT JOIN reservation_log l ON r.id = l.reservation_id AND l.change_type = 'cancel'
WHERE l.id IS NULL
The first version is more intuitive, but I think the second version usually gets better performance (assuming you have indexes on the columns used in the join).
The second version works because LEFT JOIN returns a row for all rows in the first table. When the ON condition succeeds, those rows will include the columns from the second table, just like INNER JOIN. When the condition fails, the returned row will contain NULL for all the columns in the second table. The WHERE l.id IS NULL test then matches those rows, so it finds all the rows that don't have a match between the tables.
Just for completeness (and I honestly believe it fits better), I encourage you to use a simple NOT EXISTS.
SELECT * FROM reservation R
WHERE NOT EXISTS (
SELECT 1 FROM reservation_log
WHERE reservation_id = R.id
AND change_type = 'cancel'
);
I have 2 tables - reservation:
id | some_other_column
----+------------------
1 | value
2 | value
3 | value
And second table - reservation_log:
id | reservation_id | change_type
----+----------------+-------------
1 | 1 | create
2 | 2 | create
3 | 3 | create
4 | 1 | cancel
5 | 2 | cancel
I need to select only reservations NOT cancelled (it is only ID 3 in this example).
I can easily select cancelled with a simple WHERE change_type = cancel condition, but I'm struggling with NOT cancelled, since the simple WHERE doesn't work here.
SELECT *
FROM reservation
WHERE id NOT IN (select reservation_id
FROM reservation_log
WHERE change_type = 'cancel')
OR:
SELECT r.*
FROM reservation r
LEFT JOIN reservation_log l ON r.id = l.reservation_id AND l.change_type = 'cancel'
WHERE l.id IS NULL
The first version is more intuitive, but I think the second version usually gets better performance (assuming you have indexes on the columns used in the join).
The second version works because LEFT JOIN returns a row for all rows in the first table. When the ON condition succeeds, those rows will include the columns from the second table, just like INNER JOIN. When the condition fails, the returned row will contain NULL for all the columns in the second table. The WHERE l.id IS NULL test then matches those rows, so it finds all the rows that don't have a match between the tables.
Just for completeness (and I honestly believe it fits better), I encourage you to use a simple NOT EXISTS.
SELECT * FROM reservation R
WHERE NOT EXISTS (
SELECT 1 FROM reservation_log
WHERE reservation_id = R.id
AND change_type = 'cancel'
);