Выражения, возвращающие одиночное значение

4.2. Выражения, возвращающие одиночное значение

Выражения, возвращающие одиночное значение (value expression) используются в различных контекстах, таких как список выборки в команде SELECT, как значения новых колонок в команде INSERT или UPDATE, или для условий поиска в ряде других команд. Результат этого выражения (т.е. одиночное значение), иногда называют скаляром, чтобы отличать его от результата выражения, которое возвращает таблицу. Таким образом, выражения, возвращающие одиночное значение также называют скалярными выражениями (или даже просто выражениями). Синтаксис выражения позволяет вычислять значения из его частей, используя арифметические, логические, списочные и другие операции.

Выражение, возвращающее одиночное значение может быть одним из следующих видов:

  • Константа или литеральное значение

  • Сылка на колонку

  • Ссылка на позиционный параметр, в теле определения функции или подготовленного оператора

  • Индексное выражение

  • Выражение выбора поля

  • Вызов оператора

  • Вызов функции

  • Агрегатное выражение

  • Вызов оконной функции

  • Приведение типа

  • Выражение сопоставления

  • Скалярный подзапрос

  • Конструктор массива

  • Конструктор строки

  • Другое выражение в круглых скобках (используемое для группировки подвыражений и перекрывающее precedence)

В дополнение к этому списоку, существует несколько конструкций, которые могут классифицироваться как выражения, но которые не следуют никаким общим синтаксическим правилам. Они обычно имеют семантику функции или оператора и описываются в соответствущем разделе в Chapter 9. Примером, такого выражения является IS NULL.

Мы уже говорили о константах в Section 4.1.2. Следующие секции рассказывают об остальных видах скалярных выражений.

4.2.1. Ссылки на колонку

Ссылка на колонку может иметь вид:

отношение.имя_колонки

Отношение — это имя таблицы (возможно полное имя вместе с именем схемы) или псевдоним таблицы, определённый с помощью предложения FROM. Имя отношения и отдельная точка могут быть опущены, если имя колонки является уникальным для всех таблиц, которые используются в текущем запросе. (См. также Chapter 7.)

4.2.2. Позиционные параметры

Ссылка на позиционный параметр используется, чтобы показать значение, которым снаружи снабжается оператор SQL. Параметры используются в определениях функций SQL и в подготовленных запросах. Некоторые клиентские библиотеки также поддерживают задание значений данных отдельно от строк команды SQL, в этом случае параметры используются чтобы сослаться на значения данных, которые находятся снаружи. Вот форма ссылки на параметр:

$number

Например: рассмотрим опредение функции, dept, как:

CREATE FUNCTION dept(text) RETURNS dept
    AS $$ SELECT * FROM dept WHERE name = $1 $$
    LANGUAGE SQL;

Здесь $1 ссылается на значение первого аргумента функции в момент, когда вызывает функция.

4.2.3. Элементы массива

Если выражение даёт значение какого-либо типа массива, то конкретный элемент этого массива может быть извлечён из него с помощью:

выражениe[индекс]

а несколько соседних элементов ("часть массива") могут быть извлечены с помощью:

выражение[начальный_индекс:конечный_индекс]

(Здесь, скобки [ ] должны пониматься буквально.) Каждый индекс сам является выражением, которое должно быть целым числом.

Обычно, массив выражение должен быть в круглых скобках, но круглые скобки могут быть опущены: когда у выражение является ссылкой на колонку или позиционным параметром. Несколько индексов могут быть соединены, если оригинальный массив является многомерным. Например:

mytable.arraycolumn[4]
mytable.two_d_column[17][34]
$1[10:42]
(arrayfunction(a,b))[42]

Круглые скобки в последнем примере необходимы. См. подробности о массивах в Section 8.14.

4.2.4. Выбор поля

Если выражение даёт значение какого-либо составного типа (тип записи), то конкретное поле из этой записи может быть извлечено с помощью:

выражение.имя_поля

Обычно, выражение должно быть в круглых скобках: но круглые скобки могут быть опущены, когда выражение является ссылкой на таблицу или позиционным параметром. Например:

mytable.mycolumn
$1.somecolumn
(rowfunction(a,b)).col3

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

(compositecol).somefield
(mytable.compositecol).somefield

Чтобы показать, что compositecol является именем колонки, а не именем таблицы или что mytable является именем таблицы, а не именем схемы (во втором случае), здесь требуются скобки.

В списке выбора (см. Section 7.3), вы можете запросить все поля составного значения с помощью .*:

