SQL Server - выбор записей с конкретными, несколькими записями в ассоциативной таблице - PullRequest
0 голосов
/ 18 ноября 2009

Я работаю в SQL Server со следующим примером проблемы. Брэндон предпочитает ПК и Mac, Сью предпочитает только ПК, а Алан предпочитает Mac. Данные будут представлены примерно так. Я должен был пойти на некоторые компромиссы, но, надеюсь, вы получите картину:

ТАБЛИЦА 1: Пользователь

uID (INT PK), uName (VARCHAR)
1        'Brandon'
2        'Sue'
3        'Alan'

ТАБЛИЦА 2: Компьютер

cID (INT PK), cName (VARCHAR)
1        'Mac'
2        'PC'

ТАБЛИЦА 3: UCPref - сохраняет настройки компьютера для каждого пользователя

uID (INT FK), cID (INT FK)
1        1
1        2
2        1
3        2

Теперь, если я хочу выбрать всех, кому нравятся ПК или Mac, это будет довольно просто. Есть дюжина способов сделать это, но если у меня есть список введенных элементов, то предложение IN довольно простое:

SELECT u.uName
FROM User u
INNER JOIN UCPref p ON u.uID = p.uID
WHERE cID IN (1,2)

Проблема, с которой я столкнулся, заключается в том, что происходит, когда я ТОЛЬКО хочу выбирать людей, которым нравятся ОБА ПК и Mac? Я могу сделать это в нескольких подзапросах, однако это не очень масштабируемо.

SELECT u.uName
FROM User u
INNER JOIN UCPref p ON u.uID = p.uID
WHERE u.uID IN (SELECT uID FROM UCPref WHERE cID = 1)
AND u.uID IN (SELECT uID FROM UCPref WHERE cID = 2)

Как можно написать этот запрос таким образом, чтобы вы могли вернуть пользователей, которые предпочитают несколько компьютеров, принимая во внимание, что могут быть сотни, может быть, тысячи разных типов компьютеров (то есть никаких подзапросов)? Если бы только вы могли изменить предложение IN, добавив в него ключевое слово типа «ВСЕ», чтобы указать, что вы хотите сопоставлять только те записи, в которых все элементы указаны в скобках?

SELECT u.uName
FROM User u
INNER JOIN UCPref p ON u.uID = p.uID
WHERE cID IN *ALL* (1,2)

1 Ответ

0 голосов
/ 18 ноября 2009

Использование JOIN:

SELECT u.uname
  FROM USERS u
  JOIN UCPREF ucp ON ucp.uid = u.uid
  JOIN COMPUTER mac ON mac.cid = ucp.cid
                   AND mac.cname = 'Mac'
  JOIN COMPUTER pc ON pc.cid = ucp.cid
                  AND pc.cname = 'PC'

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

Использование EXISTS:

SELECT u.uname
  FROM USERS u
  JOIN UCPREF ucp ON ucp.uid = u.uid
 WHERE EXISTS (SELECT NULL
                 FROM COMPUTER c
                WHERE c.cid = ucp.cid
                  AND c.cid IN (1, 2)
             GROUP BY c.cid 
               HAVING COUNT(*) = 2)

Если вы собираетесь использовать предложение IN, вы должны использовать GROUP BY/HAVING, но в COUNT () есть риск. Некоторые базы данных не допускают больше, чем *, в то время как MySQL допускает DISTINCT .... Проблема в том, что если вы не можете использовать DISTINCT в подсчете, вы можете получить два экземпляра значения 2, и оно будет действительным для SQL - давая вам ложное срабатывание.

...