Как предикат IN работает в SQL? - программирование
Подтвердить что ты не робот

Как предикат IN работает в SQL?

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

В моем первом задании программирования мне сказали, что запрос в предикате IN () выполняется для каждой строки, содержащейся в родительском запросе, и поэтому следует избегать использования IN.

Например, с учетом запроса:

SELECT count(*) FROM Table1 WHERE Table1Id NOT IN (
SELECT Table1Id FROM Table2 WHERE id_user = 1)
Table1 Rows | # of "IN" executions
----------------------------------
      10    |       10
     100    |      100
    1000    |     1000
   10000    |    10000

Это правильно? Как работает предикат IN?

4b9b3361

Ответ 1

Предупреждение, которое вы получили о выполнении подзапросов для каждой строки, истинно - для взаимосвязанных подзапросов.

SELECT COUNT(*) FROM Table1 a 
WHERE a.Table1id NOT IN (
  SELECT b.Table1Id FROM Table2 b WHERE b.id_user = a.id_user
);

Обратите внимание, что подзапрос ссылается на столбец id_user внешнего запроса. Значение id_user для каждой строки Table1 может быть разным. Таким образом, результат подзапроса, вероятно, будет другим, в зависимости от текущей строки во внешнем запросе. СУРБД должна выполнять подзапрос много раз, один раз для каждой строки во внешнем запросе.

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

PS: В SQL, IN() называется "предикатом", а не выражением. Предикат является частью языка, который оценивает либо true, либо false, но не обязательно может выполняться независимо как оператор. То есть вы не можете просто запустить это как запрос SQL: "2 IN (1,2,3);" Хотя это допустимый предикат, это не действительный оператор.

Ответ 2

Он будет полностью зависеть от используемой базы данных и точного запроса.

Оптимизаторы запросов очень умны - в вашем примере запроса я ожидаю, что лучшие базы данных смогут использовать те же методы, что и при соединении. Более наивные базы данных могут просто выполнять один и тот же запрос много раз.

Ответ 3

Это зависит от RDBMS.

См. подробный анализ здесь:

Короче:

  • MySQL будет оптимизировать запрос к этому:

    SELECT  COUNT(*)
    FROM    Table1 t1
    WHERE   NOT EXISTS
            (
            SELECT  1
            FROM    Table2 t2
            WHERE   t2.id_user = 1
                    AND t2.Table1ID = t1.Table2ID
            )
    

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

    • SQL Server будет использовать MERGE ANTI JOIN.

    Внутренний подзапрос не будет "выполнен" в общем смысле слова, вместо этого результаты как запроса, так и подзапроса будут получены одновременно.

    Подробнее см. ссылку выше.

    • Oracle будет использовать HASH ANTI JOIN.

    Внутренний подзапрос будет выполняться один раз, а хэш-таблица будет построена из набора результатов.

    Значения внешнего запроса будут найдены в хэш-таблице.

    • PostgreSQL будет использовать NOT (HASHED SUBPLAN).

    Гораздо больше, чем Oracle.

Обратите внимание, что переписывание запроса следующим образом:

SELECT  (
        SELECT  COUNT(*)
        FROM    Table1
        ) - 
        (
        SELECT  COUNT(*)
        FROM    Table2 t2
        WHERE   (t2.id_user, t2.Table1ID) IN
                (
                SELECT  1, Table1ID
                FROM    Table1
                )
        )

значительно улучшит производительность во всех четырех системах.

Ответ 4

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

В Oracle, который будет:

EXPLAIN PLAN FOR «your query»

В MySQL или PostgreSQL

EXPLAIN «your query»

Ответ 5

Большинство SQL-систем в наши дни почти всегда будут создавать один и тот же план выполнения для LEFT JOIN, NOT IN и NOT EXISTS

Я бы сказал, посмотрите на свой план выполнения и узнайте: -)

Также, если у вас есть значения NULL для столбца Table1Id, вы не получите никаких данных назад

Ответ 6

Не совсем. Но это масло, чтобы писать такие запросы, используя JOIN

Ответ 7

Да, но выполнение останавливается, как только обработчик запроса "находит" значение, которое вы ищете... Итак, если, например, первая строка во внешнем select имеет Table1Id = 32, и если Table2 имеет запись с TableId = 32, затем  как только подзапрос найдет строку в таблице2, где TableId = 32, она останавливается...