(compositecol).*

4.2.5. Вызовы операторов

Существуют три возможных синтаксиса для вызова операторов:

выражение оператор выражение (бинарный инфиксный оператор)
оператор выражение (унарный префиксный оператор)
выражение оператор (унарный постфиксный оператор)

где токен оператор следует синтаксическим правилам в Section 4.1.3, или является одним из ключевых слов AND, OR и NOT, или полным именем оператора в форме:

OPERATOR(схема.имя_оператора)

Существование отдельных операторов и являются ли они унарными или бинарными, зависит от того, какие операторы были заданы системой или пользователем. Chapter 9 описывает встроенные операторы.

4.2.6. Вызовы функций

Ситаксис вызова функции является именем этой функции (возможно с указанием имени схемы), за которым следуют список аргументов функции, заключённый в круглые скобки:

имя_функция ([выражение [, выражение ... ]] )

Например: следюущая функция подсчитывает квадратный корень из 2:

sqrt(2)

Список встроенный функций дан в Chapter 9. Другие функции могут быть добавлены пользователем.

Аргументы опционально могут иметь прикреплённые имена. Подробности см. в Section 4.3.

Note: Функция, которая получает один один аргумент составного типа, может в необязательном порядке быть вызвана с помощью синтаксиса выбора поля и наоборот. Так, нотации col(table) и table.col являются взаимозаменяемыми. Такое поведение не соответствует стандарту SQL, но предоставляется в PostgreSQL, потому что оно поволяет использовать функции для эмуляции "вычисляемых полей". Подробности см. в Section 35.4.2.

4.2.7. Агрегатные выражения

Агрегатное выражение представляет какую-либо агрегатную функцию, вызываемую для строк, выбранных в запросе. Агрегатная функция получает несколько значений, а выдаёт только одно значение, такое как сумма или среднее значение. Синтаксис агрегатного выражения может быть одним из следующих:

имя_агрегата (выражение [ , ... ] [ предложение_order_by ] )
имя_агрегата (ALL выражение [ , ... ] [ предложение_order_by ] )
имя_агрегата (DISTINCT выражение [ , ... ] [ предложение_order_by ] )
имя_агрегата ( * )

где имя_агрегата является заданным ранее агрегатом (возможно с указанием имени схемы), выражение является любым значимым выражением, которое само не содержит какое-либо агрегатное выражение или именем оконной функции и предложение_order_by является необязательным предложением ORDER BY как описывается ниже.

Первая форма агрегатного выражения вызывает агрегат для каждой строки на входе. Вторая форма синтаксиса является такой же как и первая, за исключением того: что ALL является значением по умолчанию. Третья форма вызывает агрегат один раз для каждого уникального значения выражения (или уникального списка значений, для нескольких выражений) найденного во входных строках. Последняя форма синтаксиса вызывает агрегат один раз для каждой строки на входе; так как никаких особых входных значений не задаётся и обычно эта форма полезна только для агрегатной функции count(*).

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

Например, count(*) выдаёт полное количество строк на входе; count(f1) выдаёт количество строк на входе: в которых f1 является не-NULL, поскольку count игнорирует значения NULL; и count(distinct f1) выдаёт количество уникальных не-NULL значений f1.

Обычно, входные строки скармливаются агрегатной функции в произвольном порядке. Во многих случаях, это не имеет значения; например min produces выдаёт один и тот же результат, вне зависимости от того в какой последовательности эта функция получает строки на входе. Однако, некоторые агрегатные функции (такие как array_agg и string_agg), выдаёт результаты, которые зависят от порядка в котором следуют входные строки. Когда используется такой агрегат, для упорядочивания входных значений, может быть указано необязательное предложение_order_by. Предложение_order_by имеет тот же синтаксис, что и для предложение ORDER BY на уровне запроса, как описано в Section 7.5, за исключением того, что используемые выражения всегда являются только выражениями и не могут быть именами или номерами колонок на выходе. Например:

SELECT array_agg(a ORDER BY b DESC) FROM table;

Когда происходит работа с агрегатными функциями, имеющими несколько аргументов, обратите внимание, что предложение ORDER BY следует после всех аргументов агрегата. Например, пишите так:

SELECT string_agg(a, ',' ORDER BY a) FROM table;

а не так:

SELECT string_agg(a ORDER BY a, ',') FROM table;  -- неправильно

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

