По крайней мере, один X, но нет Ys Query - PullRequest
4 голосов
/ 09 марта 2012

Я иногда сталкиваюсь с этим паттерном, и я не нашел ужасно удовлетворительного способа его решения.

Скажем, у меня есть таблица employee и таблица review. Каждый сотрудник может иметь более одного отзыва. Я хочу найти всех employee, у которых есть хотя бы один «хороший» отзыв, но нет «плохих».

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

Есть ли способ сделать это БЕЗ хранимых процедур, функций или переноса данных на сервер? Я заставил его работать с ними, но я уверен, что есть другой способ.

Ответы [ 4 ]

7 голосов
/ 09 марта 2012

Поскольку вы не опубликовали свою структуру БД, я сделал несколько предположений и упрощений (относительно столбца rating, который, вероятно, является числом, а не символьным полем). Отрегулируйте соответственно.

Решение 1. Использование соединений

select distinct e.EmployeeId, e.Name
from employee e
left join reviews r1 on e.EmployeeId = r1.EmployeeId and r1.rating = 'good'
left join reviews r2 on e.EmployeeId = r2.EmployeeId and r1.rating = 'bad'
where r1.ReviewId is not null --meaning there's at least one
and r2.ReviewId is null --meaning there's no bad review

Решение 2. Группировка по и фильтрация с условным подсчетом

select e.EmployeeId, max(e.Name) Name
from employee e
left join reviews r on e.EmployeeId = r.EmployeeId
group by e.EmployeeId
having count(case r.rating when 'good' then 1 else null end) > 0
and  count(case r.rating when 'bad' then 1 else null end) = 0

Оба решения совместимы с SQL ANSI, что означает, что оба работают с любой разновидностью RDBMS, которая полностью поддерживает стандарты SQL ANSI (что справедливо для большинства RDBMS).

Как указал @onedaywhen, код не будет работать в MS Access (не проверял, я доверяю его опыту в этой области).

Но у меня есть одно высказывание по этому поводу (которое может расстроить некоторых людей): я вряд ли рассматриваю MS Access как RDBMS. Я работал с этим в прошлом. Как только вы продолжаете (Oracle, SQL Server, Firebird, PostGreSQL, MySQL, вы называете это), вы никогда не захотите возвращаться. Серьезно.

5 голосов
/ 09 марта 2012

Вопрос - вернуть строки на стороне A на основании отсутствия совпадения в B - (сотрудники без «плохих» проверок) описывает «анти-полу соединение». Существует множество способов выполнить этот вид запроса, по крайней мере 5 я обнаружил в MS Sql 2005 и выше.

Я знаю, что это решение работает в MSSQL 2000 и выше и является наиболее эффективным из 5 способов, которые я пробовал в MS Sql 2005 и 2008. Я не уверен, будет ли оно работать в MySQL, но так и должно быть, так как он отражает довольно распространенную операцию множества.

Обратите внимание, что предложение IN предоставляет подзапросу доступ к таблице employee во внешней области видимости.

SELECT EE.*
FROM   employee EE
WHERE
    EE.EmpKey IN (
      SELECT RR.EmpKey
      FROM   review RR
      WHERE  RR.EmpKey = EE.EmpKey
        AND  RR.ScoreCategory = 'good'
    ) 
  AND
    EE.EmpKey NOT IN (
      SELECT  RR.EmpKey 
      FROM    review RR
      WHERE   RR.EmpKey = EE.EmpKey
        AND   RR.ScoreCategory = 'bad'
    )
1 голос
/ 09 марта 2012
SELECT ???? FROM employee,review
WHERE employees.id = review.id
GROUP BY employees.id
HAVING SUM(IF(review='good',1,0)) > 1 AND SUM(IF(review='bad',1,0)) = 0
1 голос
/ 09 марта 2012

Это возможно.Конкретный синтаксис зависит от того, как вы храните «хорошие» и «плохие» обзоры.

Предположим, у вас был столбец classification в review со значениями «хорошо» и «плохо».

Тогда вы могли бы сделать:

SELECT employee.*
FROM employee
JOIN review 
 ON employee.id=review.employee_id
GROUP BY employee.id
HAVING SUM(IF(classification='good',1,0))>0 -- count up #good reviews, > 0
   AND SUM(IF(classification='bad',1,0))=0  -- count up #bad  reviews, = 0.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...