42.2. Система правил и представления
Представления в Postgres Pro реализованы на основе системы правил. Фактически по сути нет никакого отличия
CREATE VIEW myview AS SELECT * FROM mytab;
от следующих двух команд:
CREATE TABLE myview (same column list as mytab
);
CREATE RULE "_RETURN" AS ON SELECT TO myview DO INSTEAD
SELECT * FROM mytab;
так как именно эти действия CREATE VIEW
выполняет внутри. Это имеет некоторые побочные эффекты. В частности, информация о представлениях в системных каталогах Postgres Pro ничем не отличается от информации о таблицах. Поэтому при анализе запроса нет абсолютно никакой разницы между таблицами и представлениями. Они представляют собой одно и то же — отношения.
42.2.1. Как работают правила SELECT
Правила ON SELECT
применяются ко всем запросам на последнем этапе, даже если это команда INSERT
, UPDATE
или DELETE
. Эти правила отличаются от правил других видов тем, что они модифицируют непосредственно дерево запросов, а не создают новое. Поэтому мы начнём описание с правил SELECT
.
В настоящее время возможно только одно действие в правиле ON SELECT
и это должно быть безусловное действие SELECT
, выполняемое в режиме INSTEAD
. Это ограничение было введено, чтобы сделать правила достаточно безопасными для применения обычными пользователями, так что действие правил ON SELECT
сводится к реализации представлений.
В примерах этой главы рассматриваются два представления с соединением, которые выполняют некоторые вычисления, и которые, в свою очередь, используются другими представлениями. Первое из этих двух представлений затем модифицируется, к нему добавляются правила для операций INSERT
, UPDATE
и DELETE
, так что в итоге получается представление, которое работает как обычная таблица с некоторыми необычными функциями. Это не самый простой пример для начала, поэтому понять некоторые вещи будет сложнее. Но лучше иметь один пример, поэтапно охватывающий все обсуждаемые здесь темы, чем несколько различных, при восприятии которых в итоге может возникнуть путаница.
Таблицы, которые понадобятся нам для описания системы правил, выглядят так:
CREATE TABLE shoe_data ( shoename text, -- первичный ключ sh_avail integer, -- число имеющихся пар slcolor text, -- предпочитаемый цвет шнурков slminlen real, -- минимальная длина шнурков slmaxlen real, -- максимальная длина шнурков slunit text -- единица длины ); CREATE TABLE shoelace_data ( sl_name text, -- первичный ключ sl_avail integer, -- число имеющихся пар sl_color text, -- цвет шнурков sl_len real, -- длина шнурков sl_unit text -- единица длины ); CREATE TABLE unit ( un_name text, -- первичный ключ un_fact real -- коэффициент для перевода в см );
Как можно догадаться, в них хранятся данные обувной фабрики.
Представления создаются так:
CREATE VIEW shoe AS SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, sh.slminlen * un.un_fact AS slminlen_cm, sh.slmaxlen, sh.slmaxlen * un.un_fact AS slmaxlen_cm, sh.slunit FROM shoe_data sh, unit un WHERE sh.slunit = un.un_name; CREATE VIEW shoelace AS SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm FROM shoelace_data s, unit u WHERE s.sl_unit = u.un_name; CREATE VIEW shoe_ready AS SELECT rsh.shoename, rsh.sh_avail, rsl.sl_name, rsl.sl_avail, least(rsh.sh_avail, rsl.sl_avail) AS total_avail FROM shoe rsh, shoelace rsl WHERE rsl.sl_color = rsh.slcolor AND rsl.sl_len_cm >= rsh.slminlen_cm AND rsl.sl_len_cm <= rsh.slmaxlen_cm;
Команда CREATE VIEW
для представления shoelace
(самого простого из имеющихся) создаёт отношение shoelace
и запись в pg_rewrite
о правиле перезаписи, которое должно применяться, когда в запросе на выборку задействуется отношение shoelace
. Для этого правила не задаются условия применения (о них рассказывается ниже, в описании правил не для SELECT
, так как правила SELECT
в настоящее бывают только безусловными) и оно действует в режиме INSTEAD
. Заметьте, что условия применения отличаются от условий фильтра запроса, например, действие для нашего правила содержит условие фильтра. Действие правила выражается одним деревом запроса, которое является копией оператора SELECT
в команде, создающей представление.
Примечание
Два дополнительных элемента списка отношений NEW
и OLD
, которые можно увидеть в соответствующей строке pg_rewrite
, не представляют интереса для правил SELECT
.
Сейчас мы наполним таблицы unit
(единицы измерения), shoe_data
(данные о туфлях) и shoelace_data
(данные о шнурках) и выполним простой запрос к представлению:
INSERT INTO unit VALUES ('cm', 1.0); INSERT INTO unit VALUES ('m', 100.0); INSERT INTO unit VALUES ('inch', 2.54); INSERT INTO shoe_data VALUES ('sh1', 2, 'black', 70.0, 90.0, 'cm'); INSERT INTO shoe_data VALUES ('sh2', 0, 'black', 30.0, 40.0, 'inch'); INSERT INTO shoe_data VALUES ('sh3', 4, 'brown', 50.0, 65.0, 'cm'); INSERT INTO shoe_data VALUES ('sh4', 3, 'brown', 40.0, 50.0, 'inch'); INSERT INTO shoelace_data VALUES ('sl1', 5, 'black', 80.0, 'cm'); INSERT INTO shoelace_data VALUES ('sl2', 6, 'black', 100.0, 'cm'); INSERT INTO shoelace_data VALUES ('sl3', 0, 'black', 35.0, 'inch'); INSERT INTO shoelace_data VALUES ('sl4', 8, 'black', 40.0, 'inch'); INSERT INTO shoelace_data VALUES ('sl5', 4, 'brown', 1.0, 'm'); INSERT INTO shoelace_data VALUES ('sl6', 0, 'brown', 0.9, 'm'); INSERT INTO shoelace_data VALUES ('sl7', 7, 'brown', 60, 'cm'); INSERT INTO shoelace_data VALUES ('sl8', 1, 'brown', 40, 'inch'); SELECT * FROM shoelace; sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm -----------+----------+----------+--------+---------+----------- sl1 | 5 | black | 80 | cm | 80 sl2 | 6 | black | 100 | cm | 100 sl7 | 7 | brown | 60 | cm | 60 sl3 | 0 | black | 35 | inch | 88.9 sl4 | 8 | black | 40 | inch | 101.6 sl8 | 1 | brown | 40 | inch | 101.6 sl5 | 4 | brown | 1 | m | 100 sl6 | 0 | brown | 0.9 | m | 90 (8 rows)
Это самый простой запрос SELECT
, который можно выполнить с нашими представлениями, и мы воспользуемся этим, чтобы объяснить азы правил представлений. Запрос SELECT * FROM shoelace
интерпретируется анализатором запросов и преобразуется в дерево запроса:
SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace shoelace;
Это дерево передаётся в систему правил, которая проходит по списку отношений и проверяет, есть ли какие-либо правила для этих отношений. Обрабатывая элемент отношения shoelace
(сейчас он единственный), система правил находит правило _RETURN
с деревом запроса:
SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm FROM shoelace old, shoelace new, shoelace_data s, unit u WHERE s.sl_unit = u.un_name;
Чтобы развернуть представление, механизм перезаписи просто формирует новый элемент для списка отношений — подзапрос, содержащий дерево действия правила, и подставляет этот элемент вместо исходного, на который ссылалось представление. Получившееся перезаписанное дерево запроса будет почти таким как дерево запроса:
SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM (SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm FROM shoelace_data s, unit u WHERE s.sl_unit = u.un_name) shoelace;
Однако есть одно различие: в списке отношений подзапроса будут содержаться два дополнительных элемента: shoelace old
и shoelace new
. Эти элементы не принимают непосредственного участия в запросе, так как они не задействованы в дереве соединения подзапроса и в целевом списке. Механизм перезаписи использует их для хранения информации о проверке прав доступа, которая изначально хранилась в элементе, указывающем на представление. Таким образом, исполнитель будет по-прежнему проверять, имеет ли пользователь необходимые права для доступа к представлению, хотя в перезаписанном запросе это представление не фигурирует непосредственно.
Так было применено первое правило. Система правил продолжит проверку оставшихся элементов списка отношений на верхнем уровне запроса (в данном случае таких элементов нет) и рекурсивно проверит элементы списка отношений в добавленном подзапросе, не ссылаются ли они на представления. (Но old
и new
разворачиваться не будут — иначе мы получили бы бесконечную рекурсию!) В этом примере для shoelace_data
и unit
нет правил перезаписи, так что перезапись завершается и результат, полученный выше, передаётся планировщику.
Сейчас мы хотим написать запрос, который выбирает туфли из имеющихся в данный момент, для которых есть подходящие шнурки (по цвету и длине) и число готовых пар больше или равно двум.
SELECT * FROM shoe_ready WHERE total_avail >= 2; shoename | sh_avail | sl_name | sl_avail | total_avail ----------+----------+---------+----------+------------- sh1 | 2 | sl1 | 5 | 2 sh3 | 4 | sl7 | 7 | 4 (2 rows)
На этот раз анализатор запроса выводит такое дерево:
SELECT shoe_ready.shoename, shoe_ready.sh_avail, shoe_ready.sl_name, shoe_ready.sl_avail, shoe_ready.total_avail FROM shoe_ready shoe_ready WHERE shoe_ready.total_avail >= 2;
Первое правило применяется к представлению shoe_ready
и в результате получается дерево запроса:
SELECT shoe_ready.shoename, shoe_ready.sh_avail, shoe_ready.sl_name, shoe_ready.sl_avail, shoe_ready.total_avail FROM (SELECT rsh.shoename, rsh.sh_avail, rsl.sl_name, rsl.sl_avail, least(rsh.sh_avail, rsl.sl_avail) AS total_avail FROM shoe rsh, shoelace rsl WHERE rsl.sl_color = rsh.slcolor AND rsl.sl_len_cm >= rsh.slminlen_cm AND rsl.sl_len_cm <= rsh.slmaxlen_cm) shoe_ready WHERE shoe_ready.total_avail >= 2;
Подобным образом, правила для shoe
и shoelace
подставляются в список отношений, что даёт окончательное дерево запроса:
SELECT shoe_ready.shoename, shoe_ready.sh_avail, shoe_ready.sl_name, shoe_ready.sl_avail, shoe_ready.total_avail FROM (SELECT rsh.shoename, rsh.sh_avail, rsl.sl_name, rsl.sl_avail, least(rsh.sh_avail, rsl.sl_avail) AS total_avail FROM (SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, sh.slminlen * un.un_fact AS slminlen_cm, sh.slmaxlen, sh.slmaxlen * un.un_fact AS slmaxlen_cm, sh.slunit FROM shoe_data sh, unit un WHERE sh.slunit = un.un_name) rsh, (SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm FROM shoelace_data s, unit u WHERE s.sl_unit = u.un_name) rsl WHERE rsl.sl_color = rsh.slcolor AND rsl.sl_len_cm >= rsh.slminlen_cm AND rsl.sl_len_cm <= rsh.slmaxlen_cm) shoe_ready WHERE shoe_ready.total_avail > 2;
Это может показаться неэффективным, но планировщик преобразует этот запрос в одноуровневое дерево, «подтягивая» подзапросы в главный запрос, а затем планирует соединения так же, как и при явной записи с соединениями. Таким образом, упрощение дерева запросов является оптимизацией, которая производится независимо от перезаписи запросов.
42.2.2. Правила представлений не для SELECT
До этого в описании правил представлений не затрагивались два компонента дерева запросов — тип команды и результирующее отношение. На самом деле, тип команды не важен для правил представления, но результирующее отношение может повлиять на работу механизма перезаписи, потому что если это представление, требуются дополнительные операции.
Есть только несколько отличий между деревом запроса для SELECT
и деревом для другой команды. Очевидно, у них различные типы команд, и для команды, отличной от SELECT
, результирующее отношение указывает на элемент в списке отношений, куда должен попасть результат. Все остальные компоненты в точности те же. Поэтому, например, если взять таблицы t1
и t2
со столбцами a
и b
, деревья запросов для этих операторов:
SELECT t2.b FROM t1, t2 WHERE t1.a = t2.a; UPDATE t1 SET b = t2.b FROM t2 WHERE t1.a = t2.a;
будут практически одинаковыми. В частности:
Списки отношений содержат элементы для таблиц
t1
иt2
.Выходные списки содержат одну переменную, указывающую на столбец
b
элемента-отношения для таблицыt2
.Выражения условий сравнивают столбцы
a
обоих элементов-отношений на равенство.Деревья соединений показывают простое соединение между
t1
иt2
.
Как следствие, для обоих деревьев строятся похожие планы выполнения, с соединением двух таблиц. Для UPDATE
планировщик добавляет в выходной список недостающие столбцы из t1
и окончательное дерево становится таким:
UPDATE t1 SET a = t1.a, b = t2.b FROM t2 WHERE t1.a = t2.a;
В результате исполнитель, обрабатывающий соединение, выдаёт тот же результат, что и запрос:
SELECT t1.a, t2.b FROM t1, t2 WHERE t1.a = t2.a;
Но с UPDATE
есть маленькая проблема: часть плана исполнителя, в которой выполняется соединение, не представляет, для чего предназначены результаты соединения. Она просто выдаёт результирующий набор строк. Фактически есть одна команда SELECT
, а другая, UPDATE
, обрабатывается исполнителем выше, где он уже знает, что это команда UPDATE
и что результат должен попасть в таблицу t1
. Но какие из строк таблицы должны заменяться новыми?
Для решения этой проблемы в выходной список операторов UPDATE
(и DELETE
) добавляется ещё один элемент: идентификатор текущего кортежа (Current Tuple ID, CTID). Это системный столбец, содержащий номер блока в файле и позицию строки в блоке. Зная таблицу, по CTID можно получить исходную строку в t1
, подлежащую изменению. С добавленным в выходной список CTID запрос фактически выглядит так:
SELECT t1.a, t2.b, t1.ctid FROM t1, t2 WHERE t1.a = t2.a;
Теперь мы перейдём ещё к одной особенности Postgres Pro. Старые строки таблицы не переписываются, поэтому ROLLBACK
выполняется быстро. С командой UPDATE
в таблицу вставляется новая строка результата (без CTID) и в заголовке старой строки, на которую указывает CTID, в поля cmax
и xmax
записываются текущий счётчик команд и идентификатор текущей транзакции. Таким образом, старая строка оказывается скрытой и после фиксирования транзакции процесс очистки может окончательно удалить неактуальную версию строки.
Зная всё это, мы можем применять правила представлений абсолютно таким же образом к любой команде — никаких различий нет.
42.2.3. Преимущества представлений в Postgres Pro
Выше было показано, как система правил внедряет определения представлений в исходное дерево запроса. Во втором примере простой запрос SELECT
к одному представлению создал окончательное дерево запроса, соединяющее 4 таблицы (таблица unit
использовалась дважды с разными именами).
Преимущество реализации представлений через систему правил заключается в том, что планировщик получает в одном дереве запроса всю информацию о таблицах, которые нужно прочитать, о том, как связаны эти таблицы, об условиях в представлениях, а также об условиях, заданных в исходном запросе. И всё это имеет место, когда сам исходный запрос представляет собой соединение представлений. Планировщик должен выбрать лучший способ выполнения запроса, и чем больше информации он получит, тем лучше может быть его выбор. И то, как в Postgres Pro реализована система правил, гарантирует, что ему поступает вся информация, собранная о запросе на данный момент.
42.2.4. Изменение представления
Но что произойдёт, если записать имя представления в качестве целевого отношения команды INSERT
, UPDATE
или DELETE
? Если проделать подстановки, описанные выше, будет получено дерево запроса, в котором результирующее отношение указывает на элемент-подзапрос, что не будет работать. Однако Postgres Pro даёт ряд возможностей, чтобы сделать представления изменяемыми.
Если подзапрос выбирает данные из одного базового отношения и он достаточно прост, механизм перезаписи может автоматически заменить его нижележащим базовым отношением, чтобы команды INSERT
, UPDATE
или DELETE
обращались к базовому отношению. Представления, «достаточно простые» для этого, называются автоматически изменяемыми. Подробнее виды представлений, которые могут изменяться автоматически, описаны в CREATE VIEW.
Эту задачу также можно решить, создав триггер INSTEAD OF
для представления. В этом случае перезапись будет работать немного по-другому. Для INSERT
механизм перезаписи не делает с представлением ничего, оставляя его результирующим отношением запроса. Для UPDATE
и DELETE
ему по-прежнему придётся разворачивать запрос представления, чтобы получить «старые» строки, которые эта команда попытается изменить или удалить. Поэтому представление разворачивается как обычно, но в запрос добавляется ещё один элемент списка отношений, указывающий на представление в роли результирующего отношения.
При этом возникает проблема идентификации строк в представлении, подлежащих изменению. Вспомните, что когда результирующее отношение является таблицей, в выходной список добавляется специальное поле CTID, указывающее на физическое расположение изменяемых строк. Но это не будет работать, когда результирующее отношение — представление, так как в представлениях нет CTID, потому что их строки физически нигде не находятся. Вместо этого, для операций UPDATE
или DELETE
в выходной список добавляется специальный элемент wholerow
(вся строка), который разворачивается в содержимое всех столбцов представления. Используя этот элемент, исполнитель передаёт строку «old» в триггер INSTEAD OF
. Какие именно строки должны изменяться фактически, будет решать сам триггер, исходя из полученных значений старых и новых строк.
Кроме того, пользователь может определить правила INSTEAD
, в которых задать действия замены для команд INSERT
, UPDATE
и DELETE
с представлением. Эти правила обычно преобразуют команду в другую команду, изменяющую одну или несколько таблиц, а не представление. Эта тема освещается в Разделе 42.4.
Заметьте, что такие правила вычисляются сначала, перезаписывая исходный запрос до того, как он будет планироваться и выполняться. Поэтому, если для представления определены и триггеры INSTEAD OF
, и правила для INSERT
, UPDATE
или DELETE
, сначала вычисляются правила, а в зависимости от их действия, триггеры могут не вызываться вовсе.
Автоматическая перезапись запросов INSERT
, UPDATE
или DELETE
с простыми представлениями всегда производится в последнюю очередь. Таким образом, если у представления есть правила или триггеры, они переопределяют поведение автоматически изменяемых представлений.
Если для представления не определены правила INSTEAD
или триггеры INSTEAD OF
, и запрос не удаётся автоматически переписать в виде обращения к нижележащему базовому отношению, возникает ошибка, потому что исполнитель не сможет изменить такое представление.
9.23. Subquery Expressions
This section describes the SQL-compliant subquery expressions available in Postgres Pro. All of the expression forms documented in this section return Boolean (true/false) results.
9.23.1. EXISTS
EXISTS (subquery
)
The argument of EXISTS
is an arbitrary SELECT
statement, or subquery. The subquery is evaluated to determine whether it returns any rows. If it returns at least one row, the result of EXISTS
is “true”; if the subquery returns no rows, the result of EXISTS
is “false”.
The subquery can refer to variables from the surrounding query, which will act as constants during any one evaluation of the subquery.
The subquery will generally only be executed long enough to determine whether at least one row is returned, not all the way to completion. It is unwise to write a subquery that has side effects (such as calling sequence functions); whether the side effects occur might be unpredictable.
Since the result depends only on whether any rows are returned, and not on the contents of those rows, the output list of the subquery is normally unimportant. A common coding convention is to write all EXISTS
tests in the form EXISTS(SELECT 1 WHERE ...)
. There are exceptions to this rule however, such as subqueries that use INTERSECT
.
This simple example is like an inner join on col2
, but it produces at most one output row for each tab1
row, even if there are several matching tab2
rows:
SELECT col1 FROM tab1 WHERE EXISTS (SELECT 1 FROM tab2 WHERE col2 = tab1.col2);
9.23.2. IN
expression
IN (subquery
)
The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result. The result of IN
is “true” if any equal subquery row is found. The result is “false” if no equal row is found (including the case where the subquery returns no rows).
Note that if the left-hand expression yields null, or if there are no equal right-hand values and at least one right-hand row yields null, the result of the IN
construct will be null, not false. This is in accordance with SQL's normal rules for Boolean combinations of null values.
As with EXISTS
, it's unwise to assume that the subquery will be evaluated completely.
row_constructor
IN (subquery
)
The left-hand side of this form of IN
is a row constructor, as described in Section 4.2.13. The right-hand side is a parenthesized subquery, which must return exactly as many columns as there are expressions in the left-hand row. The left-hand expressions are evaluated and compared row-wise to each row of the subquery result. The result of IN
is “true” if any equal subquery row is found. The result is “false” if no equal row is found (including the case where the subquery returns no rows).
As usual, null values in the rows are combined per the normal rules of SQL Boolean expressions. Two rows are considered equal if all their corresponding members are non-null and equal; the rows are unequal if any corresponding members are non-null and unequal; otherwise the result of that row comparison is unknown (null). If all the per-row results are either unequal or null, with at least one null, then the result of IN
is null.
9.23.3. NOT IN
expression
NOT IN (subquery
)
The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result. The result of NOT IN
is “true” if only unequal subquery rows are found (including the case where the subquery returns no rows). The result is “false” if any equal row is found.
Note that if the left-hand expression yields null, or if there are no equal right-hand values and at least one right-hand row yields null, the result of the NOT IN
construct will be null, not true. This is in accordance with SQL's normal rules for Boolean combinations of null values.
As with EXISTS
, it's unwise to assume that the subquery will be evaluated completely.
row_constructor
NOT IN (subquery
)
The left-hand side of this form of NOT IN
is a row constructor, as described in Section 4.2.13. The right-hand side is a parenthesized subquery, which must return exactly as many columns as there are expressions in the left-hand row. The left-hand expressions are evaluated and compared row-wise to each row of the subquery result. The result of NOT IN
is “true” if only unequal subquery rows are found (including the case where the subquery returns no rows). The result is “false” if any equal row is found.
As usual, null values in the rows are combined per the normal rules of SQL Boolean expressions. Two rows are considered equal if all their corresponding members are non-null and equal; the rows are unequal if any corresponding members are non-null and unequal; otherwise the result of that row comparison is unknown (null). If all the per-row results are either unequal or null, with at least one null, then the result of NOT IN
is null.
9.23.4. ANY
/SOME
expression
operator
ANY (subquery
)expression
operator
SOME (subquery
)
The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result using the given operator
, which must yield a Boolean result. The result of ANY
is “true” if any true result is obtained. The result is “false” if no true result is found (including the case where the subquery returns no rows).
SOME
is a synonym for ANY
. IN
is equivalent to = ANY
.
Note that if there are no successes and at least one right-hand row yields null for the operator's result, the result of the ANY
construct will be null, not false. This is in accordance with SQL's normal rules for Boolean combinations of null values.
As with EXISTS
, it's unwise to assume that the subquery will be evaluated completely.
row_constructor
operator
ANY (subquery
)row_constructor
operator
SOME (subquery
)
The left-hand side of this form of ANY
is a row constructor, as described in Section 4.2.13. The right-hand side is a parenthesized subquery, which must return exactly as many columns as there are expressions in the left-hand row. The left-hand expressions are evaluated and compared row-wise to each row of the subquery result, using the given operator
. The result of ANY
is “true” if the comparison returns true for any subquery row. The result is “false” if the comparison returns false for every subquery row (including the case where the subquery returns no rows). The result is NULL if no comparison with a subquery row returns true, and at least one comparison returns NULL.
See Section 9.24.5 for details about the meaning of a row constructor comparison.
9.23.5. ALL
expression
operator
ALL (subquery
)
The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result using the given operator
, which must yield a Boolean result. The result of ALL
is “true” if all rows yield true (including the case where the subquery returns no rows). The result is “false” if any false result is found. The result is NULL if no comparison with a subquery row returns false, and at least one comparison returns NULL.
NOT IN
is equivalent to <> ALL
.
As with EXISTS
, it's unwise to assume that the subquery will be evaluated completely.
row_constructor
operator
ALL (subquery
)
The left-hand side of this form of ALL
is a row constructor, as described in Section 4.2.13. The right-hand side is a parenthesized subquery, which must return exactly as many columns as there are expressions in the left-hand row. The left-hand expressions are evaluated and compared row-wise to each row of the subquery result, using the given operator
. The result of ALL
is “true” if the comparison returns true for all subquery rows (including the case where the subquery returns no rows). The result is “false” if the comparison returns false for any subquery row. The result is NULL if no comparison with a subquery row returns false, and at least one comparison returns NULL.
See Section 9.24.5 for details about the meaning of a row constructor comparison.
9.23.6. Single-Row Comparison
row_constructor
operator
(subquery
)
The left-hand side is a row constructor, as described in Section 4.2.13. The right-hand side is a parenthesized subquery, which must return exactly as many columns as there are expressions in the left-hand row. Furthermore, the subquery cannot return more than one row. (If it returns zero rows, the result is taken to be null.) The left-hand side is evaluated and compared row-wise to the single subquery result row.
See Section 9.24.5 for details about the meaning of a row constructor comparison.