Если в дополнение к предложению_order_by, указывается DISTINCT, то все выражения ORDER BY, должны соответствовать аргументам данного агрегата; т.е. вы не можете выполнять сортировку выражения, которое не включено в список DISTINCT.

Note: Возможность указывать и DISTINCT и ORDER BY в агрегатных функциях, является дополнительной возможностью PostgreSQL.

Предопределённые агрегатные функции описываются в Section 9.18. Пользователем могут быть добавлены другие агрегатные функции.

Агрегатное выражение может применяться только в списке результата или с ключевым словом HAVING в команде SELECT. Использование с другими ключевыми словами, такими как WHERE, запрещено, потому что эти ключевые слова логически выполняются перед тем как формируются результаты агрегатов.

Когда агрегатное выражение появляется в позапросе (см. Section 4.2.11 и Section 9.20), агрегат обычно выполняется для строк подзапроса. Но есть исключение, если аргументы агрегата содержат только переменные внешнего уровня: агрегат тогда выходит на ближайший такой внешний уровень и выполняется для строк запроса на этом уровне. Тогда агрегатное выражение целиком является внешней ссыкой для позапроса, в котором оно используется и работает как константа для любых действий этого подзапроса. Ограничение касательно применения только в списке результата или с ключевым словом HAVING, работает в соответствии с уровнем запроса, в который выходит агрегат.

4.2.8. Вызовы оконных функций

Вызов оконной функции предполагает использование какой-либо агрегатной функции для некоторой порции строк, которая выбрана запросом. В отличие от вызова обычной агрегатной функции, группировка выбранных строк в одну на выходе запроса не производится — каждая строка остаётся на выходе запроса отдельной строкой. Однако, оконная функция позволяет сканировать все строки, которые могут быть частью группы, в которую входит текущая строка, в соответствии со спецификацией группировки (списка PARTITION BY) вызова оконной функции. Синтаксис вызова оконной функции может быть одним из следующих:

имя_функции ([выражение [, выражение ... ]]) OVER ( определение_окна )
имя_функции ([выражение [, выражение ... ]]) OVER имя_окна
имя_функции ( * ) OVER ( определение_окна )
имя_функции ( * ) OVER имя_окна

где определение_окна имеет синтаксис:

