2011-01-09 7 views
2

я после двух таблицSQL и оператор не работает должным образом

LandParcels Таблица

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

Действия Таблица

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

Я хочу, чтобы найти записи Received но не Send to Computer

Вот мой запрос

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 

В результате

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

Я хочу ParcelNo 4 и 5

+1

Нормализация таблицу действия, то , – Mchl

+1

@Mchl, что заставляет вас сказать, что это не нормализуется? – Ronnis

+0

Что ** база данных ** вы используете? Какая версия этой базы данных ??? –

ответ

0

Пожалуйста, очень маловероятно, что вы найдете простую ошибку в операторе AND в любом обычно используемом продукте базы данных. Проблема здесь заключается не в том, что механизм базы данных не дает правильных результатов, а потому, что вы не понимаете, что делает оператор И.

Посмотрите на свое состояние ac.actiontaken = 'Received' AND ac.actiontaken <> 'Send to Computer'. Что произойдет, так это то, что движок будет проверять каждую возможную строку на выходе и решать, соответствует ли она указанному вами условию. Итак, для первой строки это правда, что actiontaken является «Received»? Да. Верно ли, что actiontaken - это не «Отправить компьютеру»? Конечно. Таким образом, строка подходит.

В самом деле, любой ряд с actiontaken из «полученного» будет иметь право, так как, по определению, это также верно, что actiontaken для этой строки является не 'Отправить на компьютер.

Существует несколько способов получить ответ, который вы хотите. Вот мой любимый один:

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

Это тоже хорошо. Большое спасибо –

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

или что-то менее фантазии, которые могут быть расширены за пределы частного случая ищет 1 из двух :

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

Я просто понял, что вам не нужно joi n с landparcels, учитывая ваши требования, но я оставляю это как есть, в случае, если ваше реальное требование требует данных из этой таблицы.

обновление Добавлен blockid фильтр

+0

Ваше первое решение не совсем верно, так как ОП не утверждал, что нет третьего значения ActionTaken, что делает предложение 'count (*) = 1' неправильным. – kirakun

+0

Посмотрите, где предложение ... – Ronnis

+0

не может быть отфильтровано blockid = 52000105 –

1

Если ваша база данных поддерживает tuple в where положения, попробуйте

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

Редактировать в ответ на marc_s: В противном случае, попробуйте этот вариант, который я думаю, ANSI, вместо

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

Это не будет работать во всей системе баз данных, например. в SQL Server это не работает ... –

+0

Теперь отредактирован, чтобы использовать 'exist' вместо этого. – kirakun

+0

FWIW ваш исходный запрос действителен. Синтаксис SQL/ANSI полного ISO/ANSI, известный как «конструкторы строк». – onedaywhen

0

Вы не сказали, что система базы данных вы используете - но на SQL Server, вы можете использовать этот запрос здесь:

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

В результате этого вывода здесь:

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

Еще вариации на @ Kirakun отвечают «Конструкторы строк», по-прежнему/синтаксис ISO ANSI, но это действительно поддерживается в 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'; 
Смежные вопросы