Интересное наблюдение за SQL, ГДЕ СУЩЕСТВУЕТ в подкатегориях - PullRequest
1 голос
/ 13 января 2012

Я хотел бы знать, может ли кто-нибудь объяснить, почему происходит следующее:

У меня есть следующая таблица, которую я составил. Имеет 2 столбца «А» и «В»:

A       B
==========
1,      11
2,      12
3,      13
4,      14
5,      15
6,      16
7,      17
8,      18
9,      19

Если я запускаю следующий запрос: -

SELECT * FROM 
    (SELECT A,B FROM Table_1) T1
WHERE EXISTS 
    (SELECT 'X' FROM Table_1 WHERE A = 3)

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

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

SELECT * FROM 
    (SELECT A,B FROM Table_1) T1
WHERE EXISTS 
    (SELECT 'X' FROM Table_1 WHERE T1.A=3 OR T1.A=4)

Порционные результаты следующие

A     B
========
3,    13
4,    14

Может кто-нибудь объяснить, почему это происходит?

Ответы [ 5 ]

3 голосов
/ 13 января 2012

Давайте разберем, что вы на самом деле просите сервер баз данных.

SELECT * FROM - получите все поля.Я собираюсь игнорировать этот бит с этого момента, так как он не важен.

(SELECT A,B FROM Table_1) T1 - получить все строки из таблицы и назвать этот набор результатов "T1".

WHERE EXISTS (SELECT 'X' FROM Table_1 WHERE A = 3) - Выберите, НЕЗАВИСИМЫЙ из вышеперечисленного, все строки таблицы.Это происходит один раз для каждой строки в таблице - но всегда делает одно и то же, поскольку T1 не используется в подзапросе.Если в одной из строк таблицы A = 3 (всегда так), ограничений нет.В противном случае, откажитесь от этого конкретного T1 (здесь этого не происходит).

WHERE EXISTS (SELECT 'X' FROM Table_1 WHERE T1.A=3 OR T1.A=4) - это сложно.Вы делаете второй выбор всех строк в таблице, но вы ограничиваетесь этим условием: T1.A=3 OR T1.A=4.Это условие основано на результате T1 - но это не вся таблица, а конкретная строка .Когда вы говорите SELECT * FROM mytable WHERE mytable.A=3, вы не имеете в виду «выбрать все строки mytable, если какая-то строка имеет A = 3», вы имеете в виду только те конкретные строки, где это верно.Таким образом, подзапрос SELECT 'X' FROM Table_1 WHERE T1.A=3 OR T1.A=4 не содержит ни строк, ни всех строк в Table_1, в зависимости от того, какое значение находится в T1.A.Поскольку вы используете EXISTS, вы получаете значение true для всех строк и false для ни одного.

Именно поэтому вы получаете разные результаты - ваш подзапрос выполняется один раз для строки в Table_1.В первом случае он всегда содержит одну строку.Во втором случае он не содержит ни строк, ни всех девяти, в зависимости от конкретной строки Table_1, содержащейся в T1.

1 голос
/ 13 января 2012

Всегда есть несколько способов написать одну и ту же вещь в SQL.Например, это

SELECT * FROM 
    (SELECT A,B FROM Table_1) T1

... можно переписать так:

SELECT A, B
  FROM Table_1

Последнее проще, и я не вижу причин предпочитать первое.Переписав ваш первый запрос соответственно

SELECT A, B
  FROM Table_1
 WHERE EXISTS (
               SELECT 'X' 
                 FROM Table_1 
                WHERE A = 3
              );

Я удалил имя корреляции T1, потому что оно не имеет смысла.Подзапрос не ссылается на свое «внешнее» табличное выражение, поэтому нет необходимости устранять неоднозначность каждого вида Table_1.

Я думаю, вы понимаете, что здесь происходит: если одна или несколько строк в Table_1 удовлетворяют условию поиска A = 3, тогда возвращается вся таблица, в противном случае возвращается пустой набор.Несмотря на то, что это допустимый запрос, он часто не очень полезная конструкция.

Однако для второго запроса требуется хотя бы одно имя корреляции, поскольку подзапрос ссылается на свою внешнюю таблицу:

SELECT A, B
  FROM Table_1 T1
 WHERE EXISTS (
               SELECT 'X' 
                 FROM Table_1 T2
                WHERE T1.A IN (3, 4)
              );

Опять же, это семантически эквивалентно вашему второму запросу.Обратите внимание, что я добавил в подзапросе Table_1 имя корреляции T2, но T2 не появляется в предложении WHERE подзапроса.Поскольку T2 не используется, мы можем полностью удалить подзапрос (отсюда и необходимость в именах корреляции):

SELECT A, B
  FROM Table_1
 WHERE A IN (3, 4);

Стоит отметить, что возможность подзапроса ссылаться на «внешнюю» таблицуВыражение обычно используется как «коррелированный подзапрос», то есть условие поиска (предложение WHERE) включает в себя как «внутренние», так и «внешние» таблицы (условие поиска вашего второго запроса включает только «внешнюю» таблицу).

Используя обычную базу данных запчастей и поставщиков , здесь приведен пример коррелированного подзапроса для реализации semi-join для поиска поставщиков (S), которые поставляют (SP) хотя быодна часть:

SELECT SNO, SNAME
  FROM S
 WHERE EXISTS (
               SELECT *
                 FROM SP
                WHERE SP.SNO = S.SNO
              );

Обратите внимание, что условие поиска подзапроса связывает «внешнюю» таблицу SP с «внутренней» таблицей S.Кроме того, проекция SELECT SNO, SNAME во «внешней» таблице не требует включения имени корреляции S, поскольку SP из «внешней» таблицы не входит в область действия для «внутренней» таблицы.

1 голос
/ 13 января 2012

Ух, круто.

У вас есть немного дополнительного SQL там. Что вы на самом деле делаете:

 SELECT * FROM Table_1 T1 WHERE EXISTS 
    (SELECT 'X' FROM Table_1 WHERE T1.A = 3 OR T1.A = 4)

Так что же происходит?

Когда вы читаете каждую строку Таблицы_1 как T1, механизм оценивает суб-выбор, чтобы решить, должна ли строка из T1 быть включена в набор результатов.

В первой строке, где T1.A = 1, затем T1.A = 3 - FALSE, а T1.A = 4 - FALSE, поэтому выбор не выполнен. Эта строка не включена в набор результатов.

То же самое, когда A = 2. Строка с A = 2 не включена.

Но для следующих двух строк (T1.A из 3 и 4) по крайней мере одна сторона OR оценивается как TRUE, и дополнительный выбор завершается успешно. Итак, эти две строки включены.

Когда вы нажимаете T1.A из 5 и для остальной части таблицы, дополнительный выбор завершается неудачей.

1 голос
/ 13 января 2012

Я полагаю, что будет проверена текущая строка T1, чтобы увидеть, если A = 3 или A = 4, как если бы вы написали:

SELECT * FROM 
  (SELECT A,B FROM Table_1) T1
WHERE A=3 OR A=4;
1 голос
/ 13 января 2012

Вероятно, будет проще, если вы удалите некоторые из подзапросов. Ваши запросы сводятся к следующему:

Запрос 1: Выбрать все записи из Table_1, где true. Это потому, что в Table_1 есть поля A, которые содержат 3 или 4.

Запрос 2: Выбрать все записи из Table_1, где A равно 3 или A равно 4

В выражении SQL ваши запросы могут быть упрощены до

SELECT * FROM Table_1

и

SELECT * FROM Table_1
WHERE A IN (3,4)
...