[ существующее_имя_окна ]
[ PARTITION BY выражение [, ...] ]
[ ORDER BY выражение [ ASC | DESC | USING опертор ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ предложение_фрейма ]

и необязательное предложение_фрейма может принимать одно из значений:

[ RANGE | ROWS ] начало_фрейма
[ RANGE | ROWS ] BETWEEN начало_фрейма AND конец_фрейма

где начало_фрейма и конец_фрейма могут принимать одно из значений

UNBOUNDED PRECEDING
значение PRECEDING
CURRENT ROW
значение FOLLOWING
UNBOUNDED FOLLOWING

Здесь, выражение представляет любое значение выражения, которое само не содержит вызовы оконных функций. Списки PARTITION BY и ORDER BY имеют такой же синтаксис и смысл как и предложения GROUP BY и ORDER BY в обычном запросе, за исключением того, что их выражения всегда являются только выражениями и не могут быть именами или номерами выводимых колонок. Имя_окна является ссылкой на именованую спецификацию окна, определяемую в запросе в предложении WINDOW. Именованые спецификации окна обычно используются в виде OVER имя_окна, но также возможно написать имя окна внутри круглых скобок и затем в необязательном порядке добавить предложения сортировки и/или предложения фрейма (имя окна должно быть в этих предложениях, если они есть). Последняя форма синтаксиса следует тем же правилам, что и изменение существующего имени окна внутри предложения WINDOW; подробности смотрите на странице руководства SELECT.

Предложение_фрейма задаёт список строк, составляющих фрейм окна, для тех оконных функций, которые работают с фреймом, вместо всего разбиения. Если конец_фрейма опускается, то по умолчанию оно устанавливается CURRENT ROW (текущая строка). При этом существуют ограничения, такие что начало_фрейма не может быть UNBOUNDED FOLLOWING, а конец_фрейма не может быть UNBOUNDED PRECEDING, и выбор конец_фрейма, в данном выше списке, не может осуществляться раньше, чем выбор начало_фрейма — например RANGE BETWEEN CURRENT ROW AND значение PRECEDING не разрешается. Опции фрейма, установленные по умолчанию это: RANGE UNBOUNDED PRECEDING, что тоже самое, что и RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW; они устанавливают фрейм для всех строк от начала разбиения до последнего появления текущей строки в сортировке ORDER BY (что означает все строки если ORDER BY нет). Обычно, UNBOUNDED PRECEDING означает, что фрейм начинается с первой строки разбиения и соответственно UNBOUNDED FOLLOWING означает, что фрейм заканчивается последней строкой разбиения (за исключением режима RANGE или ROWS). В режиме ROWS, CURRENT ROW означает, что фрейм начинается или заканчивается с текущей строки; но в режиме RANGE, это означает, что фрейм начинается или заканчивается с первого или последнего появления текущей строки в ORDER BY. Значение PRECEDING и значение FOLLOWING в настоящий момент разрешены только в режиме ROWS. Они показывают, что фрейм начинается или заканчивается строкой, которая находится за множество строк перед или после текущей строки. Значение должно быть целым выражением и не содержать каких-либо переменных, агрегатных функций или оконных функций. Также оно не должно быть NULL или отрицательным; но может быть нулём, который выбирает саму текущую строку.

Встроенные оконные функции описываются в Table 9-44. Пользователь может добавить и другие оконные функции. Также, как оконные функции могут быть использованы любые встроенные или определённые пользователем агрегатные функции.

Синаксисы, использующие *, используются для агрегатных функций без параметров как оконных функций, например count(*) OVER (PARTITION BY x ORDER BY y). * специально не используется для неагрегатных оконных функций. Агрегатные оконные функции, в отличие от обычных агрегатных функций не позволяют использовать DISTINCT или ORDER BY внутри агументов функции.

Вызовы оконных функций разрешаются в запросе только в списке SELECT и предложени ORDER BY.

Подробности об оконных функциях можно найти в Section 3.5, Section 9.19, Section 7.2.4.

4.2.9. Приведения типов

Приведение типов определяет механизм преобразования из одного типа данных в другой. Для приведений типов PostgreSQL позволяет использовать два эквивалентных варианта синтаксиса:

CAST ( выражение AS тип )
выражение::тип

Ситаксис CAST совместим со стандартом SQL; ситаксис с :: является историческим для PostgreSQL.

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

Явное приведение типа обычно можно опустить, если нет двусмысленности в том, какое значение выражения должно получиться для типа (например, когда это значение заносится в колонку таблицы); в этом случае, система автоматически выполнит приведение типа. Однако, автоматическое приведение выполнятся только для случаев, которые отмечаются как "OK to apply implicitly" (подходит для неявного применения) в системных каталогах. Другие приведения типов должны быть вызваны с использованием синтаксиса явного приведения типа. Данное ограничение задумано для предотвращения неожиданных преобразований при приведении типов, без каких-либо предупреждающих сообщений.

Также возможно задать преобразование типа, используя ситаксис похожий на вызов функции:

имя_типа ( выражение )

Однако, такое работает только для типов, чьи имена также являются допустимыми в качестве имён функций. Например, double precision в данном случае использовать нельзя, но можно использовать эквивалент данного типа float8. Также имена interval, time и timestamp можно использовать только, если они заключены в двойные кавычки, потому что иначе будут синтаксические конфликты. Таким образом, использование синтаксиса приведения типа в виде функций приводит к противоречиям и предположительно должно избегаться.

Note: Синтаксис в форме вызова функции фактически и является вызовом функции. Когда для приведения типов, во время выполнения, используются один из двух вышеописанных стандартов синтаксиса, это приводит к внутреннему вызову зарегистрированной функции для выполнения преобразования типа. По соглашению, эти функции преобразования имеют то же имя, что и тип данных на их выходе и таким образом, "синтаксис в форме функции" является ничем иным как прямым вызовом требуемой функции преобразования. Очевидно, что это не то, на что должны полагаться переносимые приложения. Подробности смотрите в CREATE CAST.

4.2.10. Выражения сопоставления

Предложение COLLATE перекрывает настройки сопоставления данного выражения. Оно добавляется к выражению следующим образом:

expr COLLATE сопоставление

где сопоставление является возможным полным идентификатором схемы. Предложение COLLATE делает более жёскую привязку, чем операторы; когда необходимо, можно использовать круглые скобки.

Если сопоставление явно не указывается, то СУБД или получает значение сопоставления из колонок, задействованных в выражении, или, если никакие колонки в выражении не задействованы, оно берётся из значения по умолчанию для базы данных.

Два наиболее частных случая использования предложения COLLATE - это перекрытие порядка сортировки в предложении ORDER BY, например:

SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";

и перекрытие сопоставления при вызове функции или оператора, результаты работы которых зависят от локали, например:

SELECT * FROM tbl WHERE a > 'foo' COLLATE "C";

Обратите внимание, что в последнем случае, предложение COLLATE прикрепляется к входному аргументу оператора, если мы хотим получить от него эффект. Это не означает, что при вызове оператора или функции аргумент тоже прикрепляется к предложению COLLATE, потому что сопоставление, которое применяется оператором или функцией, получается путём рассмотрения всех аргументов, а явно указанное предложение COLLATE, будет перекрывать сопоставления для всех других аргументов. (Однако, прикрепление не совпадающих предложений COLLATE к более, чем одному аргументу - это ошибка. Подробности см. в Section 22.2.) Таким образом, данный пример приводит к тому же результату, что и предыдущий:

SELECT * FROM tbl WHERE a COLLATE "C" > 'foo';

Но вот это будет ошибкой:

SELECT * FROM tbl WHERE (a > 'foo') COLLATE "C";

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

4.2.11. Скалярные подзапросы

Скалярный поздапрос — это обычный запрос SELECT в круглых скобках, который возвращает ровно одну строку с одной колонкой. (См. информацию о написании запросов в Chapter 7.) После выполнения запроса SELECT, возвращаемое значение используется во внешнем выражении. Использование запроса, который возвращает более чем одну строку или более: чем одну колонку в качестве скалярного позапроса является ошибкой. (Но если во время выполнения, подзапрос не вернул ни одной строки — это не является ошибкой; такой результат считается значением NULL). Подзапрос может ссылаться на переменные из внешнего запроса, которые во время выполнения подзапроса будут использоваться как константы. Информацию о других выражениях, используемых в подзапросах см. также в Section 9.20.

Следующий пример ищет наибольшее количество людей, проживающих в городах для каждого штата:

SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name)
    FROM states;

