Inlist iterator oracle это

Обновлено: 04.07.2024

* subquery unnesting
подзапрос разворачивается в основной запрос
Пример - Semi join. Преобразование in/exists в join
Oracle ищет строки в правой таблице до первого совпадения (очень быстро по индексу, если в левой таблице немного строк)

* join predicate pushdown
фильтр из внешнего запроса проталкивается ниже, чтобы раньше отфильтровать данные

* join factorization
повторяющаяся часть Union all выносится во внешний запрос, внутри Union all остается только различия

* star transforamtion
join измерений с фильтрами разворачивается в in фильтры
таким образом получаем rowid битмап индексов на каждой колонке, потом мержим битовые карты через and и таблица быстро фильтруется по этим rowid без выполнения соединения

* Table Expansion
На партицированной таблице можно отключить индексы на часто изменяющихся частях. Тогда oracle разобъем запрос на 2 части: full scan по партициям без индекса и index scan с индексом.
Без такого преобразования был бы только full scan.

* or expansion
or Запрос разворачивается Union all или or merge битмап индекса или inlist iterator
Каждая итерация concatenation становится сложней, т.к. нужно доплонительно отфильтровать записи, которые уже попали в предыдущий этап конкатенации
Включение конкатенации при index skip scan: +use_concat(OR_PREDICATES(1))

* matview rewrite
Перезапись части запроса данными matview, если совпадает текст

* px: join filter/bloom
bloom предфильтрация потока 2 по данным потока 1

* транзитивность constraint
к примеру есть функциональный constraint: col2 = trunc(col1, 'yyyy'). При фильтрации по col1 на таблицу будет наложен также и на col2


результрующий запрос после всех преобразований оптимизатора
dbms_sql2.expand_sql_text

Ручная трансформаиця 1 запроса в другой

Статистика

Хинт для задания статистики колонки
+COLUMN_STATS(DD, VN, scale, length=3 distinct=5 nulls=0 min=2 max=10)

статистика по использованию сегмента
запись, полное сканирование, лукап - это инфаормация используется для тепловых карт и автоархивации
Ассоциация статистики к функции
* статистика поумолчанию в селективности и стоимости * диманическая статистика в зависимости от входных параметров через ODCIStatsSelectivity Как описать promo_stats_ot подробней тут

Колонки-кандидаты для гистограммы
Это должен быть столбец часто используемый в where с оператором = (equality)
Статистика ипользования столбца в условиях фильтрации содержится в таблице SYS.COL_USAGE$
Дополнительное условие создание гистограммы - это наличие перекоса в числе строк группы при сборе статистики по числу уникальных значений.

Просмотр данных гистограммы Планируемое число строк = общее число строк таблицы * SUM(frequency подходящее под условие) / последнее endpoint_number
ENDPOINT_REPEAT_COUNT - это число повторений популярного значения в endpoint_actual_value
Если значение не попало в endpoint_actual_value, то число строк = общее число / число уникальных

Join cardinality по гистограмме
если столбец факта сильно перекошен, и этот стоблец используется в join с измерением, то оптимизатор будет использовать стандартный план: число строк факта / селективность измерения , что даст не верный результат
Для обхода можно скопировать гистограмму с перекошенного столбца на столбец с фильтром измерения, тогда оценка части измерения

= оценки части перекошенных данных

Определение селективности, если на обоих столбцах соединения есть гистограмма
* SUM(frequency ведущей гистограммы * frequency ведомой гистограммы) (по всем совпадающим значениям) * селективность фильтра ведущей таблицы
* если это frequency гистограмма и часть данных выпала, то по выпашей части будет дан приблизительный расчет на основе distinct

PLSQL

Автономная транзакция
начинает работать с begin, т.е. все select в declare работают в основной

Иключение при поиске элемента по ключу
Если заранее известно, что exception будет немного или их почти не будет, то этот вариант быстрей всех других: min, цикл, подзапрос
Чем больше exception будет случаться, тем медленней

plsql redefinition
возможность делать ревизии (несколько версий) пакета и переключать через alter session/system.
Т.е. возможно выкладка изменений без остановки работы пользователей.

Консистентность функций
sql функция возвращает данные на момент своего вызова, а не на момент старта основного запроса! (Документация)

Параллелизация pipeline функций

PLSQL коллекции

Varrays - обычный массив
не может быть пропусков, должно быть точно указан размер * .count == .last - число элементов всегда равно индексу последнего
* может быть столбцом в таблице

