SELECT#
SELECT
SELECT, TABLE, WITH — извлекает строки из таблицы или представления
Синтаксис
[ WITH [ RECURSIVE ]with_query
[, ...] ] SELECT [ ALL | DISTINCT [ ON (expression
[, ...] ) ] ] [ { * |expression
[ [ AS ]output_name
] } [, ...] ] [ FROMfrom_item
[, ...] ] [ WHEREcondition
] [ GROUP BY [ ALL | DISTINCT ]grouping_element
[, ...] ] [ HAVINGcondition
] [ WINDOWwindow_name
AS (window_definition
) [, ...] ] [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ]select
] [ ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...] ] [ LIMIT {count
| ALL } ] [ OFFSETstart
[ ROW | ROWS ] ] [ FETCH { FIRST | NEXT } [count
] { ROW | ROWS } { ONLY | WITH TIES } ] [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OFtable_name
[, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ] wherefrom_item
can be one of: [ ONLY ]table_name
[ * ] [ [ AS ]alias
[ (column_alias
[, ...] ) ] ] [ TABLESAMPLEsampling_method
(argument
[, ...] ) [ REPEATABLE (seed
) ] ] [ LATERAL ] (select
) [ [ AS ]alias
[ (column_alias
[, ...] ) ] ]with_query_name
[ [ AS ]alias
[ (column_alias
[, ...] ) ] ] [ LATERAL ]function_name
( [argument
[, ...] ] ) [ WITH ORDINALITY ] [ [ AS ]alias
[ (column_alias
[, ...] ) ] ] [ LATERAL ]function_name
( [argument
[, ...] ] ) [ AS ]alias
(column_definition
[, ...] ) [ LATERAL ]function_name
( [argument
[, ...] ] ) AS (column_definition
[, ...] ) [ LATERAL ] ROWS FROM(function_name
( [argument
[, ...] ] ) [ AS (column_definition
[, ...] ) ] [, ...] ) [ WITH ORDINALITY ] [ [ AS ]alias
[ (column_alias
[, ...] ) ] ]from_item
join_type
from_item
{ ONjoin_condition
| USING (join_column
[, ...] ) [ ASjoin_using_alias
] }from_item
NATURALjoin_type
from_item
from_item
CROSS JOINfrom_item
andgrouping_element
can be one of: ( )expression
(expression
[, ...] ) ROLLUP ( {expression
| (expression
[, ...] ) } [, ...] ) CUBE ( {expression
| (expression
[, ...] ) } [, ...] ) GROUPING SETS (grouping_element
[, ...] ) andwith_query
is:with_query_name
[ (column_name
[, ...] ) ] AS [ [ NOT ] MATERIALIZED ] (select
|values
|insert
|update
|delete
) [ SEARCH { BREADTH | DEPTH } FIRST BYcolumn_name
[, ...] SETsearch_seq_col_name
] [ CYCLEcolumn_name
[, ...] SETcycle_mark_col_name
[ TOcycle_mark_value
DEFAULTcycle_mark_default
] USINGcycle_path_col_name
] TABLE [ ONLY ]table_name
[ * ]
Описание
SELECT
извлекает строки из нуля или более таблиц.
Общая обработка SELECT
происходит следующим образом:
Все запросы в списке
WITH
вычисляются. Они фактически служат временными таблицами, на которые можно ссылаться в спискеFROM
. ЗапросWITH
, который ссылается более одного раза вFROM
, вычисляется только один раз, если не указано иное с помощьюNOT MATERIALIZED
. (См. WITH Clause ниже).Все элементы в списке
FROM
вычисляются. (Каждый элемент в спискеFROM
является реальной или виртуальной таблицей). Если в спискеFROM
указано более одного элемента, они перекрещиваются. (См. FROM Clause ниже).Если указано условие
WHERE
, то из вывода удаляются все строки, которые не удовлетворяют этому условию. (См. WHERE Clause ниже).Если указано
GROUP BY
, или если есть вызовы агрегатных функций, вывод объединяется в группы строк, которые совпадают по одному или нескольким значениям, и вычисляются результаты агрегатных функций. Если присутствуетHAVING
, он исключает группы, которые не удовлетворяют заданному условию. (См. GROUP BY Clause и HAVING Clause ниже.) Хотя столбцы вывода запроса номинально вычисляются на следующем шаге, они также могут быть указаны (по имени или порядковому номеру) в предложенииGROUP BY
.Фактические выходные строки вычисляются с использованием выражений вывода
SELECT
для каждой выбранной строки или группы строк. (См. SELECT List ниже).SELECT DISTINCT
удаляет дублирующиеся строки из результата.SELECT DISTINCT ON
удаляет строки, которые совпадают по всем указанным выражениям.SELECT ALL
(по умолчанию) вернет все кандидатские строки, включая дубликаты. (См. DISTINCT Clause ниже).Используя операторы
UNION
,INTERSECT
иEXCEPT
, результаты более чем одного оператораSELECT
могут быть объединены в один набор результатов. ОператорUNION
возвращает все строки, которые находятся в одном или обоих наборах результатов. ОператорINTERSECT
возвращает все строки, которые строго находятся в обоих наборах результатов. ОператорEXCEPT
возвращает строки, которые находятся в первом наборе результатов, но не во втором. Во всех трех случаях дублирующиеся строки удаляются, если не указаноALL
. Шумовое словоDISTINCT
может быть добавлено для явного указания удаления дублирующихся строк. Обратите внимание, что здесь поведение по умолчанию дляDISTINCT
, даже если по умолчанию для самого оператораSELECT
являетсяALL
. (См. UNION Clause, INTERSECT Clause и EXCEPT Clause ниже).Если указано предложение
ORDER BY
, возвращаемые строки сортируются в указанном порядке. ЕслиORDER BY
не указано, строки возвращаются в том порядке, который система считает наиболее быстрым для получения результата. (См. раздел ORDER BY Clause ниже).Если указано условие
LIMIT
(илиFETCH FIRST
) илиOFFSET
, операторSELECT
возвращает только подмножество строк результата. (См. LIMIT Clause ниже).Если указаны
FOR UPDATE
,FOR NO KEY UPDATE
,FOR SHARE
илиFOR KEY SHARE
, операторSELECT
блокирует выбранные строки от одновременных обновлений. (См. раздел The Locking Clause ниже).
Необходимо иметь привилегию SELECT
для каждого столбца, используемого в команде SELECT
. Использование FOR NO KEY UPDATE
, FOR UPDATE
, FOR SHARE
или FOR KEY SHARE
также требует наличия привилегии UPDATE
(для по крайней мере одного столбца каждой выбранной таблицы).
Параметры
WITH
Clause
Предложение WITH
позволяет указать одно или несколько подзапросов, на которые можно ссылаться по имени в основном запросе. Подзапросы фактически действуют как временные таблицы или представления на протяжении основного запроса. Каждый подзапрос может быть оператором SELECT
, TABLE
, VALUES
, INSERT
, UPDATE
или DELETE
. При написании модифицирующего данные оператора (INSERT
, UPDATE
или DELETE
) в WITH
обычно включается предложение RETURNING
. Выходные данные RETURNING
, а не базовая таблица, которую изменяет оператор, формируют временную таблицу, которую читает основной запрос. Если RETURNING
не указано, оператор все равно выполняется, но не производит вывода, поэтому его нельзя использовать как таблицу в основном запросе.
Для каждого запроса WITH
необходимо указать имя (без указания схемы). При желании можно указать список имен столбцов; если это не указано, имена столбцов выводятся из подзапроса.
Если указано RECURSIVE
, это позволяет подзапросу SELECT
ссылаться на себя по имени. Такой подзапрос должен иметь следующую форму.
non_recursive_term
UNION [ ALL | DISTINCT ]recursive_term
где рекурсивная само-ссылка должна появиться на правой стороне
UNION
. В одном запросе разрешена только одна рекурсивная само-ссылка.
Рекурсивные операторы модификации данных не поддерживаются, но вы можете использовать результаты рекурсивного
SELECT
запроса в
операторе модификации данных. См. Раздел 7.8 для
примера.
Другой эффект RECURSIVE
заключается в том, что
запросы WITH
не обязательно должны быть упорядочены: запрос
может ссылаться на другой запрос, который находится позже в списке. (Однако
циклические ссылки или взаимная рекурсия не реализованы).
Без RECURSIVE
запросы WITH
могут ссылаться только на соседние запросы WITH
,
которые находятся раньше в списке WITH
.
Когда в предложении WITH
присутствует несколько запросов, RECURSIVE
должно быть написано только один раз, сразу после WITH
. Это применяется ко всем запросам в предложении WITH
, хотя оно не влияет на запросы, которые не используют рекурсию или прямые ссылки.
Необязательная фраза SEARCH
вычисляет столбец последовательности поиска, который может использоваться для упорядочивания результатов рекурсивного запроса в порядке поиска в ширину или в глубину. Предоставленный список имен столбцов указывает ключ строки, который будет использоваться для отслеживания посещенных строк. В результате запроса WITH
будет добавлен столбец с именем search_seq_col_name
в список столбцов результата. Этот столбец может быть упорядочен во внешнем запросе для достижения соответствующего порядка. См. Раздел 7.8.2.1 для примеров.
Необязательное предложение CYCLE
используется для обнаружения циклов в рекурсивных запросах. Предоставленный список имен столбцов указывает ключ строки, который будет использоваться для отслеживания посещенных строк. В результате запроса WITH
будет добавлен столбец с именем cycle_mark_col_name
. Этот столбец будет установлен в значение cycle_mark_value
, когда будет обнаружен цикл, иначе - в значение cycle_mark_default
.
Кроме того, обработка рекурсивного объединения будет остановлена, когда будет обнаружен цикл. cycle_mark_value
и cycle_mark_default
должны быть константами, и они должны быть приводимыми к общему типу данных, а тип данных должен иметь оператор неравенства. (Стандарт SQL требует, чтобы они были логическими константами или строками символов, но PostgreSQL не требует этого). По умолчанию используются значения TRUE
и FALSE
(типа boolean
). Кроме того, в результате запроса WITH
будет добавлен столбец с именем cycle_path_col_name
. Этот столбец используется внутренне для отслеживания посещенных строк. См. примеры в разделе Раздел 7.8.2.2.
Предложения SEARCH
и CYCLE
допустимы только для рекурсивных запросов WITH
. with_query
должен быть UNION
(или UNION ALL
) из двух команд SELECT
(или эквивалентных) (без вложенных UNION
). Если используются обе Предложения, столбец, добавленный предложением SEARCH
, появляется перед столбцами, добавленными предложением CYCLE
.
Все основные запросы и запросы WITH
выполняются (по представлению) одновременно. Это означает, что эффекты модифицирующего данные оператора в WITH
не могут быть видны из других частей запроса, кроме как путем чтения его вывода RETURNING
. Если два таких модифицирующих данные оператора пытаются изменить одну и ту же строку, результаты неопределены.
Одним из ключевых свойств запросов WITH
является то, что они обычно вычисляются только один раз за выполнение основного запроса, даже если основной запрос ссылается на них несколько раз. В частности, гарантируется, что операторы модификации данных будут выполнены только один раз, независимо от того, читает ли основной запрос все или любую часть их вывода.
Однако запрос WITH
может быть помечен как NOT MATERIALIZED
, чтобы удалить эту гарантию. В этом случае запрос WITH
может быть объединен с основным запросом, как если бы он был простым подзапросом SELECT
в разделе FROM
основного запроса. Это приводит к дублированию вычислений, если основной запрос ссылается на этот запрос WITH
более одного раза; но если каждое такое использование требует только нескольких строк из общего вывода запроса WITH
, NOT MATERIALIZED
может обеспечить сокращение затрат, позволяя оптимизировать запросы совместно. NOT MATERIALIZED
игнорируется, если он применяется к рекурсивному запросу WITH
или к запросу, содержащему побочные эффекты (т.е. не являющемуся простым SELECT
, не содержащим волатильных функций).
По умолчанию, безопасный для побочных эффектов запрос WITH
сворачивается в основной запрос, если он используется ровно один раз в предложении FROM
основного запроса. Это позволяет совместную оптимизацию двух уровней запроса в ситуациях, когда это должно быть семантически невидимо. Однако такое сворачивание может быть предотвращено путем пометки запроса WITH
как MATERIALIZED
. Это может быть полезно, например, если запрос WITH
используется в качестве оптимизационного барьера для предотвращения выбора плохого плана планировщиком. В версиях PostgreSQL до v12 такое сворачивание никогда не выполнялось, поэтому запросы, написанные для более старых версий, могут полагаться на WITH
как на оптимизационный барьер.
См. Раздел 7.8 для получения дополнительной информации.
Условие FROM
Предложение FROM
указывает одну или несколько исходных таблиц для SELECT
. Если указано несколько источников, результатом будет декартово произведение (перекрестное соединение) всех источников. Но обычно добавляются условия квалификации (через WHERE
), чтобы ограничить возвращаемые строки до небольшого подмножества декартова произведения.
Предложение FROM
может содержать следующие элементы:
table_name
Имя (опционально с указанием схемы) существующей таблицы или представления. Если перед именем таблицы указано
ONLY
, сканируется только эта таблица. ЕслиONLY
не указано, сканируется таблица и все ее наследники (если они есть). Дополнительно, после имени таблицы можно указать*
, чтобы явно указать, что включены наследники таблицы.alias
Заменяющее имя для элемента
FROM
, содержащего псевдоним. Псевдоним используется для краткости или для устранения неоднозначности при самосоединении (когда одна и та же таблица сканируется несколько раз). Когда указывается псевдоним, он полностью скрывает фактическое имя таблицы или функции; например, при указанииFROM foo AS f
, остатокSELECT
должен ссылаться на этот элементFROM
какf
, а не какfoo
. Если указан псевдоним, также можно указать список псевдонимов столбцов для замены одного или нескольких столбцов таблицы.TABLESAMPLE
sampling_method
(argument
[, ...] ) [ REPEATABLE (seed
) ]Предложение
TABLESAMPLE
послеимя_таблицы
указывает на то, что что указанныйsampling_method
должен быть использован для получения подмножества строк этой таблицы. Эта выборка предшествует применению любых других фильтров, таких как например, предложенияWHERE
. Стандартный дистрибутив Tantor BE включает два метода выборки,BERNOULLI
иSYSTEM
, а другие методы выборки могут быть установлены в базу данных с помощью расширений.Методы выборки
BERNOULLI
иSYSTEM
принимают одинаргумент
, который представляет собой долю таблицы для выборки, выраженную в процентах от 0 до 100. Этот аргумент может быть любым выражением типаreal
. (Другие методы выборки могут принимать больше или другие аргументы). Оба этих метода возвращают случайно выбранную выборку таблицы, которая будет содержать примерно указанный процент строк таблицы. МетодBERNOULLI
сканирует всю таблицу и независимо выбирает или игнорирует отдельные строки с указанной вероятностью. МетодSYSTEM
выполняет выборку на уровне блоков, при этом каждый блок имеет указанную вероятность быть выбранным; возвращаются все строки в каждом выбранном блоке. МетодSYSTEM
значительно быстрее методаBERNOULLI
, когда указываются небольшие проценты выборки, но он может возвращать менее случайную выборку таблицы из-за эффектов кластеризации.Необязательное предложение
REPEATABLE
указывает номер или выражениеseed
, которое будет использоваться для генерации случайных чисел внутри метода выборки. Значение seed может быть любым не нулевым числом с плавающей точкой. Два запроса, которые указывают одинаковый seed иаргументы
, будут выбирать одну и ту же выборку из таблицы, если таблица не была изменена за это время. Но разные начальные значения обычно приводят к разным выборкам. ЕслиREPEATABLE
не указано, то для каждого запроса будет выбрана новая случайная выборка на основе системно-сгенерированного начального значения. Обратите внимание, что некоторые дополнительные методы выборки не принимаютREPEATABLE
и всегда будут создавать новые выборки при каждом использовании.select
Под-
SELECT
может появляться в предложенииFROM
. Это действует так, как будто его вывод был создан как временная таблица на время выполнения этой единственной командыSELECT
. Обратите внимание, что под-SELECT
должен быть заключен в скобки, и псевдоним может быть предоставлен так же, как и для таблицы. КомандаVALUES
также может быть использована здесь.with_query_name
Запрос
WITH
ссылается на него, указывая его имя, так же, как если бы имя запроса было именем таблицы. (Фактически, запросWITH
скрывает любую реальную таблицу с тем же именем для целей основного запроса. При необходимости вы можете ссылаться на реальную таблицу с тем же именем, указав схему имя таблицы). Псевдоним может быть указан так же, как и для таблицы.function_name
Функции могут появляться в разделе
FROM
запроса. (Это особенно полезно для функций, возвращающих наборы результатов, но может использоваться для любой функции). Это действует так, как если бы вывод функции был создан в виде временной таблицы на время выполнения этой отдельной командыSELECT
. Если тип результата функции является составным (включая случай функции с несколькими параметрамиOUT
), каждый атрибут становится отдельным столбцом в неявной таблице.Когда к функции добавляется необязательная фраза
WITH ORDINALITY
, к результату функции будет добавлен дополнительный столбец типаbigint
. Этот столбец нумерует строки результата набора функции, начиная с 1. По умолчанию этот столбец называетсяordinality
.Псевдоним может быть указан так же, как и для таблицы. Если указан псевдоним, можно также указать список псевдонимов столбцов, чтобы предоставить заменяющие имена для одного или нескольких атрибутов составного типа возвращаемого функцией, включая столбец порядкового номера, если он присутствует.
Все вызовы функций могут быть объединены в один элемент
FROM
-clause, окружив их с помощьюROWS FROM( ... )
. Результатом такого элемента является конкатенация первой строки из каждой функции, затем второй строки из каждой функции и так далее. Если некоторые функции возвращают меньше строк, чем другие, для отсутствующих данных подставляются значения null, чтобы общее количество возвращаемых строк всегда было таким же, как у функции, которая вернула наибольшее количество строк.Если функция была определена с возвращаемым типом данных
record
, то должен присутствовать псевдоним или ключевое словоAS
, за которым следует список определений столбцов в форме(
. Список определений столбцов должен соответствовать фактическому количеству и типам столбцов, возвращаемых функцией.column_name
data_type
[, ... ])При использовании синтаксиса
ROWS FROM( ... )
, если одна из функций требует список определений столбцов, предпочтительно поместить список определений столбцов после вызова функции внутриROWS FROM( ... )
. Список определений столбцов может быть размещен после конструкцииROWS FROM( ... )
только если есть только одна функция и нет предложенияWITH ORDINALITY
.Чтобы использовать
ORDINALITY
вместе с списком определений столбцов, вы должны использовать синтаксисROWS FROM( ... )
и поместить список определений столбцов внутрьROWS FROM( ... )
.join_type
Один из
[ INNER ] JOIN
LEFT [ OUTER ] JOIN
RIGHT [ OUTER ] JOIN
FULL [ OUTER ] JOIN
Для типов соединения
INNER
иOUTER
необходимо указать условие соединения, а именно одно из следующих:ON
,условие_соединения
USING (
илистолбец_соединения
[, ...])NATURAL
. См. ниже для получения более подробной информации.Предложение
JOIN
объединяет два элементаFROM
, которые для удобства мы будем называть “таблицами”, хотя на самом деле они могут быть любым типом элементаFROM
. Используйте скобки, если необходимо определить порядок вложенности. В отсутствие скобокJOIN
вкладывается слева направо. В любом случаеJOIN
связывает более плотно, чем запятые, разделяющие элементы спискаFROM
. Все вариантыJOIN
являются просто удобством в нотации, так как они не делают ничего, что нельзя сделать с помощью простогоFROM
иWHERE
.LEFT OUTER JOIN
возвращает все строки в квалифицированном декартовом произведении (т.е. все комбинированные строки, которые соответствуют его условию соединения), плюс одну копию каждой строки в левой таблице, для которой не было строки в правой таблице, которая прошла условие соединения. Эта строка из левой таблицы расширяется до полной ширины соединенной таблицы путем вставки значений null для столбцов правой таблицы. Обратите внимание, что только условиеJOIN
собственного предложения рассматривается при определении, какие строки имеют совпадения. Внешние условия применяются после этого.Обратно,
RIGHT OUTER JOIN
возвращает все соединенные строки, а также одну строку для каждой несоответствующей строке справа (расширенную с нулями слева). Это всего лишь удобство в обозначении, так как вы можете преобразовать его вLEFT OUTER JOIN
, поменяв местами левую и правую таблицы.FULL OUTER
возвращает все соединенные строки, а также одну строку для каждой неподходящей строке слева (расширенную с помощью null значений справа), а также одну строку для каждой неподходящей строке справа (расширенную с помощью null значений слева).ON
join_condition
join_condition
- это выражение, результатом которого является значение типаboolean
(аналогично предложениюWHERE
), которое указывает, какие строки в соединении считаются совпадающими.USING (
join_column
[, ...] ) [ ASjoin_using_alias
]Конструкция вида
USING ( a, b, ... )
является сокращением дляON left_table.a = right_table.a AND left_table.b = right_table.b ...
. Кроме того,USING
подразумевает, что в результате соединения будет включен только один из каждой пары эквивалентных столбцов, а не оба.Если указано имя
join_using_alias
, оно предоставляет псевдоним таблицы для столбцов соединения. Только столбцы соединения, перечисленные в предложенииUSING
, могут быть обращены по этому имени. В отличие от обычногоalias
, это не скрывает имена объединяемых таблиц от остального запроса. Также, в отличие от обычногоalias
, нельзя написать список псевдонимов столбцов - имена выходных столбцов соединения такие же, как они появляются в спискеUSING
.NATURAL
NATURAL
является сокращением для спискаUSING
, который упоминает все столбцы в двух таблицах с совпадающими именами. Если нет общих имен столбцов,NATURAL
эквивалентноON TRUE
.CROSS JOIN
CROSS JOIN
эквивалентноINNER JOIN ON (TRUE)
, то есть, строки не удаляются по условию. Они создают простое декартово произведение, то же самое, что вы получаете, перечисляя две таблицы на верхнем уровнеFROM
, но ограниченное условием соединения (если есть).LATERAL
Ключевое слово
LATERAL
может предшествовать подзапросуSELECT
FROM
. Это позволяет подзапросуSELECT
ссылаться на столбцыFROM
, которые появляются перед ним в спискеFROM
. (БезLATERAL
каждый подзапросSELECT
оценивается независимо и поэтому не может ссылаться на другие элементыFROM
).LATERAL
также может предшествовать вызову функцииFROM
элемент, но в этом случае это шумовое слово, потому что выражение функции может ссылаться на ранее указанные элементыFROM
в любом случае.Элемент
LATERAL
может появляться на верхнем уровне в спискеFROM
или внутри дереваJOIN
. В последнем случае он также может ссылаться на любые элементы, которые находятся слева отJOIN
, на котором он находится справа.Когда элемент
FROM
содержит перекрестные ссылкиLATERAL
, вычисление происходит следующим образом: для каждой строки элементаFROM
, предоставляющей перекрестно-ссылочные столбцы, или набора строк из нескольких элементовFROM
, предоставляющих столбцы, элементLATERAL
вычисляется с использованием значений столбцов этой строки или набора строк. Полученные строки объединяются, как обычно, с вычисленными из них строками. Это повторяется для каждой строки или набора строк из исходной таблицы столбцов.Все исходные таблицы столбца(-ов) должны быть объединены с использованием
INNER
илиLEFT
соединения с элементомLATERAL
, иначе не будет определенного набора строк, из которого можно вычислить каждый набор строк для элементаLATERAL
. Таким образом, хотя конструкция вроде
синтаксически допустима, на самом деле нельзя разрешить ссылкуX
RIGHT JOIN LATERALY
Y
наX
.
Предложение WHERE
Опциональная WHERE
фраза имеет общую форму
WHERE condition
где условие
- это любое выражение, которое вычисляется в результат типа boolean
. Любая строка, которая не удовлетворяет этому условию, будет исключена из вывода. Строка удовлетворяет условию, если она возвращает true, когда фактические значения строки подставляются вместо любых ссылок на переменные.
Предложение GROUP BY
Опциональная GROUP BY
фраза имеет общую форму
GROUP BY [ ALL | DISTINCT ] grouping_element
[, ...]
GROUP BY
сгруппирует в одну строку все выбранные строки, которые имеют одинаковые значения для группирующих выражений. Выражение
, используемое внутри элемента группировки
, может быть именем входного столбца, именем или порядковым номером выходного столбца (SELECT
элемент списка) или произвольным выражением, составленным из значений входных столбцов. В случае неоднозначности, имя GROUP BY
будет интерпретировано как имя входного столбца, а не имя выходного столбца.
Если в качестве элементов группировки присутствуют GROUPING SETS
, ROLLUP
или CUBE
, то весь GROUP BY
определяет некоторое количество независимых grouping sets
. В результате этого создается эквивалентное объединение UNION ALL
между подзапросами с отдельными наборами группировки в качестве соответствующих предложений GROUP BY
. Опциональное предложение DISTINCT
удаляет дублирующиеся наборы перед обработкой; оно не преобразует UNION ALL
в UNION DISTINCT
. Дополнительные сведения о работе с наборами группировки см. в разделе Раздел 7.2.4.
Все агрегатные функции, если они используются, вычисляются для всех строк, составляющих каждую группу, и для каждой группы вычисляется отдельное значение.
(Если есть агрегатные функции, но нет предложения GROUP BY
, запрос рассматривается как имеющий одну группу, включающую все выбранные строки).
Набор строк, передаваемых каждой агрегатной функции, может быть дополнительно отфильтрован с помощью предложения FILTER
, присоединенного к вызову агрегатной функции; см. Раздел 4.2.7 для получения дополнительной информации. Когда присутствует предложение FILTER
, входные данные для этой агрегатной функции включают только те строки, которые соответствуют этому выражению.
Когда присутствует GROUP BY
или любые агрегатные функции, недопустимо, чтобы выражения списка SELECT
ссылались на несгруппированные столбцы, за исключением случаев, когда несгруппированный столбец функционально зависит от группируемых столбцов, поскольку в противном случае может быть более одного возможного значения для несгруппированного столбца. Функциональная зависимость существует, если группируемые столбцы (или их подмножество) являются первичным ключом таблицы, содержащей несгруппированный столбец.
Учтите, что все агрегатные функции вычисляются перед вычислением любых скалярных выражений в
предложении HAVING
или списке SELECT
. Это означает,
что, например, выражение CASE
не может использоваться для пропуска
вычисления агрегатной функции; см. Раздел 4.2.14.
В настоящее время, FOR NO KEY UPDATE
, FOR UPDATE
,
FOR SHARE
и FOR KEY SHARE
не могут быть
указаны с GROUP BY
.
Предложение HAVING
Опциональная фраза HAVING
имеет общую форму
HAVING condition
где условие
такое же, как указано для предложения
WHERE
.
HAVING
удаляет групповые строки, которые не удовлетворяют условию. HAVING
отличается от WHERE
: WHERE
фильтрует отдельные строки перед применением GROUP BY
, в то время как HAVING
фильтрует групповые строки, созданные с помощью GROUP BY
. Каждый столбец, на который ссылается condition
, должен однозначно ссылаться на группирующий столбец, если ссылка появляется внутри агрегатной функции или негруппированный столбец функционально зависит от группирующих столбцов.
Присутствие HAVING
превращает запрос в групповой запрос, даже если отсутствует предложение GROUP BY
. Это происходит также, как и в случае, когда запрос содержит агрегатные функции, но не содержит предложения GROUP BY
. Все выбранные строки считаются одной группой, и список SELECT
и предложение HAVING
могут ссылаться только на столбцы таблицы из агрегатных функций. Такой запрос выдаст одну строку, если условие HAVING
истинно, и ноль строк, если оно ложно.
В настоящее время, FOR NO KEY UPDATE
, FOR UPDATE
,
FOR SHARE
и FOR KEY SHARE
не могут быть
указаны с HAVING
.
Предложение WINDOW
Опциональная фраза WINDOW
имеет общую форму
WINDOWwindow_name
AS (window_definition
) [, ...]
где window_name
- это имя, на которое можно ссылаться из предложений OVER
или последующих определений окон, а window_definition
- это
[existing_window_name
] [ PARTITION BYexpression
[, ...] ] [ ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...] ] [frame_clause
]
Если указано существующее имя окна existing_window_name
, оно должно ссылаться на ранее введенную запись в списке WINDOW
; новое окно копирует предложение секционирования из этой записи, а также предложение сортировки, если таковые имеются. В этом случае новое окно не может указывать свою собственную часть PARTITION BY
, и оно может указывать ORDER BY
только в том случае, если скопированное окно его не имеет. Новое окно всегда использует свою собственную часть рамки; скопированное окно не должно указывать часть рамки.
Элементы списка PARTITION BY
интерпретируются почти так же, как элементы предложения
GROUP BY
, за исключением того, что
они всегда являются простыми выражениями и никогда не являются именем или номером
выходной колонки.
Еще одно отличие заключается в том, что эти выражения могут содержать вызовы агрегатных
функций, которые не разрешены в обычном предложении GROUP BY
. Они разрешены здесь,
потому что оконные функции выполняются после группировки и агрегации.
Аналогично, элементы списка ORDER BY
интерпретируются
примерно так же, как элементы
предложения ORDER BY
на уровне оператора, за исключением того, что выражения всегда рассматриваются
как простые выражения и никогда не являются именем или номером выходного столбца.
Необязательное предложение frame_clause
определяет
рамку окна для оконных функций, которые зависят от
рамки (не все функции зависят). рамка окна - это набор связанных строк для
каждой строки запроса (называемой текущей строкой).
frame_clause
можно записать одним из следующих способов
{ RANGE | ROWS | GROUPS }frame_start
[frame_exclusion
] { RANGE | ROWS | GROUPS } BETWEENframe_start
ANDframe_end
[frame_exclusion
]
где frame_start
и frame_end
можно записать одним из следующих способов
UNBOUNDED PRECEDINGoffset
PRECEDING CURRENT ROWoffset
FOLLOWING UNBOUNDED FOLLOWING
и frame_exclusion
можно записать одним из следующих способов
EXCLUDE CURRENT ROW EXCLUDE GROUP EXCLUDE TIES EXCLUDE NO OTHERS
Если frame_end
не указан, то по умолчанию используется CURRENT ROW
. Ограничения заключаются в том, что UNBOUNDED FOLLOWING
нельзя указать как frame_start
, а UNBOUNDED PRECEDING
- в качестве frame_end
, и frame_end
не может идти раньше, чем frame_start
в списке вышеуказанных вариантов frame_start
и frame_end
- например, RANGE BETWEEN CURRENT ROW AND
не допускается.
offset
PRECEDING
Параметр по умолчанию для ограничения рамки - RANGE UNBOUNDED PRECEDING
,
что эквивалентно RANGE BETWEEN UNBOUNDED PRECEDING AND
CURRENT ROW
; это устанавливает рамку, которая включает все строки от начала секции до последней строки peer (строки,
которые в предложении ORDER BY
окна считаются
эквивалентными текущей строке; все строки являются peer, если
нет ORDER BY
).
Обычно, UNBOUNDED PRECEDING
означает, что рамка
начинается с первой строки секции, и аналогично
UNBOUNDED FOLLOWING
означает, что рамка заканчивается последней
строкой секции, независимо
от режима RANGE
, ROWS
или GROUPS
.
В режиме ROWS
, CURRENT ROW
означает,
что рамка начинается или заканчивается текущей строкой; но
в режиме RANGE
или GROUPS
означает,
что рамка начинается или заканчивается первой или последней строкой текущей строки
в порядке ORDER BY
.
Опции offset
PRECEDING
и
offset
FOLLOWING
имеют разное значение в зависимости от режима рамки.
В режиме ROWS
, offset
- это целое число, указывающее, что рамка начинается или заканчивается на столько строк
до или после текущей строки.
В режиме GROUPS
, offset
- это целое число, указывающее, что рамка начинается или заканчивается на столько групп
до или после группы текущей строки, где
группа строк - это группа строк, которые
эквивалентны согласно предложению ORDER BY
окна.
В режиме RANGE
, использование
опции offset
требует наличия
ровно одного столбца ORDER BY
в определении окна.
Затем рамка содержит те строки, значение столбца сортировки которых не
превышает значение столбца сортировки текущей строки на
offset
меньше
(для PRECEDING
) или больше
(для FOLLOWING
) значения столбца сортировки текущей строки.
В этих случаях тип выражения offset
зависит от типа данных столбца сортировки. Для числовых столбцов сортировки он
обычно имеет тот же тип, что и столбец сортировки, но для столбцов сортировки даты и времени это interval
.
Во всех этих случаях значение offset
должно быть не нулевым и неотрицательным. Кроме того, хотя
offset
не обязательно должен быть простой
константой, он не может содержать переменные, агрегатные функции или оконные
функции.
Опция frame_exclusion
позволяет исключить строки вокруг текущей строки из рамки, даже если они должны быть включены в соответствии с настройками начала и конца рамки.
EXCLUDE CURRENT ROW
исключает текущую строку из рамки.
EXCLUDE GROUP
исключает текущую строку и родственные ей строки из рамки.
EXCLUDE TIES
исключает любые родственные строки текущей строки из рамки, но не саму текущую строку.
EXCLUDE NO OTHERS
явно указывает на стандартное поведение, когда текущая строка и ее родственные строки не исключаются.
Будьте осторожны, что режим ROWS
может привести к непредсказуемым результатам, если порядок ORDER BY
не упорядочивает строки уникальным образом. Режимы RANGE
и GROUPS
разработаны для обеспечения одинаковой обработки строк, которые являются пирами в порядке ORDER BY
: все строки данной группы пиров будут включены в рамку или исключены из нее.
Целью предложения WINDOW
является указание поведения оконных функций, которые появляются в списке SELECT
запроса или в предложении ORDER BY
. Эти функции могут ссылаться на записи предложения WINDOW
по имени в своих предложениях OVER
. Однако запись предложения WINDOW
не обязательно должна быть использована где-либо; если она не используется в запросе, она просто игнорируется. Возможно использование оконных функций без предложения WINDOW
вообще, так как вызов оконной функции может указывать свое определение окна непосредственно в своем предложении OVER
. Однако, предложение WINDOW
экономит набор, когда одно и то же определение окна необходимо для нескольких оконных функций.
В настоящее время, FOR NO KEY UPDATE
, FOR UPDATE
,
FOR SHARE
и FOR KEY SHARE
не могут быть
указаны с WINDOW
.
Все функции окон подробно описаны в Раздел 3.5, Раздел 4.2.8 и Раздел 7.2.5.
SELECT
Список
Список SELECT
(между ключевыми словами
SELECT
и FROM
) определяет выражения,
которые формируют выходные строки оператора SELECT
.
Выражения могут (и обычно) ссылаться на столбцы,
вычисленные в предложении FROM
.
Как и в таблице, у каждого выходного столбца SELECT
есть имя. В простом SELECT
это имя просто используется для маркировки столбца для отображения, но когда SELECT
является подзапросом более крупного запроса, имя видно более крупному запросу как имя столбца виртуальной таблицы, созданной подзапросом. Чтобы указать имя для выходного столбца, напишите AS
имя_вывода
после выражения столбца. (Вы можете опустить AS
, но только если желаемое имя вывода не совпадает с любым ключевым словом Tantor BE (см. Предметный указатель C). Для защиты от возможного добавления ключевых слов в будущем рекомендуется всегда либо писать AS
, либо заключать имя вывода в двойные кавычки). Если вы не указываете имя столбца, Tantor BE выбирает имя автоматически. Если выражение столбца является простым ссылкой на столбец, то выбранное имя совпадает с именем этого столбца. В более сложных случаях может использоваться имя функции или типа, или система может прибегнуть к генерируемому имени, такому как ?column?
.
Выводимое имя столбца можно использовать для ссылки на значение столбца в
предложениях ORDER BY
и GROUP BY
, но не в
предложениях WHERE
или HAVING
; в этих случаях необходимо написать
выражение полностью.
Вместо выражения в выходном списке можно написать *
в качестве сокращения для всех столбцов выбранных строк. Также можно написать
в качестве сокращения для столбцов, которые идут только из этой таблицы. В этих случаях нельзя указать новые имена с помощью table_name
.*AS
; имена выходных столбцов будут такими же, как имена столбцов таблицы.
Согласно стандарту SQL, выражения в списке вывода должны быть вычислены перед применением DISTINCT
, ORDER BY
или LIMIT
. Это, очевидно, необходимо при использовании DISTINCT
, поскольку иначе неясно, какие значения делаются уникальными. Однако во многих случаях удобно, чтобы выражения вывода вычислялись после ORDER BY
и LIMIT
, особенно если список вывода содержит волатильные или дорогостоящие функции. С таким поведением порядок вычисления функций более интуитивен, и не будет вычислений, соответствующих строкам, которые не появляются в выводе. Tantor BE эффективно вычисляет выражения вывода после сортировки и ограничения, при условии, что эти выражения не используются в DISTINCT
, ORDER BY
или GROUP BY
. (В качестве противопримера, SELECT f(x) FROM tab ORDER BY 1
явно должен вычислить f(x)
перед сортировкой). Выражения вывода, содержащие функции, возвращающие наборы, эффективно вычисляются после сортировки и перед ограничением, так что LIMIT
будет обрезать вывод от функции, возвращающей набор.
Примечание
Версии PostgreSQL до 9.6 не предоставляли никаких гарантий относительно времени вычисления выходных выражений по сравнению с сортировкой и ограничением; это зависело от формы выбранного плана запроса.
Условие DISTINCT
Если указано SELECT DISTINCT
, все повторяющиеся строки удаляются из результирующего набора (остается одна строка из каждой группы дубликатов). SELECT ALL
указывает на обратное: все строки сохраняются; это значение по умолчанию.
SELECT DISTINCT ON (
сохраняет только первую строку из каждого набора строк, где заданные
выражения оцениваются как равные. Выражения выражение
[, ...] )DISTINCT ON
интерпретируются с использованием тех же правил, что и для
ORDER BY
(см. выше). Обратите внимание, что “первая
строка” из каждого набора непредсказуема, если не использовать ORDER
BY
, чтобы гарантировать, что нужная строка появится первой. Например:
SELECT DISTINCT ON (location) location, time, report FROM weather_reports ORDER BY location, time DESC;
получает самый последний отчет о погоде для каждого местоположения. Но если бы мы не использовали ORDER BY
, чтобы принудительно упорядочить значения времени по убыванию для каждого местоположения, мы получили бы отчет из непредсказуемого времени для каждого местоположения.
Выражение(я) DISTINCT ON
должно совпадать с самым левым выражением(ями) ORDER BY
. Предложение ORDER BY
обычно содержит дополнительные выражения, которые определяют желаемый приоритет строк в каждой группе DISTINCT ON
.
В настоящее время, с FOR NO KEY UPDATE
, FOR UPDATE
,
FOR SHARE
и FOR KEY SHARE
нельзя использовать
опцию DISTINCT
.
Предложение UNION
Предложение UNION
имеет следующую общую форму:
select_statement
UNION [ ALL | DISTINCT ]select_statement
select_statement
- это любой оператор SELECT
без предложения ORDER BY
, LIMIT
, FOR NO KEY UPDATE
, FOR UPDATE
, FOR SHARE
или FOR KEY SHARE
. (ORDER BY
и LIMIT
могут быть применены к подвыражению, если оно заключено в скобки. Без скобок эти предложения будут применены к результату UNION
, а не к его правому входному выражению).
Оператор UNION
вычисляет объединение множеств строк, возвращаемых участвующими операторами SELECT
. Строка принадлежит объединению двух наборов результатов, если она присутствует хотя бы в одном из наборов результатов. Два оператора SELECT
, которые представляют прямые операнды UNION
, должны производить одинаковое количество столбцов, и соответствующие столбцы должны иметь совместимые типы данных.
Результат оператора UNION
не содержит дублирующихся строк, если не указана опция ALL
.
Опция ALL
предотвращает удаление дубликатов. (Поэтому оператор UNION ALL
обычно значительно быстрее оператора UNION
; используйте ALL
, когда это возможно).
Оператор DISTINCT
может быть использован для явного указания стандартного поведения по удалению дублирующихся строк.
Все операторы UNION
в одном операторе SELECT
вычисляются слева направо, если не указано иное в скобках.
В настоящее время нельзя указывать FOR NO KEY UPDATE
, FOR UPDATE
, FOR SHARE
и FOR KEY SHARE
ни для результата UNION
, ни для любого входного значения UNION
.
Предложение INTERSECT
Предложение INTERSECT
имеет следующую общую форму:
select_statement
INTERSECT [ ALL | DISTINCT ]select_statement
select_statement
- это любой оператор SELECT
без предложения ORDER BY
, LIMIT
, FOR NO KEY UPDATE
, FOR UPDATE
, FOR SHARE
или FOR KEY SHARE
.
Оператор INTERSECT
вычисляет пересечение множеств строк, возвращаемых участвующими операторами SELECT
. Строка находится в пересечении двух наборов результатов, если она присутствует в обоих наборах результатов.
Результат INTERSECT
не содержит дублирующихся строк, если не указана опция ALL
.
С опцией ALL
строка, которая имеет m
дубликатов в левой таблице и n
дубликатов в правой таблице, будет появляться
min(m
,n
) раз в наборе результатов.
DISTINCT
может быть записано для явного указания
стандартного поведения по удалению дублирующихся строк.
Все операторы INTERSECT
в одном операторе SELECT
вычисляются слева направо, если скобки не указывают иное.
INTERSECT
имеет более высокий приоритет, чем UNION
. То есть A UNION B INTERSECT C
будет прочитано как A UNION (B INTERSECT C)
.
В настоящее время нельзя указывать FOR NO KEY UPDATE
, FOR UPDATE
, FOR SHARE
и FOR KEY SHARE
ни для результата INTERSECT
, ни для любого входного значения INTERSECT
.
Условие EXCEPT
Предложение EXCEPT
имеет следующую общую форму:
select_statement
EXCEPT [ ALL | DISTINCT ]select_statement
select_statement
- это любой оператор SELECT
без предложения ORDER BY
, LIMIT
, FOR NO KEY UPDATE
, FOR UPDATE
, FOR SHARE
или FOR KEY SHARE
.
Оператор EXCEPT
вычисляет множество строк, которые присутствуют в результате левого оператора SELECT
, но отсутствуют в результате правого оператора.
Результат оператора EXCEPT
не содержит повторяющихся строк, если не указана опция ALL
.
С опцией ALL
строка, которая имеет m
дубликатов в левой таблице и n
дубликатов в правой таблице, будет появляться в результирующем наборе max(m
-n
,0) раз.
Для явного указания стандартного поведения по удалению повторяющихся строк можно использовать ключевое слово DISTINCT
.
Несколько операторов EXCEPT
в одном
операторе SELECT
вычисляются слева направо,
если скобки не указывают иное. EXCEPT
связывается на
том же уровне, что и UNION
.
В настоящее время нельзя указывать FOR NO KEY UPDATE
, FOR UPDATE
, FOR SHARE
и FOR KEY SHARE
ни для результата EXCEPT
, ни для любого входного значения EXCEPT
.
Условие ORDER BY
Необязательное предложение ORDER BY
имеет следующую общую форму:
ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...]
Предложение ORDER BY
приводит к сортировке строк результата в соответствии с указанным выражением(ями). Если две строки равны по левому выражению, они сравниваются по следующему выражению и так далее. Если они равны по всем указанным выражениям, они возвращаются в порядке, зависящем от реализации.
Каждое expression
может быть именем или порядковым номером выходной колонки (элементом списка SELECT
), или это может быть произвольное выражение, составленное из значений входных колонок.
Порядковый номер относится к порядковой (слева направо) позиции выходного столбца. Эта функция позволяет определить порядок на основе столбца, у которого нет уникального имени. Это никогда не является абсолютно необходимым, потому что всегда можно присвоить имя выходному столбцу с помощью ключевого слова AS
.
Также возможно использовать произвольные выражения в предложении ORDER BY
, включая столбцы, которые не
появляются в списке вывода SELECT
. Таким образом,
следующий оператор является допустимым:
SELECT name FROM distributors ORDER BY code;
Ограничением этой функции является то, что в предложении ORDER BY
, применяемом к результату предложения UNION
,
INTERSECT
или EXCEPT
,
можно указывать только имя или номер выходного столбца, а не выражение.
Если выражение ORDER BY
является простым именем, которое совпадает и с именем выходного столбца, и с именем входного столбца, ORDER BY
интерпретирует его как имя выходного столбца. Это противоположный выбор, который сделает GROUP BY
в той же ситуации. Это несогласованность сделана для совместимости со стандартом SQL.
Опционально можно добавить ключевое слово ASC
(возрастающий) или DESC
(убывающий) после любого выражения в предложении ORDER BY
. Если не указано, по умолчанию предполагается ASC
. В качестве альтернативы, в операторе USING
можно указать конкретное имя оператора сортировки. Оператор сортировки должен быть оператором "меньше" или "больше" в некоторой семье операторов B-дерева. Обычно ASC
эквивалентен USING <
, а DESC
эквивалентен USING >
. (Однако создатель пользовательского типа данных может определить точный порядок сортировки по умолчанию, и он может соответствовать операторам с другими именами).
Если указано NULLS LAST
, то значения NULL сортируются после всех ненулевых значений; если указано NULLS FIRST
, то значения NULL сортируются перед всеми ненулевыми значениями. Если ни одно из них не указано, то поведение по умолчанию - NULLS LAST
при указании или подразумевании ASC
, и NULLS FIRST
при указании DESC
(таким образом, по умолчанию считается, что значения NULL больше, чем ненулевые значения). При указании USING
порядок сортировки NULL по умолчанию зависит от того, является ли оператор оператором "меньше" или "больше".
Обратите внимание, что опции сортировки применяются только к выражению, за которым они следуют;
например, ORDER BY x, y DESC
не означает
то же самое, что и ORDER BY x DESC, y DESC
.
Символьные строки сортируются в соответствии с правилом сортировки, применяемым к сортируемому столбцу. При необходимости это можно изменить, включив в предложение COLLATE
вместе с expression
, например ORDER BY mycolumn COLLATE "en_US"
. Дополнительную информацию см. в разделах Раздел 4.2.10 и Раздел 22.2.
Предложение LIMIT
Предложение LIMIT
состоит из двух независимых вложенных предложений:
LIMIT {count
| ALL } OFFSETstart
Параметр count
определяет максимальное количество строк для возврата, в то время как start
указывает количество строк, которые нужно пропустить перед началом возврата строк. Когда оба параметра указаны, сначала пропускается start
строк перед началом подсчета count
строк, которые будут возвращены.
Если выражение count
вычисляется как NULL, оно рассматривается как LIMIT ALL
, то есть без ограничений. Если start
вычисляется как NULL, он рассматривается так же, как OFFSET 0
.
SQL:2008 ввел другой синтаксис для достижения того же результата, который также поддерживает Tantor BE. Он выглядит так:
OFFSETstart
{ ROW | ROWS } FETCH { FIRST | NEXT } [count
] { ROW | ROWS } { ONLY | WITH TIES }
В этом синтаксисе значение start
или count
является обязательным
в соответствии со стандартом и может быть литеральной константой, параметром или именем переменной;
как расширение Tantor BE, допускаются и другие выражения,
но обычно они должны быть заключены в скобки, чтобы избежать неоднозначности.
Если в предложении FETCH
не указано значение count
,
оно по умолчанию равно 1.
Опция WITH TIES
используется для возврата всех дополнительных
строк, которые занимают последнее место в результате согласно
предложению ORDER BY
; в этом случае ORDER BY
обязателен, а SKIP LOCKED
не разрешается.
Слова ROW
и ROWS
, а также
FIRST
и NEXT
являются
шумовыми словами, которые не влияют на результат выполнения этих предложений.
Согласно стандарту, предложение OFFSET
должно предшествовать
предложению FETCH
, если оба присутствуют; но
Tantor BE более гибок и позволяет использовать любой порядок.
При использовании LIMIT
рекомендуется добавить предложение ORDER BY
, которая ограничивает результаты запроса в уникальном порядке. В противном случае вы получите непредсказуемое подмножество строк запроса - вы можете запросить строки с десятой по двадцатую, но с десятой по двадцатую в каком порядке? Вы не знаете, в каком порядке, если не указана ORDER BY
.
Планировщик запросов учитывает ограничение LIMIT
при
генерации плана запроса, поэтому вероятно, что вы получите разные
планы (приводящие к различным порядкам строк), в зависимости от того,
что вы используете для LIMIT
и OFFSET
. Таким образом, использование
разных значений LIMIT
/OFFSET
для выбора
различных подмножеств результата запроса приведет к
непоследовательным результатам, если вы не обеспечите
предсказуемый порядок результатов с помощью ORDER BY
. Это не ошибка;
это неизбежное следствие того факта, что SQL не обещает
предоставить результаты запроса в каком-либо определенном порядке, если
не используется ORDER BY
для ограничения порядка.
Возможно, что повторные выполнения одного и того же запроса с использованием ограничения LIMIT
могут возвращать различные подмножества строк таблицы, если не указано ORDER BY
, чтобы гарантировать выбор детерминированного подмножества. Опять же, это не является ошибкой; детерминизм результатов просто не гарантируется в таком случае.
Предложение блокировки
FOR UPDATE
, FOR NO KEY UPDATE
, FOR SHARE
и FOR KEY SHARE
являются предложениями блокировки; они влияют на то, как SELECT
блокирует строки при их получении из таблицы.
Предложение блокировки имеет общую форму
FORlock_strength
[ OFtable_name
[, ...] ] [ NOWAIT | SKIP LOCKED ]
где lock_strength
можно записать одним из следующих способов
UPDATE NO KEY UPDATE SHARE KEY SHARE
Для получения дополнительной информации о каждом режиме блокировки на уровне строк см. Раздел 13.3.2.
Чтобы предотвратить ожидание завершения других транзакций перед выполнением операции, используйте опцию NOWAIT
или SKIP LOCKED
. С опцией NOWAIT
оператор сообщает об ошибке, а не ожидает, если выбранная строка не может быть немедленно заблокирована. С опцией SKIP LOCKED
пропускаются все выбранные строки, которые не могут быть немедленно заблокированы. Пропуск заблокированных строк обеспечивает несогласованное представление данных, поэтому это не подходит для общего использования, но может быть использовано для избежания конфликтов блокировки при доступе нескольких потребителей к таблице-очереди. Обратите внимание, что опции NOWAIT
и SKIP LOCKED
применяются только к блокировкам на уровне строк, а не к требуемой блокировке таблицы на уровне строк ROW SHARE
(см. Глава 13). Если вам нужно получить блокировку таблицы без ожидания, вы можете сначала использовать LOCK
с опцией NOWAIT
.
Если в блокирующем предложении указаны конкретные таблицы,
то блокируются только строки, приходящие из этих таблиц; любые другие
таблицы, используемые в SELECT
, просто читаются
как обычно. Блокирующее предложение без списка таблиц влияет на все таблицы, используемые в операторе.
Если блокирующее предложение применяется к представлению или подзапросу, она влияет на все таблицы, используемые в
представлении или подзапросе.
Однако эти предложения применяются к запросам WITH
, на которые ссылается основной запрос.
Если нужно, чтобы блокировка строк происходила внутри запроса WITH
, укажите
блокирующее предложение внутри запроса WITH
.
Можно написать несколько блокировок, если необходимо указать разное поведение блокировки для разных таблиц. Если одна и та же таблица упоминается (или неявно затрагивается) более чем одним блокировочным оператором, она обрабатывается так, как если бы она была указана только самым сильным. Аналогично, таблица обрабатывается как NOWAIT
, если это указано в любом из затрагивающих ее операторов. В противном случае, таблица обрабатывается как SKIP LOCKED
, если это указано в любом из затрагивающих ее операторов.
Предложения блокировки не могут быть использованы в контекстах, где возвращенные строки не могут быть однозначно идентифицированы с отдельными строками таблицы; например, они не могут быть использованы с агрегацией.
Когда предложение блокировки появляется на верхнем уровне запроса SELECT
, строки, которые заблокированы, точно те, которые возвращаются запросом; в случае запроса с соединением, заблокированы будут строки, которые вносят вклад в возвращаемые строки соединения. Кроме того, строки, которые удовлетворяли условиям запроса на момент снимка запроса, будут заблокированы, хотя они не будут возвращены, если они были обновлены после снимка и больше не удовлетворяют условиям запроса. Если используется LIMIT
, блокировка останавливается, как только будет возвращено достаточное количество строк для удовлетворения лимита (но обратите внимание, что строки, прне указанные с помощью OFFSET
, будут заблокированы). Аналогично, если предложение блокировки используется в запросе курсора, будут заблокированы только строки, фактически выбранные или прне указанные курсором.
Когда в подзапросе SELECT
присутствует блокировка, заблокированными будут строки, возвращаемые внешнему запросу подзапросом. Это может включать в себя меньшее количество строк, чем предполагает только анализ подзапроса, так как условия из внешнего запроса могут использоваться для оптимизации выполнения подзапроса. Например,
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;
будет блокировать только строки, у которых col1 = 5
, даже если это условие не находится текстом в подзапросе.
Предыдущие версии не сохраняли блокировку, которая обновляется позднее точкой сохранения. Например, следующий код:
BEGIN; SELECT * FROM mytable WHERE key = 1 FOR UPDATE; SAVEPOINT s; UPDATE mytable SET ... WHERE key = 1; ROLLBACK TO s;
не смог бы сохранить блокировку FOR UPDATE
после
ROLLBACK TO
. Это было исправлено в версии 9.3.
Предостережение
Возможно, что команда SELECT
, выполняющаяся на уровне изоляции транзакции READ COMMITTED
и использующая ORDER BY
и предложение блокировки, может возвращать строки в неправильном порядке. Это происходит потому, что сначала применяется ORDER BY
. Команда сортирует результат, но затем может заблокироваться при попытке получить блокировку на одну или несколько строк. Когда SELECT
разблокируется, некоторые значения столбца сортировки могут быть изменены, что приводит к тому, что эти строки кажутся неупорядоченными (хотя они упорядочены по исходным значениям столбца). Это можно обойти, поместив предложение FOR UPDATE/SHARE
в подзапрос, например.
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss ORDER BY column1;
Обратите внимание, что это приведет к блокировке всех строк таблицы mytable
,
в то время как FOR UPDATE
на верхнем уровне заблокирует только
фактически возвращенные строки. Это может привести к значительной разнице в производительности,
особенно если ORDER BY
комбинируется с LIMIT
или другими ограничениями.
Поэтому эта техника рекомендуется только в случае ожидания одновременных обновлений столбцов сортировки
и требуется строго отсортированный результат.
При уровне изоляции транзакций REPEATABLE READ
или SERIALIZABLE
это вызовет сбой сериализации (с SQLSTATE
'40001'
), поэтому нет возможности получить строки в неправильном порядке при этих уровнях изоляции.
Команда TABLE
Команда
TABLE name
эквивалентно
SELECT * FROM name
Это может использоваться как команда верхнего уровня или как синтаксический вариант, экономящий пространство, в частях сложных запросов. Только с предложениями блокировки WITH
, UNION
, INTERSECT
, EXCEPT
, ORDER BY
, LIMIT
, OFFSET
, FETCH
и FOR
можно использовать TABLE
; предложение WHERE
и любая форма агрегации не могут быть использованы.
Примеры
Для соединения таблицы films
с таблицей distributors
:
SELECT f.title, f.did, d.name, f.date_prod, f.kind FROM distributors d JOIN films f USING (did); title | did | name | date_prod | kind -------------------+-----+--------------+------------+---------- The Third Man | 101 | British Lion | 1949-12-23 | Drama The African Queen | 101 | British Lion | 1951-08-11 | Romantic ...
Для суммирования столбца len
всех фильмов и группировки
результатов по kind
:
SELECT kind, sum(len) AS total FROM films GROUP BY kind; kind | total ----------+------- Action | 07:34 Comedy | 02:58 Drama | 14:28 Musical | 06:42 Romantic | 04:38
Для подсчета суммы столбца len
всех фильмов, сгруппируйте
результаты по столбцу kind
и покажите суммы для тех групп,
которые меньше 5 часов:
SELECT kind, sum(len) AS total FROM films GROUP BY kind HAVING sum(len) < interval '5 hours'; kind | total ----------+------- Comedy | 02:58 Romantic | 04:38
Следующие два примера представляют собой идентичные способы сортировки отдельных результатов в соответствии с содержимым второго столбца (name
):
SELECT * FROM distributors ORDER BY name; SELECT * FROM distributors ORDER BY 2; did | name -----+------------------ 109 | 20th Century Fox 110 | Bavaria Atelier 101 | British Lion 107 | Columbia 102 | Jean Luc Godard 113 | Luso films 104 | Mosfilm 103 | Paramount 106 | Toho 105 | United Artists 111 | Walt Disney 112 | Warner Bros. 108 | Westward
Следующий пример показывает, как получить объединение таблиц distributors
и actors
, ограничивая результаты только теми, которые начинаются с буквы W в каждой таблице. Требуются только уникальные строки, поэтому ключевое слово ALL
не указано.
distributors: actors: did | name id | name -----+-------------- ----+---------------- 108 | Westward 1 | Woody Allen 111 | Walt Disney 2 | Warren Beatty 112 | Warner Bros. 3 | Walter Matthau ... ... SELECT distributors.name FROM distributors WHERE distributors.name LIKE 'W%' UNION SELECT actors.name FROM actors WHERE actors.name LIKE 'W%'; name ---------------- Walt Disney Walter Matthau Warner Bros. Warren Beatty Westward Woody Allen
В этом примере показано, как использовать функцию в предложении FROM
, как с указанием списка определений столбцов, так и без него:
CREATE FUNCTION distributors(int) RETURNS SETOF distributors AS $$ SELECT * FROM distributors WHERE did = $1; $$ LANGUAGE SQL; SELECT * FROM distributors(111); did | name -----+------------- 111 | Walt Disney CREATE FUNCTION distributors_2(int) RETURNS SETOF record AS $$ SELECT * FROM distributors WHERE did = $1; $$ LANGUAGE SQL; SELECT * FROM distributors_2(111) AS (f1 int, f2 text); f1 | f2 -----+------------- 111 | Walt Disney
Вот пример функции с добавленным столбцом ordinality:
SELECT * FROM unnest(ARRAY['a','b','c','d','e','f']) WITH ORDINALITY; unnest | ordinality --------+---------- a | 1 b | 2 c | 3 d | 4 e | 5 f | 6 (6 rows)
В этом примере показано, как использовать простое предложение WITH
:
WITH t AS ( SELECT random() as x FROM generate_series(1, 3) ) SELECT * FROM t UNION ALL SELECT * FROM t; x -------------------- 0.534150459803641 0.520092216785997 0.0735620250925422 0.534150459803641 0.520092216785997 0.0735620250925422
Обратите внимание, что запрос WITH
был вычислен только один раз,
поэтому мы получили два набора одинаковых трех случайных значений.
В этом примере используется WITH RECURSIVE
для поиска всех подчиненных (прямых или косвенных) сотрудника Мэри и их уровня косвенности из таблицы, которая показывает только прямых подчиненных:
WITH RECURSIVE employee_recursive(distance, employee_name, manager_name) AS ( SELECT 1, employee_name, manager_name FROM employee WHERE manager_name = 'Mary' UNION ALL SELECT er.distance + 1, e.employee_name, e.manager_name FROM employee_recursive er, employee e WHERE er.employee_name = e.manager_name ) SELECT distance, employee_name FROM employee_recursive;
Обратите внимание на типичную форму рекурсивных запросов: начальное условие, за которым следует UNION
, за которым следует рекурсивная часть запроса. Убедитесь, что рекурсивная часть запроса в конечном итоге не вернет ни одной строки, иначе запрос будет бесконечно выполняться. (См. Раздел 7.8 для получения дополнительных примеров).
В этом примере используется LATERAL
для применения функции, возвращающей набор значений
get_product_names()
для каждой строки таблицы
manufacturers
:
SELECT m.name AS mname, pname FROM manufacturers m, LATERAL get_product_names(m.id) pname;
Производители, у которых в настоящее время нет никаких продуктов, не будут отображаться в результате, так как это внутреннее соединение. Если мы хотим включить имена таких производителей в результат, можно сделать следующее:
SELECT m.name AS mname, pname FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true;
Совместимость
Само собой разумеется, оператор SELECT
совместим с SQL-стандартом. Однако есть некоторые расширения и недостающие функции.
Прне указаны предложения FROM
Tantor BE позволяет опустить предложение
FROM
. Это имеет простое применение для
вычисления результатов простых выражений:
SELECT 2+2; ?column? ---------- 4
Некоторые другие базы данных SQL не могут сделать это, кроме как
путем введения фиктивной таблицы с одной строкой, из которой выполняется
SELECT
.
Пустые списки SELECT
Список выходных выражений после SELECT
может быть пустым, что приводит к созданию таблицы результатов с нулевым количеством столбцов.
Это не является допустимым синтаксисом согласно стандарту SQL.
Tantor BE позволяет это, чтобы быть согласованным с возможностью создания таблиц с нулевым количеством столбцов.
Однако пустой список не допускается при использовании DISTINCT
.
Пропуская ключевое слово AS
В стандарте SQL, необязательное ключевое слово AS
можно опустить перед именем выходной колонки, если новое имя колонки является допустимым именем колонки (то есть не совпадает с зарезервированным ключевым словом). Tantor BE немного более строгий: AS
требуется, если новое имя колонки совпадает с любым ключевым словом, зарезервированным или нет. Рекомендуется использовать AS
или обрамлять имена выходных колонок в двойные кавычки, чтобы предотвратить возможные конфликты с будущими добавлениями ключевых слов.
В элементах FROM
оба стандарт и Tantor BE позволяют опускать AS
перед псевдонимом, который является незарезервированным ключевым словом. Но это непрактично для имен выходных столбцов из-за синтаксической неоднозначности.
Пропуск под-SELECT
псевдонимов в FROM
Согласно стандарту SQL, под-SELECT
в
списке FROM
должен иметь псевдоним. В
Tantor BE этот псевдоним может быть опущен.
ONLY
и Наследование
Стандарт SQL требует наличия скобок вокруг имени таблицы при использовании ключевого слова ONLY
, например SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...
. Tantor BE считает эти скобки необязательными.
Tantor BE позволяет добавить в конец *
, чтобы явно указать, что включение дочерних таблиц происходит без использования ONLY
. Стандарт не разрешает это.
(Эти пункты одинаково применимы ко всем SQL-командам, поддерживающим опцию ONLY
).
Ограничения для предложения TABLESAMPLE
Предложение TABLESAMPLE
в настоящее время принимается только для обычных таблиц и материализованных представлений. Согласно стандарту SQL, ее можно применять к любому элементу FROM
.
Вызовы функций в FROM
Tantor BE позволяет вызывать функцию напрямую в качестве элемента списка FROM
. В стандарте SQL для этого необходимо обернуть вызов функции в подзапрос SELECT
; то есть синтаксис FROM
примерно эквивалентен func
(...) alias
FROM LATERAL (SELECT
. Обратите внимание, что func
(...)) alias
LATERAL
считается неявным; это связано с тем, что стандарт требует использования семантики LATERAL
для элемента UNNEST()
в FROM
. Tantor BE обрабатывает UNNEST()
так же, как и другие функции, возвращающие наборы значений.
Пространство имен доступно для GROUP BY
и ORDER BY
В стандарте SQL-92, в предложении ORDER BY
можно использовать только имена или номера выходных столбцов, в то время как в предложении GROUP BY
можно использовать только выражения, основанные на именах входных столбцов. Tantor BE расширяет каждое из этих предложений, чтобы также разрешить другой выбор (но использует интерпретацию стандарта в случае неоднозначности). Tantor BE также позволяет обоим предложениям указывать произвольные выражения. Обратите внимание, что имена, появляющиеся в выражении, всегда будут приниматься как имена входных столбцов, а не имена выходных столбцов.
SQL:1999 и более поздние версии используют немного другое определение, которое не является полностью обратно совместимым с SQL-92.
Однако в большинстве случаев Tantor BE
будет интерпретировать выражение ORDER BY
или GROUP
BY
так же, как и SQL:1999.
Функциональные зависимости
Tantor BE распознает функциональную зависимость (позволяя опускать столбцы из GROUP BY
) только тогда, когда первичный ключ таблицы включен в список GROUP BY
. Стандарт SQL указывает дополнительные условия, которые должны быть распознаны.
LIMIT
и OFFSET
Предложения LIMIT
и OFFSET
являются специфичным синтаксисом Tantor BE, также используемым MySQL. Стандарт SQL:2008 ввел предложения OFFSET ... FETCH {FIRST|NEXT} ...
для той же функциональности, как показано выше в LIMIT Clause. Этот синтаксис также используется IBM DB2. (Приложения, написанные для Oracle, часто используют обходное решение, включающее автоматически генерируемый столбец rownum
, который недоступен в PostgreSQL, для реализации эффектов этих предложений).
FOR NO KEY UPDATE
, FOR UPDATE
, FOR SHARE
, FOR KEY SHARE
Хотя FOR UPDATE
присутствует в стандарте SQL, стандарт позволяет его использовать только как опцию DECLARE CURSOR
.
Tantor BE позволяет его использовать в любом запросе SELECT
, а также в подзапросах, но это расширение.
Варианты FOR NO KEY UPDATE
, FOR SHARE
и FOR KEY SHARE
, а также опции NOWAIT
и SKIP LOCKED
не присутствуют в стандарте.
Изменение данных в WITH
Tantor BE позволяет использовать запросы INSERT
,
UPDATE
и DELETE
в качестве запросов WITH
.
Это не предусмотрено в стандарте SQL.
Нестандартные условия
DISTINCT ON ( ... )
- это расширение стандарта SQL.
ROWS FROM( ... )
- это расширение стандарта SQL.
Варианты MATERIALIZED
и NOT MATERIALIZED
в WITH
являются расширениями стандарта SQL.