4.2.12. Конструкторы массивов

Конструктор массивов — это выражение, которое осуществляет построение массива, используя значения его элементов. Простой конструктор массива содержит одно ключевое слово ARRAY, левую квадратную скобку [, список выражений (разделённых запятыми) для элементов массива и, наконец, правую квадратную скобку ]. Например:

SELECT ARRAY[1,2,3+4];
  array
---------
 {1,2,7}
(1 row)

По умолчанию, тип элемента массива является общим типом выражений для членов массива и определяется по тем же правилам, что и в UNION или CASE (см. Section 10.5). Вы можете перекрыть данный тип, явно указав приведение конструктора массива в нужному типу, например:

SELECT ARRAY[1,2,22.7]::integer[];
  array
----------
 {1,2,23}
(1 row)

Такое приведение типа даёт тот же эффект, что и приведение типа отдельно к каждому выражению элемента массива индивидуально. Больше о приведении типов, можно узнать в Section 4.2.9.

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

SELECT ARRAY[ARRAY[1,2], ARRAY[3,4]];
     array
---------------
 {{1,2},{3,4}}
(1 row)

SELECT ARRAY[[1,2],[3,4]];
     array
---------------
 {{1,2},{3,4}}
(1 row)

Поскольку многомерные массивы должны быть прямоугольными, внутрениие конструкторы на том же уровне должны выдавать подмассивы одинаковой размерности. Любые приведения типов, применяемые к внешнему конструктору ARRAY автоматически применяются ко всем внутренним конструкторам.

Элементы многомерного конструктора массива могут быть любыми массивами правильного вида, а не только конструкторами под-ARRAY. Например:

CREATE TABLE arr(f1 int[], f2 int[]);

INSERT INTO arr VALUES (ARRAY[[1,2],[3,4]], ARRAY[[5,6],[7,8]]);

SELECT ARRAY[f1, f2, '{{9,10},{11,12}}'::int[]] FROM arr;
                     array
------------------------------------------------
 {{{1,2},{3,4}},{{5,6},{7,8}},{{9,10},{11,12}}}
(1 row)

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

SELECT ARRAY[]::integer[];
 array
-------
 {}
(1 row)

Также возможно сконструтировать массив из результатов подзапроса. В этой форме, конструктор массива записывается с ключевым словом ARRAY за которым следуют круглые скобки (не квадратные) с подзапросом. Например:

SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
                          ?column?
