Reputation: 27
I have following two tables
LandParcels Table
Blockid ParcelNo storPri
======= ======== =======
52000105 3 State
52000105 4 Private
52000105 5 State
Actions Table
Blockid ParcelNo ActionTaken
======= ======== ===========
52000105 3 Received
52000105 3 Send to Computer
52000105 4 Received
52000105 5 Received
I want to find the records Received
but not Send to Computer
Here is my query
select
l.blockid, l.parcelno
from
landparcels l
left join
actions ac on l.blockid = ac.blockid and l.parcelno = ac.parcelno
where
ac.actiontaken = 'Received'
and ac.actiontaken <> 'Send to Computer'
and ac.blockid = 52000105
The result is
Blockid ParcelNo
======= ========
52000105 3
52000105 4
52000105 5
I want ParcelNo 4 and 5
Upvotes: 2
Views: 10164
Reputation: 57023
A further variation on @Kirakun's 'row constructors' answer, is still ISO/ANSI syntax but this is indeed supported in SQL Server:
SELECT blockid, parcelno
FROM landparcels
INTERSECT
SELECT blockid, parcelno
FROM actions
WHERE actiontaken = 'Received'
EXCEPT
SELECT blockid, parcelno
FROM actions
WHERE actiontaken = 'Send to Computer';
Upvotes: 0
Reputation: 2790
If your database supports tuple
in where
clauses, try
select *
from landparcels
where (blockid, parcelno) in
(
select blockid, parcelno
from actions
where actiontaken = 'Received'
)
and (blockid, parcelno) not in
(
select blockid, parcelno
from actions
where actiontaken = 'Send to Computer'
)
Edit in response to marc_s: Otherwise, try this version, which I think is ANSI, instead
select *
from landparcels as p
where exists
(
select 1
from actions
where actiontaken = 'Received'
and blockid = p.blockid
and parcelno = p.parcelno
)
and not exists
(
select 1
from actions
where actiontaken = 'Send to Computer'
and blockid = p.blockid
and parcelno = p.parcelno
)
Upvotes: 1
Reputation: 50970
Please, it's extremely unlikely that you'll find a simple bug in the AND operator in any commonly used database product. The problem here is not that the database engine isn't producing the correct results, it's that you don't understand what the AND operator does.
Look at your condition ac.actiontaken = 'Received' AND ac.actiontaken <> 'Send to Computer'
. What's going to happen is that the engine is going to examine every possible row in the output and decide whether it meets the condition that you specified. So, for the first row is it true that actiontaken is 'Received'? Yes. Is it is also true that actiontaken is not 'Send to Computer'? Sure. So the row qualifies.
In fact, any row with actiontaken of 'Received' will qualify since, by definition, it is also true that actiontaken for that row is not 'Send to Computer'.
There are a number of ways to get the answer that you want. Here's my preferred one:
SELECT DISTINCT a.blockid, a.parcelno FROM actions a
WHERE a.blockid = 52000105 AND a.actiontaken = 'Received' AND NOT EXISTS
(SELECT * FROM actions a2 WHERE a2.blockid = a.blockid AND
a2.parcelNo = a.parcelNo AND
a2.actiontaken = 'Send to Computer')
Upvotes: 1
Reputation: 1185
SELECT Blockid, PacelNo FROM LandParcels
WHERE ( ParcelNo IN ( SELECT ParcelNo FROM Actions WHERE ActionTaken = 'Received')
AND ( ParcelNo NOT IN ( SELECT ParcelNo FROM Actions WHERE ActionTaken = 'Send to Computer')))
Upvotes: 0
Reputation: 12833
select a.blockid
,a.parcelno
from landparcels a
join actions b on(a.blockid = b.blockid and a.parcelno = b.parcelno)
where b.action_taken in('Received', 'Send to Computer')
and a.blockid = 52000105
group
by a.blockid
,a.parcelno
having count(*) = 1
and min(b.action_taken) = 'Received';
or something less fancy, that can be extended outside the special case of looking for 1 out of two:
select a.blockid
,a.parcelno
from landparcels a
join actions b on(a.blockid = b.blockid and a.parcelno = b.parcelno)
where b.blockid = 52000105
and b.action_taken = 'Received'
and not exists(
select 'x'
from actions x
where x.action_taken = 'Send to Computer'
and b.blockid = x.blockid
and b.parcelno = x.parcelno
);
I just realized that you don't need to join with landparcels, given your requirements, but I leave it as is, in case your real requirement requires data from that table.
update Added blockid filter
Upvotes: 0
Reputation: 754538
You didn't say what database system you're using - but on SQL Server, you could use this query here:
SELECT
lp.*
FROM
dbo.LandParcels lp
WHERE
EXISTS(SELECT * FROM abo.ActionTaken a
WHERE a.blockid = lp.blockid
AND a.parcelno = lp.parcelno
AND a.ActionTaken = 'Received')
AND NOT EXISTS(SELECT * FROM dbo.ActionTaken a
WHERE a.blockid = lp.blockid
AND a.parcelno = lp.parcelno
AND a.ActionTaken = 'Send to Computer')
This results in this output here:
blockid parcelno storePrio
52000105 4 Private
52000105 5 State
Upvotes: 0