SQL SELECT с использованием in (), но исключая другие - PullRequest
3 голосов
/ 20 мая 2010

У меня есть таблица под названием «страны», связанная с другой таблицей «сети» с отношением «многие ко многим»:

  countries             countries_networks                networks
+-------------+----------+  +-------------+----------+  +-------------+---------------+
| Field       | Type     |  | Field       | Type     |  | Field       | Type          |
+-------------+----------+  +-------------+----------+  +-------------+---------------+  
| id          | int(11)  |  | id          | int(11)  |  | id          | int(11)       |
| countryName | char(35) |  | country_id  | int(11)  |  | name        | varchar(100)  |
+-------------+----------+  | network_id  | int(11)  |  | description | varchar(255)  |

Чтобы извлечь все страны, у которых network_id равен 6 и 7, я просто делаю следующее: (Я мог бы пойти дальше, чтобы использовать network.name, но я знаю country_networks.network_id, поэтому я просто использую их для сокращения SQL)

SELECT DISTINCT countryName 
 FROM countries AS Country
INNER JOIN countries_networks AS n ON Country.id = n.country_id
 WHERE n.network_id IN (6,7)

Это нормально, но тогда я хочу получить страны с идентификатором сети, равным 8, и без других.

Я пробовал следующее, но он все еще возвращает сети с 6 и 7 дюймами. Это как-то связано с моим JOIN?

SELECT DISTINCT countryName 
 FROM countries AS Country
INNER JOIN countries_networks AS n ON Country.id = n.country_id
 WHERE n.network_id IN (8)
AND n.network_id not IN(6,7)

Спасибо.

Ответы [ 6 ]

2 голосов
/ 20 мая 2010

Еще одно решение с использованием предиката NOT EXISTS.

SELECT DISTINCT countryName 
FROM countries AS Country
INNER JOIN countries_networks AS n ON Country.id = n.country_id
WHERE n.network_id IN (8)
AND NOT EXISTS (SELECT 1 FROM countries_networks n1 
   WHERE n1.country_id = Country.id AND n1.network_id !=8)
2 голосов
/ 20 мая 2010

Вам нужно два соединения:

SELECT  DISTINCT c.CountryName
FROM    Countries c
        INNER JOIN
                countries_networks n
                ON c.id = n.country_id
                AND n.network_id = 8
        LEFT JOIN
                countries_networks n2
                ON c.id = n2.country_id
                AND n2.network_id IN (6, 7)
WHERE   n2.country_id IS NULL

Поскольку в вашем запросе все обстоит так, все, что вы проверяете в последней строке, - это то, что 8 отсутствует в списке (6, 7). Если я правильно прочитал ваш вопрос, вы хотите, чтобы страны, в которых есть сеть с идентификатором 8, но в которой не было сети с идентификатором 6 или 7. Каждому требуется свое собственное объединение, и вы хотите убедиться, что нет соответствующих ряды для второго.

1 голос
/ 20 мая 2010
SELECT  countryName
FROM    countries
WHERE   country_id IN
        (
        SELECT  country_id
        FROM    network
        WHERE   network_id = 8
        )
        AND country_id NOT IN
        (
        SELECT  country_id
        FROM    network
        WHERE   network_id IN (6, 7)
        )
0 голосов
/ 20 мая 2010

Несмотря на то, что вы сопротивляетесь дополнительному объединению в таблице networks, это действительно самый простой способ. Если вы проверите свой идентификатор по этой таблице, ваш результат должен быть соответствующим образом ограничен:

SELECT countryName 
  FROM countries AS Country JOIN countries_networks AS cn 
                              ON Country.id = cn.country_id
                            JOIN networks n
                              ON cn.network_id = n.id
 WHERE n.network_id IN (8)

Тестирование по таблице networks должно ограничивать результаты независимо от всего, что может быть связано в таблице countries_networks. Он также должен работать (и читать) лучше, чем подзапросы или любое другое решение, которое я могу придумать.

0 голосов
/ 20 мая 2010

Причина, по которой вы получаете страны с идентификатором сети 6 и 7, заключается в том, что вы проверяете условие в каждой записи country_networks. Часть условия, которая исключает 6 и 7, не имеет никакого эффекта вообще, так как любой идентификатор, который равен 8, никогда не может быть 6 или 7 одновременно.

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

select countryName
from countries c
inner join countries_networks i on i.country_id = c.id and i.network_id = 8
left join countries_networks e on e.country_id = c.id and e.network_id in (6,7)
where e.id is null

Кроме того, вам не нужно выделять этот запрос, если только у вас нет нескольких соединений между одной и той же страной и идентификатором сети.

0 голосов
/ 20 мая 2010

Вы можете сделать подзапрос:

SELECT DISTINCT c.countryName 
 FROM countries AS c
INNER JOIN countries_networks AS n ON c.id = n.country_id
 WHERE n.network_id IN (8)
AND c.countryName NOT IN 
    (SELECT c2.countryName FROM countries AS c2 
    INNER JOIN countries_networks AS n2 
    WHERE n2.network_id IN (6,7))

Хотя это не может быть оптимальным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...