Hash table - Associative array над связанным списком
ключ ассоциативного массива: VARCHAR2 или PLS_INTEGER + можно использовать для O(1) обращения по ключу
+ так и для прохождения first-last
+ хранится в pga памяти
- нельзя использовать в sql

Nested tables
хранится в автогенерируемой системной таблице
аналогично Varrays индекс массива автогенерируем, таблица обходится через FIRST..LAST
можно удалять элементы в отличии от Varrays, тогда .count < .last

+ может использоваться в запросах
+ может быть колонкой в таблице
+ поддерживает множественные операции в plsql: SET/DISTINCT/NOT/IN
+ может использоваться в bulk операциях select, delete

Анализ производительности запросов

индекс - кандидат на удаление
если "db block changes" > "logical reads"/3 из DBA_HIST_SEG_STAT - т.е. запись превышает логические чтения в 3 раза

forall - в статистике (ash/awr)
будет выглядеть как одно выполнение (exec), но обработавшее N строк.

Выявление skew через oem monitor
* смотрим активность, что несмотря на параллельность db time

= time
* потом делаем монитор в разрезе plan_line - сразу будет видно на каком этапе плана
* потом на parallel будет видно какое parallel set выполнял большую часть работы
* зная plan_line и сервер переходим в статистику выполнения, выбираем из выпадающего списка наш сервер и смотрим нашу plan_line
** в actual rows увидим сколько строк было обработано (оцениваем с общим числом), тамже размер памяти будет

Пометка запроса для awr
dbms_workload_repository.add_colored_sql - запрос всегда будет попадать в awr , несмотря на его частоту и скорость

Чтение плана
Читать план правильно сверху вниз, как будто это стек вызов процедур
Например FILER вначале, вообще может выключить работу sql
Сверху спускаемся до самого глубокого листа и от него стэк разворачивается в обратную сторону

Монотонный рост значений в индексе
может раздуть ITL у индекса, т.к. ITL листа наследуюется при расщеплении блока

Долгий вызов plsql в запросе
функцию можно увидеть в секции projection тормозящей строки плана

Undo/redo при вставке
append - уменьшает undo
append+nologging - уменьшает и undo и redo
CTAS == insert + append

Параллельное последовательное чтение индекса
1 поток читает адреса в связанном списке, а другие потоки считывают сами данные из блоков + дофильтровывают

Result_cache
* есть хинты на
** заставить кэшировать системные объекты
** задать время жизни
* при активации кэша на таблице, нужно проверить что запросов на ней немного и они возвращают небольшое число строк (основное время тратится на запрос, а не на возвращение строк)
* есть блэк лист (или хинтом) - отключить для разовых
* один latch на весь result_cache, так что вставка/чтение блокирует всех остальных

Вставка игнорируя consraint, но с сохранением ошибок а потом смотреть ключи и текст ошибки:
SELECT * FROM EXCEPTIONS;
+IGNORE_ROW_ON_DUPKEY_INDEX - хинт заставляет игнорировать дубликаты ключа при вставке

Пометка блока горячим
dbms_shared_pool.markHot(hash, namespace) - Оракл делает несколько клонов "горячих" объектов в пуле, как следствие соревновательность между сессиями за эти объекты несколько снижается.

Оптимизация хранения

Создание not null поля с default
не создает блоки физически, а только помечает в словаре.
Только при следующих измнеениях обновляется это значение.

Index coalesce
перемещает пустоты в конец индекса, которые потом можно будет использовать при равномерном добавлении (размер индекса не уменьшеается, blvel тоже, индекс и таблица не блокируется)

Вставка в новую таблицу
будет идти медленней, чем в старую, но с truncate. Т.к. asm в новой постоянно выделяет место на диске, а в старой место выделено, просто смещено HWM

Вставка в длинную таблицу
При вставке в таблицу с более 255 колонок, все колонки кладутся в 1 блок
При update строка разбивается на 2 блока, т.к. обновлеяемое значение записывается в новом
Так что такие таблицы имеет смысл ребилдить периодически, для избавления от одноблочных чтений

Include Индекс
Подходи, когда можно все нужные столбцы включить в unique Индекс, чтобы исключить обращение к таблице заменяет 2 индекса: Но c3,c4 не могут использоваться для фильтрации, только для извлечения данных

