Это двойной вложенный запрос NOT EXISTS
(на самом деле это не то, что я обычно видел), и он используется специально для ответа на этот тип вопросов, т. Е. «Есть ли x true для всех y?"
Here's MySQL's page on EXISTS and NOT EXISTS, в котором упоминается этот метод специально.
во-первых, в сокровенной SELECT
запросе, вы выбираете детали, возит каждый магазин. Затем, с первым NOT EXISTS
пункта, вы выбираете детали, которые не выполняются по каждому магазину. И, наконец, во внешнем NOT EXISTS
пункте, вы выбираете магазины, возвращаемые пустой набор для внутреннего NOT EXISTS
пункта означает, что они несут каждую часть.
Here is a SQLFiddle этого запроса в действии.
Предупреждающее слово: если вы работаете с SQL, всегда хорошо думать и работать в наборах, и мышление в линейных терминах, как то, что должно следовать, может быстро вызвать вас. Не делайте это привычкой!
Тем не менее, иногда, пытаясь разобраться в сложном запросе, подобном этому, это может помочь думать об этих вещах как о циклах.
Таким образом, принимая данные в скрипке в качестве примера, мы имеем:
suppliers:
sid, name
9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
1, 'wood'
2, 'stone'
3, 'paper'
catalog:
cid, pid, sid
1,1,9
2,2,9
3,1,8
4,1,7
5,2,7
6,3,7
Так что с этими данными, AAA несет дерево и камень, ГЭБ только несет дрова, и CCC несет дерево, камень, и бумага.
Теперь давайте проверим запрос по строкам. Мы выбираем из suppliers
, и мы решаем, какие строки включать в результирующий набор, поэтому начните с первой строки в suppliers
: 9,'AAA'
. Мы будем называть эту строку S
временно. Мы включим эту строку только в том случае, если во внутреннем наборе результатов нет ничего, поэтому давайте взглянем на это.
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
Этот набор результатов выбора из parts
, и мы собираемся пройти через это ряд за рядом. S
по-прежнему равен 9,'AAA'
, пока мы это делаем. Поэтому начните с первой строки в parts
: 1,'wood'
. Назовем этот ряд P
. Мы включим эту строку только в этот первый внутренний результирующий набор, если на следующем уровне набора результатов ничего не будет, поэтому давайте переместимся туда. Помните, что S
= 9,'AAA'
и P
= 1,'wood'
.
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
P => 1, 'wood'
2, 'stone'
3, 'paper'
Этот самый внутренний запрос выбирается из «каталога». Мы ищем любую строку, назовем ее C
, в catalog
, где C.sid
равно S.sid
И C.pid
равно P.pid
. Это означает, что текущий поставщик несет часть. Нам нужны детали, которые текущий поставщик НЕ переносит, поэтому мы инвертируем результирующий набор. Мы знаем, что sid S
равен 9, и мы знаем, что pid P
равен 1. Есть ли какие-либо строки в C
, которые соответствуют этому? Самая первая строка в C
соответствует этому, поэтому мы знаем, что этот набор результатов не пуст.
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
P => 1, 'wood'
2, 'stone'
3, 'paper'
catalog:
cid, pid, sid
C => 1,1,9 --Match found! Don't include P in outer result set
2,2,9
3,1,8
4,1,7
5,2,7
6,3,7
Теперь вернитесь к следующей внешней петле. Внутренний результирующий набор не был пустым, поэтому мы знаем, что 1,'wood
не будет частью результирующего набора этого цикла. Поэтому мы переходим к следующему ряду в parts
, 2,'stone'
.Мы обновляем значение P
, чтобы сравнять эту строку. Следует ли включить эту строку в результирующий набор? Мы снова должны выполнить внутренний запрос с нашим новым значением для P
(S
все еще не изменилось). Поэтому мы ищем любые строки в catalog
с sid
равными 9 и pid
, равными 2. Вторая строка соответствует, поэтому существует результирующий набор.
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
1, 'wood'
P => 2, 'stone'
3, 'paper'
catalog:
cid, pid, sid
1,1,9
C => 2,2,9 --Match found! Don't include P in outer result set
3,1,8
4,1,7
5,2,7
6,3,7
Вернитесь к следующей внешней петле. Внутренний результирующий набор не был пустым, поэтому 2,'stone'
не будет частью результирующего набора этого цикла.
Когда мы снова пройти через все это 3,'paper'
, мы находим, что нет ни одной строки в catalog
, которые имеют sid = 9
и pid = 3
. Самый внутренний набор результатов пуст, поэтому мы знаем, что это значение должно быть P
в следующем крайнем цикле.
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
1, 'wood'
2, 'stone'
P => 3, 'paper'
catalog:
cid, pid, sid
1,1,9
2,2,9
3,1,8
4,1,7
5,2,7
6,3,7
C => --No match found, include P in outer result set
Мы прошли через весь parts
таблицы на данный момент, поэтому мы наш окончательный результат установлен для второго цикла, и он не пуст, это означает, что мы нашли часть, которая S
не несет, поэтому мы знаем, что мы не можем включить текущее значение S
в наш конечный набор конечного внешнего контура.
Таким образом, мы переходим к следующей строке в suppliers
и начать весь процесс заново:
suppliers:
sid, name
9, 'AAA'
S => 8, 'BBB'
7, 'CCC'
После того, как мы получаем S = 7,'CCC'
мы будем проходить через все эти петли, и матч будет найден в внутренний цикл для каждого значения P, которое поставляется, что означает, что этот второй цикл будет иметь пустой набор. Мы не смогли найти какие-либо детали, которые поставщик не несет, поэтому в набор результатов добавляется значение S
, то есть они несут все!
Что такое РСУБД, вы используете? –
Извините, я не уверен, что понимаю этот вопрос. Вы имеете в виду SQL? – tryingtolearn
Нет, какая у вас БД? MSSQL или MySQL или любой другой БД? –