问题
For example, say you have a table of files and a logging table of transfer operations.
CREATE TABLE files
(
id INTEGER PRIMARY KEY
-- various other columns
);
CREATE TABLE transfers
(
id INTEGER PRIMARY KEY,
file_id INTEGER,
status TEXT NOT NULL,
-- various other columns
FOREIGN KEY (file_id) REFERENCES files (id)
)
A transfer operation can have various statuses -- 'succeeded'
, 'failed'
, 'in progress'
, etc. One file can have many transfer operations -- in particular, if a transfer fails, another transfer for that file may be scheduled later.
Now, say we want to find all the files with only failed transfers -- no transfers currently in progress, no later successful transfers.
So far I have the solution below with subselects:
SELECT files.*
FROM files
WHERE files.id IN (
SELECT DISTINCT file_id
FROM transfers
WHERE transfers.status == 'failed'
) AND files.id NOT IN (
SELECT DISTINCT file_id
FROM transfers
WHERE transfers.status <> 'failed'
)
However, this feels a little clunky and procedural. Is there a more elegant solution, possibly involving self-joins?
回答1:
If you want to use self joins:
SELECT DISTINCT files.*
FROM
files INNER JOIN transfers t1
ON files.id = t1.file_id AND t1.status='failed'
LEFT JOIN transfers t2
ON file.id = t2.file_id AND t2.status<>'failed'
WHERE
t2.id IS NULL
回答2:
How about an aggregation with a having
clause?
select t.file_id
from transfers t
group by t.file_id
having sum(case when status <> 'failed' then 1 else 0 end) = 0;
If there is additional information from files
that you want, you can join
it in.
回答3:
I tend to use negated exists
for these kinds of queries as they tend to perform fine with proper indices and in my opinion reflects the intent (or semantics) or the query well.
SELECT file_id
FROM transfers t
WHERE t.status = 'failed'
AND NOT EXISTS (
SELECT 1
FROM transfers
WHERE status <> 'failed'
AND file_id = t.file_id
);
来源:https://stackoverflow.com/questions/32083618/select-records-in-table-a-with-only-certain-corresponding-records-in-table-b