Дополнительные параметры таблиц в Exadata
Attribute clustering
упорядочивание данных согласно какогото индекса - делается автоматически при вставке и может использоваться exadata софтом при отсечении данных
Zone map
группирует блоки таблицы по какому то столбцу и в zone-map записывает верхнее и нижнее значение конкретной части, что также может использоваться Exadata для отсечения данных
группировать можно по значениям другой таблицы, по join запросу
Отрицательная эффективность Exadata
Может быть двойное разжатие, если после разжатия в Exadata получился размер CU больше 1МБ, тогда в бд отсылается сжатый вариант и там разжимается повторно
Так что сильное сжатие может дать отрицательную эффективность при колоночном сжатии в Exadata.

Структура Lob
lob состоит из 2 частей:
* lob locator (индекс, указывающий на части lob - хранится непосредственно в таблице)
* lob value - само значение хранится отдельно от таблицы (если больше 4000 байт)

Партицирование

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

Reference partitions
т.е. в строках нет даты, она автоматом подтягивается по FK из заголовка и партицируется по ней
У такого способопа партицирования плохая производительность.

* IOT могут быть партицированы по range/hash
* object table - также можно пратицировать.
* nested table - партицируется аналогично родительской

Глобальные индексы
можно партицировать только по range и hash

INDEXING OFF|On
можно задать активность индексов на конкретной партиции (ora 12)
индекс при этом должен быть создан как INDEXING PARTIAL
при запросе на партиции без индекса и с ним, будет конкатенация (см. раздел трансформаций)

Тепловая карта партиций
на основании статистики использования партиций (DBA_HEAT_MAP_SEG_HISTOGRAM) можно включить компресию

Настройки бд

Виды репликаций
* Statement: передаются запросы
+ меньше места, т.к. один запрос может обновлять N строк
- недерменированность резальутата в разных средах (NOW(), RAND() и прочее)
* передаются изменения данных:
+ однозначность результата
- большие объем

Exadata 12.2
* кэширование temp в flash disk (раньше только физ. диски) - ускорение!
* smart scan на сжатых индексах (раньше не работало, только бд)
* lob до 4 кб смогут использоваться в smart scan (иначе на бд)

Особенности только в exadata
* возможно для избранных таблиц делать keep flash cache
* _serial_direct_read=true - читать минуя буферный кэш
( минус в том, что бы его выполнить, нужно сбросить все данные из кэша на диск, чтобы прямым чтением забрались последние данные )
* частые запросы не используют smartscan, по тому, что таблица целиком уходит в кэш
* для inmemory можно использовать flash cache exadata

Разные SQL алгоритмы

Пагинация на ключах
Чтобы быстро пагинировать и не допускать съезжание страниц (если данные на предыдущую страницу добавилась между переключениями), нужно запоминать ID последней записи на странице и при переходе на следующую фильтровать по ней.
Так не надо будет сортировать все сначала, а достаточно пройтись по индексу нужные N записей и остановиться.
start_of_group - нумерация групп по разрывам
lag(a, 1, a) over (order by b) start_of_group -> sum(start_of_group) over(order by a)
определение начала группы, потом нарастающий итог по группам, чтобы их пронумеровать

Забор таблицы частями без fullscan, индексов и партиций
Таблица можно забирать по экстентно:
1. Определяем диапазон блоков, которые входят в экcтент.
Сортируем экстенты таблицы (v_table) по rowid и набираем нужное число блоков (num_part) на основании размеров в bytes.
2. Получаем идеально ровные 3 части данных по 5.3ГБ: 3. Формируем диапазон rowid для забора:
Размеры пачек получаются с разным количеством строк, но равные по размеру в байтах. Это обеспечит идеальное равномерное копирование данных, без перекоса при передаче по сети.
Сверяемся с полной таблицей: 4. Каждый поток забирает свой диапазон rowid
+ каждый поток читает свои блоки, нет полных или повторных сканирований
+ не нужны партиции или индексы
+ идеальное равномерное распределние по объему в байтах
+ мгновенный доступ по идентификаторам блоков (быстрей индексов и лучше работает на больших диапазонах)
+ не нужно указывать при заборе: по какому полю, в каких границах. Все можно сделать автоматически.
- В полной мере можно использовать только для полных заборов таблиц или партиций.
Сверху можно наложить дополнительные фильтры, но это может дать сильные перекосы данных между потоками забора, т.к. неизвестно в каком из блоков лежит нужная часть данных.