-------------------------------------------------------------
 {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
(1 row)

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

Индексирование элементов массива, конструируемого с ARRAY всегда начинается с единицы. Больше информации о массивах, можно найти в Section 8.14.

4.2.13. Конструкторы строк

Note: (Обратите внимание, что в этом параграфе под строками понимаются строки таблиц, записи, которые возвращаются запросами, а не строки символов. Использование в данном случае слова "строка" вызвано тем, что оба английских слова: row и string переводятся на русский язык одинаково — прим. переводчика.)

Конструктор строк — это выражение, которое осуществляет построение значения строки (также называемое составным значением), используя значения полей этой строки. Конструктор строки состоит из ключевого слова ROW, за которым следует левая круглая скобка, ноль или более выражений (разделённых запятыми) значений полей данной строки и, в конце правая круглая скобка. Например:

SELECT ROW(1,2.5,'this is a test');

Ключевое слово ROW является необязательным, если в списке выражений, их больше, чем одно.

Конструктор строки может включать синтаксис значение_строки.*, который будет расширен в список элементов значения строки, только если синтаксис .* используется в списке оператора SELECT верхнего уровня. Например, если таблица t имеет колонки f1 и f2, то два следующих оператора являются одинаковыми:

SELECT ROW(t.*, 42) FROM t;
SELECT ROW(t.f1, t.f2, 42) FROM t;

Note: В PostgreSQL до версии 8.2, синтаксис .* не расширялся, и написание ROW(t.*, 42) создавало строку из двух полей, где первое поле было другим значением строки. Новое поведение обычно более полезно. Если же вам необходимо старое поведение встроенных значений строк, записывайте внутреннее значение строки без .*, например ROW(t, 42).

По умолчанию, значение созданное с помощью выражения ROW имеет неизвестный тип записи. Если необходимо, он может быть приведён к именованому составному типу — или к типу строки таблицы или к составному типу, созданному с помощью CREATE TYPE AS. Чтобы избежать двусмысленности, может быть необходимо явное приведение типа. Например:

CREATE TABLE mytable(f1 int, f2 float, f3 text);

CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;

-- Приведение типа не нужно, потому что только одна getf1() существует
SELECT getf1(ROW(1,2.5,'this is a test'));
 getf1
-------
     1
(1 row)

CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric);

CREATE FUNCTION getf1(myrowtype) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;

-- Теперь приведение типа необходимо, чтобы показать какая функция вызывается:
SELECT getf1(ROW(1,2.5,'this is a test'));
ERROR:  function getf1(record) is not unique

SELECT getf1(ROW(1,2.5,'this is a test')::mytable);
 getf1
-------
     1
(1 row)

SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS myrowtype));
 getf1
-------
    11
(1 row)

Конструкторы строк могут быть использованы для построение составных значений, для их сохранения в колонках таблицы с составным типом данных или для передачи этих значений в функцию, которая работает с составным параметром. Также является возможным сравнить два строковых значения или протестировать строку на IS NULL или IS NOT NULL, например:

SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');

SELECT ROW(table.*) IS NULL FROM table;  -- определить все null-строки

Больше подробностей можно узнать в Section 9.21. Конструкторы строк могут также быть использованы в подзапросах, как рассказывается в Section 9.20.

4.2.14. Правила вычисления выражений

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

Тем не менее, если результат выражения может быть определён вычислениям только некоторых его частей, то все другие подвыражения могут и не вычисляться. Например, если пишется:

SELECT true OR somefunc();

то somefunc() (предположительнл) не должна бы вызываться. Тоже самое должно быть в случае:

SELECT somefunc() OR true;

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

Как следствие, неблагоразумно использовать функции с посторонними эффектами как часть сложных выражений. Особенно опасно полагаться на посторонние эффекты или порядок вычисления в предложениях WHERE и HAVING, так как эти выражения сильно перерабатываются как часть разработки плана выполнения запроса. Логические выражения (AND/OR/NOT combinations) в таких предложениях могут быть переорганизованы в любую форму, которую позволяют правила логической арифметики.

Когда крайне необходимо соблюсти порядок вычисления, можно использовать конструкт CASE (см. Section 9.16). Например, вот ненадёжный способ пытаться избежать деления на ноль в выражении WHERE:

SELECT ... WHERE x > 0 AND y/x > 1.5;

А вот это безопасный способ:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;

Конструкт CASE использующийся в данном случае будет разрушать попытки оптимизации, так что его нужно использовать только, когда он необходим. (В данном конкретном примере, будет лучше решить эту проблему, использовав вместо написанного y > 1.5*x.)

Back to top

(С) Виктор Вислобоков, 2008-2023