Двойное не существует Объяснение логики SQL

Есть 2 таблицы, одна называется пьющими со столбцом имен, другая называется частыми, в которой есть 2 столбца, пьющие и бары (которые они часто посещают).

У меня есть запрос, который отвечает на это утверждение:

Drinkers who frequent all bars 

или в другой формулировке:

Drinkers such that there aren’t any bars that they don’t frequent

Теперь вот результирующий запрос:

SELECT d.name
FROM drinkers d
WHERE NOT EXISTS (
    SELECT b.name
    FROM bars b
    WHERE NOT EXISTS (
        SELECT *
        FROM frequents f
        WHERE f.drinker = d.name
        AND f.bar = b.name
        )
       )

Мне сложнее всего следовать логике, когда используются два NOT EXISTS. Если кто-то может рассказать мне, как понять эти типы запросов, это было бы очень признательно. Спасибо.


person Chad    schedule 23.09.2015    source источник


Ответы (2)


Вы можете попытаться развернуть такие запросы изнутри. Итак, начнем с последнего подзапроса:

SELECT *
FROM frequents f
WHERE f.drinker = d.name
AND f.bar = b.name

Здесь вы выбираете клиентов определенного бара с определенным названием: другими словами, вы проверяете, ходит ли этот конкретный пьющий в этот бар. А сейчас:

SELECT b.name
FROM bars b
WHERE NOT EXISTS (
    SELECT *
    FROM frequents f
    WHERE f.drinker = d.name
    AND f.bar = b.name
)

можно рассматривать как что-то вроде

SELECT b.name
FROM bars b
WHERE NOT EXISTS (this particular client in it)

Здесь вы выбираете все бары, в которых этот человек не является клиентом. Таким образом, вы получите что-то вроде

SELECT d.name
FROM drinkers d
WHERE NOT EXISTS (any bar without this guy as a client)

И я думаю, что на этом этапе запрос должен казаться ясным: выбрать всех пьющих, для которых нет бара без них.

person Aioros    schedule 23.09.2015

Я не знаю, нужно ли вам обязательно проходить эти циклы NOT EXISTS, поскольку вы вполне можете сделать что-то подобное

SELECT d.name
FROM drinkers d 
INNER JOIN frequents f ON f.drinkerName = d.name
GROUP BY d.name
HAVING COUNT(distinct barName) = 
    (SELECT COUNT(distinct barName) 
     from frequents
)

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

person Julien Blanchard    schedule 23.09.2015