Данный подход используется в Sqoop с включенной опцией OraOop. Этот же механизм забора можно реализовать в Spark, но вручную сформировав массив предикатов алгоритмом выше.

поиск одного пропуска
* математической формулой: ( max-min ) * ( max - min + 1 ) / 2 + (count + 1) * min - sum --

= квадрат разницы/2 + число элементов - сумма
* через minus / not exists с генеренной полной последовательность тоже будет быстро (2 FTS + antijoin/sort)
* если n возрастающее число, то можно так: (1 FTS, возможно сортировка для аналитики)
Вставка данных больше размера varchar
при вставке в varchar данных больше 4000, то он обрезается до максимума
если вставляется в середину, строка обрежется по месту вставки
partition join
Добавляет недостающие данные в факте по f.cust_id (не нужно самому генерить)
Округление через to int
Удаление из обновляемого представления возьмется таблица с максимальным ключом, которая однозначно определяем результат джойна
если у sku ключ из 2 полей, а planarriv из 3, то возьмется максимальный с 3 полями: planarriv


Выражение на месте join если расставить скобки:
DBMS_HS_PASSTHROUGH - Полное выполнение запроса на удаленной бд
пакет DBMS_HS_PASSTHROUGH для выполнения запросов по дблинку к другим бд (все выполняется на стороне источника)

Не найдя на хабре статьи, объединяющей в удобном для чтения виде информацию о методах доступа к данным, используемых СУБД Oracle, я решил совершить «пробу пера» и написать эту статью.

Общая информация

Не углубляясь в детали, можно утверждать что Oracle хранит данные в таблицах, вместе с которыми могут существовать особые структуры данных – индексы, призванные ускорить запросы к таблицам. При выполнении запросов Oracle по-разному обращается к таблицам и индексам – способы доступа к данным в различных ситуациях и являются предметом этой статьи.

Для примеров мы будем использовать следующую таблицу и данные в ней:

Для анализа плана выполнения запроса будем пользоваться следующими средствами:

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

TABLE FULL SCAN

Данный метод доступа, как следует из названия, подразумевает перебор всех строк таблицы с исключением тех, которые не удовлетворяют предикату where (если таковой есть). Применяется он либо в случае, когда условия предиката отсутствуют в индексе, либо когда индекса нет в принципе. Примеры:

TABLE ACCESS BY ROWID, он же ROWID

  • Мы указали идентификатор строки в предикате where;
  • ROWID запрошенной записи был найден в индексе;

INDEX FULL SCAN

Данный метод доступа просматривает все листовые блоки индекса для поиска соответствий условиям предиката. Для того чтобы Oracle мог применить этот метод доступа, хотя бы одно из полей ключа должно иметь ограничение NOT NULL, т.к. только в этом случае соответствующая строка таблицы попадет в индекс. Этот метод обычно быстрее чем TABLE FULL SCAN, но медленнее, чем INDEX RANGE SCAN (см. ниже).

INDEX FAST FULL SCAN

Этот метод доступа применяется, когда выполнены все требования для INDEX FULL SCAN, а также все данные, выбираемые запросом, содержатся в индексе и таким образом доступ к самой таблице не требуется. В отличие от INDEX FULL SCAN этот метод может читать блоки индекса в несколько параллельных потоков и таким образом порядок возвращаемых значений не регламентирован. Oracle также не может использовать этот метод для bitmap-индексов.

INDEX RANGE SCAN

Данный метод доступа используется Oracle в том случае, если в предикат where входят столбцы индекса с условиями = (в случае если индексированные значения неуникальны), >, <, а также like ‘pattern%’, причем wildcard-символы должны стоять после искомой подстроки. В отличие от TABLE FULL SCAN, при использовании этого метода доступа Oracle не перебирает все листовые блоки и поэтому в большинстве случаев INDEX RANGE SCAN быстрее.
Пример:

INDEX UNIQUE SCAN

Данный метод доступа применяется когда в силу ограничений UNIQUE/PRIMARY KEY, а также условия предиката, запрос должен вернуть ноль или одно значение.
Пример:

INDEX SKIP SCAN

Этот метод доступа используется в случае, если в предикате where не используется первый столбец индекса.
Для примера использования этого метода доступа нам потребуется другая таблица (обратите внимание, что количество строк, данные и т.д. будут зависеть от того, что есть в используемой схеме, и поэтому данный пример может не воспроизвестись сразу):

