2011-01-09 13 views
2

Ich habe folgenden zwei TabellenSQL AND-Operator nicht ordnungsgemäß funktioniert

LandParcels Tabelle

Blockid ParcelNo storPri 
======= ======== ======= 
52000105 3  State 
52000105 4  Private 
52000105 5  State 

Aktionen Tabelle

Blockid ParcelNo ActionTaken 
======= ======== =========== 
52000105 3  Received 
52000105 3  Send to Computer 
52000105 4  Received 
52000105 5  Received 

Ich möchte Received die Datensätze zu finden, aber nicht Send to Computer

Hier ist meine Abfrage

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 

Das Ergebnis ist

Blockid ParcelNo 
======= ======== 
52000105 3 
52000105 4 
52000105 5 

Ich möchte ParcelNo 4 und 5

+1

Normalisieren Ihre Aktionen Tabelle dann . – Mchl

+1

@Mchl, was lässt dich sagen, dass es nicht normalisiert ist? – Ronnis

+0

Was ** Datenbank ** verwendest du? Welche Version dieser Datenbank ??? –

Antwort

0

Es ist sehr unwahrscheinlich, dass Sie einen einfachen Fehler im AND-Operator in einem häufig verwendeten Datenbankprodukt finden. Das Problem hier ist nicht, dass das Datenbankmodul nicht die richtigen Ergebnisse liefert, sondern dass Sie nicht verstehen, was der AND-Operator tut.

Betrachten Sie Ihren Zustand ac.actiontaken = 'Received' AND ac.actiontaken <> 'Send to Computer'. Was passiert, ist, dass die Engine jede mögliche Zeile in der Ausgabe untersucht und entscheidet, ob sie die von Ihnen angegebene Bedingung erfüllt. Also, für die erste Zeile ist es wahr, dass ActionTaken 'Received' ist? Ja. Ist es auch wahr, dass actiontaken nicht 'Senden an Computer' ist? Sicher. Die Zeile qualifiziert sich also.

Tatsächlich jede Reihe mit actiontaken von ‚Received‘ qualifizieren, da per definitionem, dass für diese Zeile actiontaken auch wahr ist, ist nicht ‚auf Computer Senden‘.

Es gibt eine Reihe von Möglichkeiten, um die gewünschte Antwort zu erhalten. Hier ist mein eine bevorzugt:

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') 
+0

Das ist auch gut. Vielen Dank –

0
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'; 

oder etwas weniger Lust, die außerhalb der Sonderfall der Suche nach 1 von zwei erweitert werden kann ich erkannte

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 
    ); 

nur, dass Sie nicht brauchen, um Joi: n mit Landparzellen, Ihren Anforderungen entsprechend, aber ich belasse es wie es ist, falls Ihre reale Anforderung Daten von dieser Tabelle erfordert.

Update hinzugefügt BLOCKID Filter

+0

Ihre erste Lösung ist nicht ganz richtig, da das OP nicht angegeben hat, dass es keinen dritten ActionTaken-Wert gibt, wodurch die Klausel "mit count (*) = 1" falsch ist. – kirakun

+0

Schauen Sie sich die Where-Klausel an ... – Ronnis

+0

kann nicht gefiltert werden durch blockid = 52000105 –

1

Wenn Ihre Datenbank tuple in where Klauseln unterstützt, versuchen

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' 
) 

bearbeiten als Antwort zu marc_s: Ansonsten versuchen, diese Version, die ich denke, ist ANSI, stattdessen

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 
) 
+0

Das wird nicht in allen Datenbanksystem, z. in SQL Server funktioniert das nicht ... –

+0

Jetzt bearbeitet, um 'exists' stattdessen zu verwenden. – kirakun

+0

FWIW Ihre ursprüngliche Abfrage ist gültige ISO/ANSI vollständige SQL-92-Syntax, bekannt als "Zeilenkonstruktoren". – onedaywhen

0

Sie hat nicht gesagt, welche Datenbanksystem Sie verwenden - aber auf SQL Server, können Sie diese Abfrage verwenden hier:

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') 

Das in dieser Ausgabe ergibt sich hier:

blockid parcelno storePrio 
52000105  4   Private 
52000105  5   State 
0
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'))) 
0

Eine weitere Variante @ Kirakun der "Reihe Konstrukteurs Antwort, ist nach wie vor ISO/ANSI-Syntax, aber dies ist in der Tat in SQL Server unterstützt:

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'; 
Verwandte Themen