У меня есть следующий запрос (это упрощенная версия гораздо более сложного запроса):

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

Мой вопрос в том, как Oracle будет оптимизировать этот запрос, если индексы ProjectId и VersionId будут проиндексированы. Будет ли список преобразован в хеш-таблицу, похожую на join на временную таблицу? Или каждый ключевой поиск будет выполняться по одному за раз?

Я попробовал этот запрос в моей тестовой базе данных и получил:

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

Это похоже на индекс, однако я не уверен, что означает INLIST ITERATOR. Я предполагаю, что это означает, что Oracle выполняет итерацию через список и делает доступ к таблице для каждого элемента в списке, что, вероятно, не будет слишком эффективным с тысячами ключей. Однако, возможно, Oracle достаточно умен, чтобы оптимизировать это лучше, если бы я действительно дал ему несколько тысяч ключей.

ПРИМЕЧАНИЕ.. Я не хочу загружать эти ключи в временную таблицу, потому что, откровенно говоря, мне не нравится, как временные таблицы работают в Oracle, и обычно они больше разочарованы, чем они (в моем не-экспертном заключении все равно).

Оптимизатор должен основывать свое решение на количестве элементов в списке и количестве строк в таблице. Если в таблице есть миллионы строк, и в списке есть даже несколько тысяч элементов, я бы обычно ожидал, что он будет использовать индекс, чтобы сделать несколько тысяч однострочных запросов. Если таблица имеет несколько тысяч строк, а в списке есть несколько тысяч элементов, я бы ожидал, что оптимизатор выполнит полное сканирование таблицы. В середине, конечно, есть место, где происходит все интересное, и где сложнее выработать то, что планирует оптимизатор.

В целом, однако, динамическое построение такого запроса будет проблематичным с точки зрения производительности не из-за того, насколько дорого выполняется конкретное выполнение запроса, а потому, что запросы, которые вы генерируете, не являются доступными. Поскольку вы не можете использовать переменные связывания (или, если вы используете переменные связывания, вам понадобится другое число переменных привязки). Это заставляет Oracle делать довольно дорогостоящий синтаксический анализ запроса каждый раз и оказывает давление на ваш общий пул, который, скорее всего, вытеснит другие связанные друг с другом вопросы, которые вызовут более жесткий синтаксический разбор в системе. Обычно вам будет лучше подавать данные, которые вы хотите сопоставить, во временную таблицу (или даже постоянную таблицу), чтобы затем ваш запрос можно было сделать совместным и разобранным только один раз.

К комментарию Бранко, в то время как Oracle ограничен 1000 литералами в списке IN , то есть только в том случае, если вы используете "нормальный" синтаксис, т.е.

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

Так, например, я получу ошибку, если я создам запрос с 2000 элементами в списке IN

Клёвые оптимизации SQL, не зависящие от стоимостной модели. Часть 3 - 1

Клёвые оптимизации SQL, не зависящие от стоимостной модели. Часть 1 Клёвые оптимизации SQL, не зависящие от стоимостной модели. Часть 2

6. Слияние предикатов

Это — интересная возможность, на которой я когда-то споткнулся, ошибочно предположив, что моя СУБД на такое способна. Рассмотрим следующий запрос: Очевидно, что два предиката пересекаются и их можно слить воедино. Можно ожидать, что база данных преобразует вышеприведенный запрос в следующее: Выглядит совершенно очевидным, правда? Это – более сложный случай транзитивного замыкания. Еще один его случай — слияние двух диапазонов. При выполнении запроса: мы надеемся, что база данных перепишет запрос следующим образом: Кардинальность второго варианта будет равна 2 строкам, но в первом база данных может не понять, что диапазоны можно объединить, и выберет полный просмотр таблицы, хотя должна была бы воспользоваться индексом. Какие же базы данных способны на эти оптимизации?

Слияние предикатов IN Да Слияние диапазонных предикатов Да (но пусть план не вводит вас в заблуждение!) Как вы можете видеть, предикат был оптимизирован не полностью. Фильтр (SARG), проверяющий на попадание между нижней и верхней границами объединенного диапазона, на месте, но более важны операции START и STOP, указывающие на быстрый доступ по индексу. Кроме того, кардинальность тоже такая, какая и должна быть. Если хотите убедиться, выполните запрос со следующим невозможный предикатом и вы получите правильный план:

MySQL

Oracle

Слияние предикатов IN Да Примененный предикат включает только значения 2 и 3, так что преобразование сработало правильно. Слияние диапазонных предикатов Опять же — да:

PostgreSQL

Слияние предикатов IN Увы, нет, оптимизации не происходит! Оба предиката по-прежнему присутствуют в плане выполнения, да и оценка кардинальности ошибочна, должно быть 2, а не 1. Если преобразовать запрос вручную, мы получили бы следующий план запроса: В частности, мы видим неправильный план в случае, когда два предиката не пересекаются и формируется "невозможный" предикат: Облом! Слияние диапазонных предикатов Выглядит не лучше: Сложно сказать, получилось или нет. В конце концов, мы получили правильный план с разумной кардинальностью, так что все может работать, как и на DB2. Но что произойдет, опять же, если создать "невозможный" предикат? План стал хуже: Кардинальность повысилась, вместо того, чтобы понизиться. И, в конце концов, такой запрос вообще не должен выполняться. Жирный минус PostgreSQL.

SQL Server

Слияние предикатов IN Да, всё работает: Слияние диапазонных предикатов Опять же похоже на случай DB2: К сожалению, обратите внимание на различие между SEEK и WHERE. Хотелось бы видеть диапазон [99, 100] в SEEK, как в DB2, поскольку SEEK выполняется быстро благодаря доступу по индексу за время O(log N), в то время как время доступа WHERE растет линейно, порядка O(N). Облом! Мне кажется, что это программная ошибка, ведь невозможный предикат приводит к гораздо более обоснованному:

Резюме

Не забывайте, что есть множество предикатов, которые сливаются правильно в одних базах данных, а в других – нет. Если сомневаетесь – обязательно проверьте план выполнения!
База данных Слияние IN Слияние диапазонов
DB2 LUW 10.5 Да Да
MySQL 8.0.2 Да Да
Oracle 12.2.0.1 Да Да
PostgreSQL 9.6 Нет Нет
SQL Server 2014 Да Нет

7. Доказуемо пустые множества

Соединение доказуемо пустого множества (предикат IS NULL): Соединение доказуемо пустого множества (INTERSECT): Полусоединение доказуемо пустого множества (предикат IS NULL): Полусоединение доказуемо пустого множества (INTERSECT): Вау, круто! Похоже, победитель забега!

MySQL

Соединение доказуемо пустого множества (предикат IS NULL): Круто, я не ожидал! Соединение доказуемо пустого множества (INTERSECT): Увы, MySQL не поддерживает INTERSECT. Полусоединение доказуемо пустого множества (предикат IS NULL): Полусоединение доказуемо пустого множества (INTERSECT): Увы, MySQL не поддерживает INTERSECT. Но все равно, MySQL демонстрирует отличный результат!

Oracle

Соединение доказуемо пустого множества (предикат IS NULL): Опять же, очень странный план выполнения в Oracle, но фильтр NULL IS NOT NULL на месте, и он находится перед всеми остальными операциями, которые, таким образом, не выполняются. Соединение доказуемо пустого множества (INTERSECT): Забавно. Как видим, при этом плане выполнения происходит просмотр всего первичного ключа таблицы FILM_ACTOR. Это может избавить от обращения к таблице ACTOR и индексу первичного ключа, поскольку сначала обрабатывается производная таблица (в которой нет ни одной строки), но операций с и 6 все же тут быть не должно. Облом! Полусоединение доказуемо пустого множества (предикат IS NULL): А это снова выполняется правильно: . с тем же странным планом выполнения, содержащим не выполняемое поддерево. Полусоединение доказуемо пустого множества (INTERSECT): Опять же, никакой оптимизации: Не слишком хорошие результаты!

PostgreSQL

К вящему разочарованию, PostgreSQL в этом эксперименте показывает себя не с лучшей стороны! Соединение доказуемо пустого множества (предикат IS NULL): Не-а: Соединение доказуемо пустого множества (INTERSECT): Еще хуже: Полусоединение доказуемо пустого множества (предикат IS NULL): Так же, как и в случае со внутренним соединением: Полусоединение доказуемо пустого множества (INTERSECT): Как и ожидалось:

SQL Server

SQL Server тут во всей красе, как и DB2! Соединение доказуемо пустого множества (предикат IS NULL): Соединение доказуемо пустого множества (INTERSECT): Полусоединение доказуемо пустого множества (предикат IS NULL): Полусоединение доказуемо пустого множества (INTERSECT):

Читайте также: