SELECT
- Синтаксис
- Описание
- Параметры
- Примеры
- Совместимость
- Необязательное выражение FROM
- Необязательное ключевое слово AS
- ONLY и наследование
- Пространства имен в GROUP BY и ORDER BY
- Функциональные зависимости
- LIMIT и OFFSET
- FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE
- Изменение данных в WITH
- Нестандартные выражения
- Ограниченное использование функций STABLE и VOLATILE
- См. также
Извлекает строки из таблицы или представления.
Синтаксис
[ WITH [ RECURSIVE ] <with_query> [, ...] ]
SELECT [ALL | DISTINCT [ON (<expression> [, ...])]]
* | <expression> [[AS] <output_name>] [, ...]
[FROM <from_item> [, ...]]
[WHERE <condition>]
[GROUP BY <grouping_element> [, ...]]
[HAVING <condition> [, ...]]
[WINDOW <window_name> AS (<window_definition>) [, ...] ]
[{UNION | INTERSECT | EXCEPT} [ALL | DISTINCT] <select>]
[ORDER BY <expression> [ASC | DESC | USING <operator>] [NULLS {FIRST | LAST}] [, ...]]
[LIMIT {<count> | ALL}]
[OFFSET <start> [ ROW | ROWS ] ]
[FETCH { FIRST | NEXT } [ <count> ] { ROW | ROWS } ONLY]
[FOR {UPDATE | NO KEY UPDATE | SHARE | KEY SHARE} [OF <table_name> [, ...]] [NOWAIT] [...]]
TABLE { [ ONLY ] <table_name> [ * ] | <with_query_name> }
где with_query:
<with_query_name> [( <column_name> [, ...] )] AS ( <select> | <values> | <insert> | <update> | <delete> )
где from_item может быть:
[ONLY] <table_name> [ * ] [ [ AS ] <alias> [ ( <column_alias> [, ...] ) ] ]
( <select> ) [ AS ] <alias> [( <column_alias> [, ...] ) ]
<with_query_name> [ [ AS ] <alias> [ ( <column_alias> [, ...] ) ] ]
<function_name> ( [ <argument> [, ...] ] )
[ WITH ORDINALITY ] [ [ AS ] <alias> [ ( <column_alias> [, ...] ) ] ]
<function_name> ( [ <argument> [, ...] ] ) [ AS ] <alias> ( <column_definition> [, ...] )
<function_name> ( [ <argument> [, ...] ] ) AS ( <column_definition> [, ...] )
ROWS FROM ( <function_name> ( [ <argument> [, ...] ] ) [ AS ( <column_definition> [, ...] ) ] [, ...] )
[ WITH ORDINALITY ] [ [ AS ] <alias> [ ( <column_alias> [, ...] ) ] ]
<from_item> [ NATURAL ] <join_type> <from_item>
[ ON <join_condition> | USING ( <join_column> [, ...] ) ]
где grouping_element может быть:
()
<expression>
ROLLUP (<expression> [,...])
CUBE (<expression> [,...])
GROUPING SETS ((<grouping_element> [, ...]))
где window_definition:
[<existing_window_name>]
[PARTITION BY <expression> [, ...]]
[ORDER BY <expression> [ASC | DESC | USING <operator>] [NULLS {FIRST | LAST}] [, ...]]
[{ RANGE | ROWS} <frame_start> | {RANGE | ROWS} BETWEEN <frame_start> AND <frame_end>]
где frame_start и frame_end могут быть:
UNBOUNDED PRECEDING
<value> PRECEDING
CURRENT ROW
<value> FOLLOWING
UNBOUNDED FOLLOWING
Когда указано условие блокировки (выражение FOR), Global Deadlock Detector влияет на то, как блокируются строки таблицы. См. пункт 11 в разделе Описание и Выражения блокировки ниже в этом разделе.
Описание
SELECT получает строки из множества (возможно, пустого) таблиц. Общий порядок выполнения SELECT выглядит следующим образом:
-
Выполняются все запросы в списке
WITH. Фактически они формируют временные таблицы, на которые можно ссылаться в спискеFROM. -
Вычисляются все элементы в списке
FROM. Каждый элемент спискаFROMпредставляет собой реальную или виртуальную таблицу. Если в спискеFROMуказано более одного элемента, они объединяются перекрестным соединением. -
Если указано условие
WHERE, все строки, не удовлетворяющие этому условию, исключаются из результата. -
Если указано выражение
GROUP BYили в запросе вызываются агрегатные функции, выходные данные разделяются по группам строк, соответствующим одному или нескольким значениям, затем вычисляются результаты агрегатных функций. Если присутствует выражениеHAVING, оно исключает группы, не удовлетворяющие указанному условию. -
Вычисляются фактические выходные строки по указанным в
SELECTвыражениям для каждой выбранной строки или группы строк. -
SELECT DISTINCTисключает повторяющиеся строки из результата.SELECT DISTINCT ONисключает строки, соответствующие всем указанным выражениям.SELECT ALL(по умолчанию) возвращает все строки, включая дубликаты. -
Если указано оконное выражение (и опциональное выражение
WINDOW), вывод организуется в соответствии c заданной на основе количества строк (ROWS) или на основе значений (RANGE) рамкой окна. -
Операторы
UNION,INTERSECTиEXCEPTобъединяют результаты нескольких командSELECTв один результирующий набор:-
UNION— возвращает все строки, которые присутствуют в одном или обоих результирующих наборах; -
INTERSECT— возвращает все строки, которые присутствуют строго в обоих результирующих наборах; -
EXCEPT— возвращает строки, которые присутствуют в первом результирующем наборе, но отсутствуют во втором.
Во всех трех случаях дублирующиеся строки исключаются, если не указан оператор
ALL. Чтобы явно обозначить, что дублирующиеся строки не должны включаться в результат, можно добавить ключевое словоDISTINCT. Обратите внимание, что в данном контекстеDISTINCTиспользуется по умолчанию, хотя в самомSELECTпо умолчанию используетсяALL. -
-
Если присутствует выражение
ORDER BY, возвращаемые строки сортируются в указанном порядке. При отсутствииORDER BYстроки возвращаются в том порядке, который система считает наиболее быстрым для обработки. -
Если указано выражение
LIMIT(илиFETCH FIRST) илиOFFSET, операторSELECTвозвращает только подмножество строк результата. -
Если указано выражение
FOR UPDATE,FOR NO KEY UPDATE,FOR SHAREилиFOR KEY SHARE, операторSELECTблокирует всю таблицу от одновременных обновлений.
Для каждого столбца, используемого в команде SELECT, необходимо иметь привилегию SELECT. Применение блокировок FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE или FOR KEY SHARE требует также наличия привилегии UPDATE (как минимум для одного столбца в каждой выбранной для блокировки таблице).
Параметры
Выражение WITH
Опциональное выражение WITH позволяет указать один или несколько подзапросов, на которые можно ссылаться по имени в основном запросе. Эти подзапросы фактически действуют как временные таблицы или представления на время выполнения основного запроса. Каждый подзапрос может быть оператором SELECT, INSERT, UPDATE или DELETE. При использовании в WITH оператора, изменяющего данные (INSERT, UPDATE или DELETE), обычно добавляется выражение RETURNING. Именно результат RETURNING, а не нижележащая таблица, изменяемая оператором, формирует временную таблицу, которую затем читает основной запрос. Если выражение RETURNING опущено, оператор все равно выполняется, но не выдает никакого результата, поэтому на него нельзя ссылаться как на таблицу в основном запросе.
Для команды SELECT, включающей выражение WITH, это выражение может содержать не более одного оператора, изменяющего данные таблицы (INSERT, UPDATE или DELETE).
Для каждого запроса в выражении WITH должно быть указано имя with_query_name (без схемы). Также можно указать необязательный список имен столбцов; если список не указан, имена столбцов определяются из подзапроса.
Если указано ключевое слово RECURSIVE, подзапрос SELECT может ссылаться сам на себя по имени. Такой подзапрос должен иметь вид:
<non_recursive_term> UNION [ALL | DISTINCT] <recursive_term>
где рекурсивная ссылка на сам запрос находится в правой части оператора UNION. В одном запросе допускается только одна рекурсивная ссылка на него же. Операторы, изменяющие данные, не могут быть рекурсивными, но результаты рекурсивного запроса SELECT в таких операторах можно использовать.
Если указано ключевое слово RECURSIVE, запросы WITH не обязательно должны быть упорядочены: запрос может ссылаться на другой запрос, который находится в списке после него. Однако циклические ссылки или взаимная рекурсия не поддерживаются.
Без ключевого слова RECURSIVE запрос в WITH может ссылаться только на запрос в WITH того же уровня, который находится раньше в списке WITH.
WITH RECURSIVE имеет ограничения. Не поддерживается:
-
Рекурсивное выражение
WITH, которое содержит вrecursive_term:-
подзапросы со ссылками на себя;
-
выражение
DISTINCT; -
выражение
GROUP BY; -
оконную функцию.
-
-
Рекурсивное выражение
WITH, в которомwith_query_nameявляется частью оператора множеств.
Ниже приведен пример ограничения, связанного с оператором множеств. Этот запрос возвращает ошибку, поскольку оператор UNION содержит ссылку на таблицу foo:
WITH RECURSIVE foo(i) AS (
SELECT 1
UNION ALL
SELECT i+1 FROM (SELECT * FROM foo UNION SELECT 0) bar
)
SELECT * FROM foo LIMIT 5;
Это рекурсивное общее табличное выражение (Common Table Expression, CTE) разрешено, поскольку оператор UNION не содержит ссылку на CTE foo:
WITH RECURSIVE foo(i) AS (
SELECT 1
UNION ALL
SELECT i+1 FROM (SELECT * FROM bar UNION SELECT 0) bar, foo
WHERE foo.i = bar.a
)
SELECT * FROM foo LIMIT 5;
Ключевое свойство запросов WITH заключается в том, что они выполняются только один раз за выполнение основного запроса, даже если основной запрос ссылается на них более одного раза. В частности, гарантируется, что операторы, изменяющие данные, будут выполнены только один раз, независимо от того, в каком объеме основной запрос будет считывать их выходные данные (полностью или только часть).
Основной запрос и все запросы WITH, условно говоря, выполняются одновременно. Это означает, что действие оператора, изменяющего данные, в WITH нельзя увидеть из других частей запроса, кроме как прочитав его вывод RETURNING. Если два таких оператора попытаются изменить одну строку, результат будет неопределенным.
За дополнительной информацией обратитесь к статье Общие табличные выражения (CTE).
Список SELECT
Список SELECT (между ключевыми словами SELECT и FROM) содержит выражения, формирующие выходные строки оператора SELECT. Эти выражения могут ссылаться (и обычно ссылаются) на столбцы, вычисленные в выражении FROM.
Выражение в списке SELECT может представлять собой постоянное значение, ссылку на столбец, вызов оператора, вызов функции, агрегатное выражение, оконное выражение, скалярный подзапрос и др. Ряд конструкций можно классифицировать как выражения, но они не подчиняются каким-либо общим синтаксическим правилам. Как правило, они имеют семантику функции или оператора.
Как и в таблице, каждый выходной столбец запроса SELECT имеет имя. В простом запросе SELECT это имя используется только для обозначения столбца при выводе, но когда SELECT является подзапросом большого запроса, имя воспринимается этим запросом как имя столбца виртуальной таблицы, созданной подзапросом. Чтобы указать имя для выходного столбца, нужно написать AS <output_name> после выражения столбца. Слово AS можно опустить, но только если желаемое имя выходного столбца не совпадает ни с одним ключевым словом SQL. Чтобы не зависеть от возможного добавления новых ключевых слов в будущем, рекомендуется всегда писать AS, либо заключать имя выходного столбца в двойные кавычки. Если имя столбца не указано, Greengage DB выберет имя автоматически. Если выражение столбца представляет собой просто ссылку на столбец, то выбранное имя совпадает с именем этого столбца. В более сложных случаях может использоваться имя функции или типа, или система может сгенерировать имя (например, ?column? или columnN).
Имя выходного столбца можно использовать для ссылки на значение столбца в выражениях ORDER BY и GROUP BY, но не WHERE или HAVING (в них должны быть выражения).
Вместо выражения в выходном списке можно использовать * в качестве сокращенного обозначения всех столбцов выбранных строк. Также можно использовать table_name.* в качестве сокращенного обозначения столбцов, взятых только из этой таблицы. В этих случаях невозможно указать новые имена с помощью AS (имена выходных столбцов будут совпадать с именами столбцов таблицы).
Выражение DISTINCT
Если указан SELECT DISTINCT, все повторяющиеся строки исключаются из результирующего набора (из каждой группы дубликатов остается одна строка). SELECT ALL указывает на обратное: сохраняются все строки (поведение по умолчанию).
SELECT DISTINCT ON (expression [, …]) сохраняет только первую строку из каждого набора строк, для которого данное выражение дает одинаковые значения. Выражения 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.
Выражение FROM
В выражении FROM указывается одна или несколько таблиц в качестве источника данных для запроса SELECT. Если указано несколько источников, результатом будет декартово произведение (перекрестное соединение) всех их строк. Но обычно добавляются уточняющие условия (с помощью WHERE), чтобы ограничить возвращаемые строки небольшим подмножеством декартова произведения. Выражение FROM может содержать следующие элементы.
| Элемент FROM | Описание |
|---|---|
table_name |
Имя (опционально указанное со схемой) существующей таблицы или представления. Если указан |
alias |
Альтернативное имя элемента |
select |
В выражении |
with_query_name |
В выражении Запрос |
function_name |
В выражении |
join_type |
Один из следующих вариантов:
Для типов соединения Оператор Типы соединения
Напротив,
|
ON <join_condition> |
|
USING (<join_column> [, …]) |
Условие вида |
NATURAL |
|
Выражение WHERE
Синтаксис в общем виде опционального выражения WHERE:
WHERE <condition>
где condition — любое выражение, результатом которого является значение типа boolean. Любая строка, не удовлетворяющая этому условию, будет исключена из результата. Строка удовлетворяет условию, если она возвращает true при подстановке фактических значений строки вместо ссылок на переменные.
Выражение GROUP BY
Синтаксис в общем виде опционального выражения GROUP BY:
GROUP BY <grouping_element> [, ...]
где grouping_element может быть:
()
<expression>
ROLLUP (<expression> [,...])
CUBE (<expression> [,...])
GROUPING SETS ((<grouping_element> [, ...]))
GROUP BY собирает в одну строку все выбранные строки, имеющие одинаковые значения для выражений группировки. Выражение может быть именем входного столбца, именем или порядковым номером выходного столбца (элемент списка SELECT) или произвольным выражением, сформированным из значений входного столбца. В случае неоднозначности имя в GROUP BY будет интерпретироваться как имя входного, а не выходного столбца.
Агрегатные функции, если они используются, вычисляются по всем строкам, составляющим каждую группу, и выдают отдельное значение для каждой группы. Если есть агрегатные функции, но отсутствует выражение GROUP BY, запрос рассматривается как имеющий одну группу, включающую все выбранные строки. Набор строк, передаваемых в каждую агрегатную функцию, может быть дополнительно отфильтрован путем добавления выражения FILTER к вызову агрегатной функции. При наличии FILTER на вход агрегатной функции поступают только те строки, которые соответствуют заданному фильтру.
Если в запросе присутствует GROUP BY или какие-либо агрегатные функции, то выражения в списке SELECT не могут обращаться к не группируемым столбцам, кроме как в агрегатных функциях или когда не группируемый столбец функционально зависит от группируемых столбцов, поскольку иначе для не группируемого столбца могло бы быть возвращено более одного значения. Функциональная зависимость образуется, если группируемые столбцы (или их подмножество) являются первичным ключом таблицы, содержащей не группируемый столбец.
Следует помнить, что все агрегатные функции вычисляются до вычисления любых скалярных выражений в предложении HAVING или списке SELECT. Это означает, что, например, выражение CASE нельзя использовать для пропуска вычисления агрегатной функции.
Greengage DB имеет следующие дополнительные расширения для группировки OLAP (часто называемые супергруппами).
| Расширение для группировки | Описание |
|---|---|
ROLLUP |
Группировка
эквивалентно:
Обратите внимание, что |
CUBE |
Группировка
эквивалентно:
Обратите внимание, что ПРИМЕЧАНИЕ
Greengage DB поддерживает возможность указать максимум 12 столбцов группировки |
GROUPING SETS |
Вы можете выборочно указать набор групп, которые хотите создать, используя выражение
|
При использовании расширений группировки ROLLUP, CUBE или GROUPING SETS возникают две проблемы. Во-первых, как определить, какие результирующие строки являются промежуточными итогами, и затем точный уровень агрегации для данного промежуточного итога? Или как отличить результирующие строки, содержащие как сохраненные значения NULL, так и значения NULL, возвращаемые ROLLUP или CUBE? Во-вторых, когда в выражении GROUP BY указаны повторяющиеся наборы группировок, как определить, какие результирующие строки являются дубликатами? Для решения этой проблемы можно использовать две дополнительные функции группировки в списке SELECT:
-
grouping(column [, …])— функцияgroupingможет применяться к одному или нескольким атрибутам группировки чтобы отличать строки, которые представляют собой супергруппы, от строк, которые являются обычными группами. Это может быть полезно, чтобы отличать значение NULL, представляющее собой набор всех значений в строке-супергруппе, от значения NULL в обычной строке. Каждый аргумент этой функции возвращает бит — либо1, либо0, где1означает, что результирующая строка является супергруппой (то есть является промежуточным итогом), а0означает, что результирующая строка относится к обычной группировке. Функцияgroupingвозвращает целое число, обрабатывая эти биты как двоичное число, а затем преобразуя его в десятичное целое число. -
group_id()— для запросов с расширениями группировки, содержащих повторяющиеся наборы группировок, функцияgroup_idиспользуется для идентификации повторяющихся строк в выходных данных. Все уникальные выходные строки наборов группировок будут иметь значениеgroup_id, равное0. Для каждого обнаруженного повторяющегося набора группировки функцияgroup_idприсваивает номер больше0. Все выходные строки в конкретном повторяющемся наборе группировки идентифицируются одним и тем же номеромgroup_id.
Выражение WINDOW
Необязательное выражение WINDOW определяет поведение оконных функций, фигурирующих в списке SELECT или выражении ORDER BY запроса. Эти функции могут ссылаться на элементы WINDOW по именам в своих выражениях OVER. Однако элементы WINDOW не обязательно должны быть использованы в запросе; если они не используются, они просто игнорируются. Можно использовать оконные функции вообще без выражения WINDOW, поскольку в вызове оконной функции может напрямую указываться определение окна непосредственно в выражении OVER. Однако выражение WINDOW позволяет уменьшить текст запроса, когда одно и то же определение окна применяется при вызове нескольких оконных функций.
Например:
SELECT vendor, rank() OVER (mywindow) FROM sale
GROUP BY vendor
WINDOW mywindow AS (ORDER BY sum(prc*qty));
Синтаксис выражения WINDOW в общем виде:
WINDOW <window_name> AS (<window_definition>)
где window_name — это имя, на которое можно ссылаться из выражений OVER или последующих определений окон, а window_definition — описание окна, которое имеет следующий вид:
[<existing_window_name>]
[PARTITION BY <expression> [, ...]]
[ORDER BY <expression> [ASC | DESC | USING <operator>] [NULLS {FIRST | LAST}] [, ...] ]
[<frame_clause>]
Описание окна имеет следующие параметры.
| Параметр | Описание |
|---|---|
existing_window_name |
Если указано имя окна |
PARTITION BY |
Выражение Аналогично, элементы списка |
ORDER BY |
Элементы выражения ПРИМЕЧАНИЕ
Столбцы типов данных, в которых отсутствует согласованный порядок, например, |
frame_clause |
Необязательное выражение
где
Если параметр По умолчанию рамка формируется выражением Следует помнить, что в режиме Для указания границ окна используйте выражение
Если выражение |
Выражение HAVING
Синтаксис опционального выражения HAVING в общем виде:
HAVING <condition>
где condition указывается так же, как для выражения WHERE. HAVING исключает из результата строки групп, которые не удовлетворяют условию. HAVING отличается от WHERE: WHERE фильтрует отдельные строки перед применением GROUP BY, а HAVING фильтрует строки групп, созданные выражением GROUP BY. Каждый столбец, присутствующий в условии, должен однозначно ссылаться на столбец группировки, за исключением случаев, когда ссылка находится внутри агрегатной функции или не сгруппированный столбец функционально зависит от столбцов группировки.
Наличие условия HAVING превращает запрос в группируемый, даже если в нем отсутствует GROUP BY. То же самое происходит, когда запрос содержит агрегатные функции, но не содержит выражения GROUP BY. Все выбранные строки считаются образующими одну группу, а в списке SELECT и выражении HAVING можно ссылаться на столбцы таблицы только из агрегатных функций. Такой запрос будет выдавать одну строку, если условие HAVING истинно, и ноль строк, если оно ложно.
Выражение 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 BY <expression> [ASC | DESC | USING <operator>] [NULLS {FIRST | LAST}] [,...]
где expression может быть именем или порядковым номером выходного столбца (элемента списка SELECT), либо произвольным выражением со значениями входных столбцов.
ORDER BY сортирует строки результата в соответствии с указанными выражениями. Если две строки равны по самому левому выражению, они сравниваются по следующему выражению и так далее. Если они равны по всем указанным выражениям, они возвращаются в порядке, зависящем от реализации.
Порядковый номер указывает на позицию (при нумерации слева направо) выходного столбца. Возможность указать порядковый номер позволяет выполнить сортировку по столбцу, не имеющему уникального имени. Это не абсолютно необходимо, поскольку выходному столбцу всегда можно присвоить имя с помощью AS.
В выражении ORDER BY также можно использовать произвольные выражения, в том числе со столбцами, отсутствующими в списке результатов SELECT. Таким образом, следующий запрос является корректным:
SELECT name FROM distributors ORDER BY code;
Однако если ORDER BY применяется к результату UNION, INTERSECT или EXCEPT, в выражении ORDER BY можно указывать только имя или номер выходного столбца, но не выражение.
Если выражение ORDER BY представляет собой простое имя, совпадающее как с именем выходного столбца, так и с именем входного столбца, то ORDER BY интерпретирует его как имя выходного столбца. Этот выбор противоположен тому, что делает GROUP BY в той же ситуации. Такая несогласованность допускается для соответствия стандарту SQL.
Дополнительно после любого выражения в выражении ORDER BY можно добавить ключевое слово ASC (по возрастанию) или DESC (по убыванию). По умолчанию используется ASC. Кроме того, в выражении USING можно указать имя конкретного оператора сортировки. ASC обычно эквивалентно USING <, а DESC — USING >. Однако при создании нестандартного типа данных можно определить по-другому порядок сортировки по умолчанию и поставить ему в соответствие операторы с другими именами.
Если указан NULLS LAST, значения NULL при сортировке оказываются после всех значений не NULL; при указании NULLS FIRST значения NULL оказываются перед всеми значениями не NULL. Если не указано ни то, ни другое, по умолчанию используется поведение NULLS LAST, если указан или подразумевается порядок ASC, и NULLS FIRST, если указан порядок DESC (таким образом, по умолчанию значения NULL считаются больше значений не NULL). При указании USING порядок значений NULL по умолчанию зависит от того, является ли указанный оператор оператором "меньше" или "больше".
Обратите внимание, что параметры сортировки применяются только к выражению, за которым они следуют; например, ORDER BY x, y DESC означает не то же самое, что ORDER BY x DESC, y DESC.
Символьно-строковые данные сортируются в соответствии со специфичным для локали порядком сопоставления, который был установлен при создании базы данных.
Символьно-строковые данные сортируются в соответствии с кодировкой, применяемой к сортируемому столбцу. При необходимости ее можно изменить, добавив в выражение COLLATE, например, ORDER BY mycolumn COLLATE "en_US". Информацию об определении кодировок можно посмотреть в статье CREATE COLLATION.
Выражение LIMIT
Выражение LIMIT состоит из двух независимых вложенных выражений:
LIMIT {<count> | ALL}
OFFSET <start>
где count определяет максимальное количество возвращаемых строк, а start — количество строк, которые следует пропустить, прежде чем начать возвращать строки. Если указаны оба параметра, сначала строки пропускаются в количестве start, затем возвращаются следующие строки в количестве, не превышающем значение count.
Если выражение count возвращает NULL, оно рассматривается как LIMIT ALL, то есть число строк не ограничивается. Если выражение start возвращает NULL, оно рассматривается как OFFSET 0.
В SQL:2008 был введен другой синтаксис для достижения того же результата, который также поддерживается Greengage DB. Он выглядит следующим образом:
OFFSET <start> [ ROW | ROWS ]
FETCH { FIRST | NEXT } [ <count> ] { ROW | ROWS } ONLY
В этом синтаксисе значение start или count должно быть, согласно стандарту, литеральной константой, параметром или именем переменной; в Greengage DB допускаются и другие выражения, но, как правило, их необходимо заключать в скобки во избежание неоднозначности. Если count опускается в выражении FETCH, по умолчанию оно равно 1. ROW и ROWS, а также FIRST и NEXT являются незначащими словами, которые не влияют на действие этих выражений. Согласно стандарту, выражение OFFSET должно стоять перед FETCH, если они оба присутствуют; однако Greengage DB допускает любой порядок.
При использовании LIMIT рекомендуется добавлять условие ORDER BY, чтобы строки результата выдавались в определенном порядке. Иначе будут возвращаться непредсказуемые подмножества строк запроса — вы можете запрашивать строки с десятой по двадцатую, но в каком порядке? Порядок будет неизвестен, если не указать ORDER BY.
Планировщик запросов учитывает LIMIT при генерации плана выполнения запроса, поэтому, скорее всего, планы будут разными (а значит и порядок строк) в зависимости от значений LIMIT и OFFSET. Таким образом, использование разных значений LIMIT/OFFSET для выбора разных подмножеств результатов запроса приведет к непоследовательным результатам, если не установить предсказуемую сортировку с помощью ORDER BY. Это не недостаток, а неизбежное следствие того, что SQL не гарантирует предоставление результатов запроса в каком-либо определенном порядке, если порядок явно не определен выражением ORDER BY.
Выражения блокировки
FOR UPDATE, FOR NO KEY UPDATE, FOR SHARE и FOR KEY SHARE — выражения блокировки, которые влияют на то, как оператор SELECT блокирует строки, получаемые из таблицы.
Условие блокировки в общем виде:
FOR <lock_strength> [OF <table_name> [ , ... ] ] [ NOWAIT ]
где lock_strength может быть:
-
FOR UPDATE— блокирует таблицу в режимеEXCLUSIVE. -
FOR NO KEY UPDATE— блокирует таблицу в режимеEXCLUSIVE. -
FOR SHARE— блокирует таблицу в режимеROW SHARE. -
FOR KEY SHARE— блокирует таблицу в режимеROW SHARE.
По умолчанию Greengage DB устанавливает более строгую блокировку EXCLUSIVE (в отличие от ROW EXCLUSIVE в PostgreSQL) для операций UPDATE, DELETE и SELECT … FOR UPDATE в heap-таблицах. При включении Global Deadlock Detector для операций UPDATE и DELETE в heap-таблицах устанавливается режим блокировки ROW EXCLUSIVE. Greengage DB всегда удерживает блокировку на уровне таблицы при использовании операторов SELECT … FOR UPDATE.
Для получения более подробной информации о каждом режиме блокировки на уровне строк обратитесь к статье Explicit Locking в документации PostgreSQL.
Чтобы операция не ждала завершения других транзакций, используйте опцию NOWAIT. С этой опцией оператор сообщает об ошибке, а не ждет, если выбранную строку не удается заблокировать немедленно. Обратите внимание, что NOWAIT влияет только на то, ожидает ли оператор SELECT получения блокировок на уровне строк. Необходимая блокировка на уровне таблицы всегда устанавливается обычным способом. Например, оператор SELECT FOR UPDATE NOWAIT всегда будет ожидать необходимой блокировки на уровне таблицы; он ведет себя так, как если опция NOWAIT не применяется. Если необходимо получить блокировку на уровне таблицы без ожидания, можно сначала выполнить LOCK с указанием NOWAIT.
Если в выражении блокировки указаны определенные таблицы, то блокируются только строки, получаемые из этих таблиц; любые другие таблицы, используемые в запросе SELECT, будут считываться как обычно. Выражение блокировки без списка таблиц влияет на все таблицы, используемые в запросе. Если выражение блокировки применяется к представлению или подзапросу, оно влияет на все таблицы, используемые в представлении или подзапросе. Однако эти выражения не применяются к запросам WITH, на которые ссылается основной запрос. Если требуется установить блокировку строк в запросе WITH, укажите выражение блокировки внутри запроса WITH.
В запрос можно добавить несколько выражений блокировки, если необходимо указать различное поведение блокировки для разных таблиц. Если одна и та же таблица упоминается (или неявно затрагивается) в нескольких выражениях блокировки, то блокировка устанавливается так, как если бы было указано только одно самое строгое условие. Аналогично, если в одном из выражений указано NOWAIT, для таблицы блокировка будет запрашиваться без ожидания.
Выражения блокировки нельзя использовать в контекстах, где возвращаемые строки не могут быть четко идентифицированы с отдельными строками таблицы; например, блокировку нельзя использовать при агрегировании.
Если в запросе SELECT на верхнем уровне присутствует выражение блокировки, блокируются только те строки, которые возвращаются запросом; в случае запроса с объединением блокируются строки, из которых составляются возвращаемые строки объединения. Кроме того, будут заблокированы строки, удовлетворяющие условиям запроса на момент создания снимка запроса, хотя они не будут возвращены, если изменятся с момента снимка и перестанут удовлетворять условиям. Если используется условие LIMIT, блокировка прекращается, как только будет возвращено достаточное количество строк для удовлетворения лимита (но обратите внимание, что строки, пропускаемые условием OFFSET, будут заблокированы). Аналогично, если выражение блокировки используется в запросе курсора, будут заблокированы только строки, фактически полученные или пройденные курсором.
Когда в подзапросе SELECT присутствует выражение блокировки, блокируются те строки, которые подзапрос возвращает во внешний запрос. Таких строк может оказаться меньше, чем можно предположить, проанализировав только сам подзапрос, так как условия из внешнего запроса могут способствовать оптимизации выполнения подзапроса. Например,
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;
заблокирует только строки, у которых col1 = 5, даже если это условие не указано в тексте подзапроса.
Команда 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>
Ее можно использовать как команду верхнего уровня или как более краткую запись внутри сложных запросов.
Примеры
Соединение таблицы films с таблицей distributors:
SELECT f.title, f.did, d.name, f.date_prod, f.kind
FROM distributors d,
films f
WHERE f.did = d.did
Суммирование значений столбца length для всех фильмов и группирование результатов по столбцу kind:
SELECT kind, sum(length) AS total
FROM films
GROUP BY kind;
Суммирование значений столбца length для всех фильмов, группирование результатов по столбцу kind и вывод только тех групп, в которых общая продолжительность фильмов меньше 5 часов:
SELECT kind, sum(length) AS total
FROM films
GROUP BY kind
HAVING sum(length) < interval '5 hours';
Вычисление промежуточных и общих сумм всех продаж по категориям kind и distributor:
SELECT kind, distributor, sum(prc * qty)
FROM sales
GROUP BY ROLLUP (kind, distributor)
ORDER BY 1, 2, 3;
Вычисление рейтинга кинокомпаний на основе общего объема продаж:
SELECT distributor,
sum(prc * qty),
rank() OVER (ORDER BY sum(prc * qty) DESC)
FROM sale
GROUP BY distributor
ORDER BY 2 DESC;
Следующие два примера демонстрируют идентичные способы сортировки результатов по содержимому второго столбца (name):
SELECT * FROM distributors ORDER BY name;
SELECT * FROM distributors ORDER BY 2;
В следующем примере показано, как получить объединение таблиц distributors и actors, ограниченное именами, начинающимися с буквы W в каждой таблице. Необходимо вывести только уникальные строки, поэтому ключевое слово ALL опущено:
SELECT distributors.name
FROM distributors
WHERE distributors.name LIKE 'W%'
UNION
SELECT actors.name
FROM actors
WHERE actors.name LIKE 'W%';
В этом примере показано, как использовать функцию в выражении FROM, со списком определений столбцов и без него:
CREATE FUNCTION distributors(int)
RETURNS SETOF distributors
AS
$$
SELECT *
FROM distributors
WHERE did = $1;
$$ LANGUAGE SQL;
SELECT *
FROM distributors(111);
CREATE FUNCTION distributors_2(int)
RETURNS SETOF record AS
$$
SELECT *
FROM distributors
WHERE did = $1;
$$ LANGUAGE SQL;
SELECT *
FROM distributors_2(111) AS (dist_id int, dist_name text);
Пример использования простого выражения WITH:
WITH test AS (SELECT random() as x
FROM generate_series(1, 3))
SELECT * FROM test
UNION ALL
SELECT * FROM test;
В этом примере используется выражение WITH для отображения итоговых продаж по каждому продукту только в регионах с наибольшими объемами продаж:
WITH regional_sales AS (SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region),
top_regions AS (SELECT region
FROM regional_sales
WHERE total_sales > (SELECT SUM(total_sales)
FROM regional_sales))
SELECT region,
product,
SUM(quantity) AS product_units,
SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;
Запрос можно было бы написать и без WITH, но тогда потребовалось бы два уровня вложенных операторов SELECT.
В этом примере используется выражение WITH RECURSIVE для поиска всех подчиненных (прямых или косвенных) сотрудника Mary и вывода их уровня косвенности из таблицы с информацией только о прямых подчиненных:
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 [ALL], а затем рекурсивная часть запроса. Убедитесь, что рекурсивная часть запроса в конечном итоге не вернет ни одного кортежа, иначе запрос окажется в бесконечном цикле. Дополнительные примеры можно посмотреть в статье Общие табличные выражения (CTE).
Совместимость
Оператор SELECT совместим со стандартом SQL, но имеет некоторые расширения, а некоторые возможности, наоборот, не реализованы.
Необязательное выражение FROM
В Greengage DB можно опустить выражение FROM, что позволяет легко вычислять результаты простых выражений. Например:
SELECT 2+2;
В некоторых других SQL-базах данных это невозможно — требуется использовать фиктивную таблицу с одной строкой, из которой затем выполняется запрос SELECT.
Обратите внимание, что если выражение FROM не указано, запрос не сможет ссылаться ни на какие таблицы базы данных. Например, следующий запрос является недопустимым:
SELECT distributors.* WHERE distributors.name = 'Westward';
Необязательное ключевое слово AS
В стандарте SQL необязательное ключевое слово AS можно опускать перед именем выходного столбца, если новое имя столбца является допустимым именем столбца (то есть не совпадает ни с одним зарезервированным ключевым словом). Greengage DB немного более строг: AS требуется, если новое имя столбца совпадает с любым ключевым словом, зарезервированным или нет. Рекомендуется использовать AS или имена выходных столбцов в двойных кавычках, чтобы предотвратить возможные конфликты с будущими добавлениями ключевых слов.
В списке FROM как стандартная база данных, так и Greengage DB позволяют опускать AS перед псевдонимом, являющимся незарезервированным ключевым словом. Однако это не подходит для имен выходных столбцов из-за синтаксической неоднозначности.
ONLY и наследование
В соответствии со стандартом SQL, при написании ONLY имя таблицы должно заключаться в скобки, например:
SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...
В Greengage DB эти скобки считаются необязательными.
В Greengage DB допускается добавление символа * в конце, чтобы явно указать, что дочерние таблицы включаются в обработку. Стандарт не позволяет этого.
Эти положения в равной степени относятся ко всем командам SQL, поддерживающим опцию ONLY.
Пространства имен в GROUP BY и ORDER BY
В стандарте SQL-92 выражение ORDER BY может содержать ссылки только на выходные столбцы, тогда как GROUP BY может содержать выражения с именами только входных столбцов. Greengage DB расширяет оба этих выражения, допуская также другие варианты (но использует интерпретацию стандарта в случае неоднозначности). Greengage DB также позволяет в обоих предложениях указывать произвольные выражения. Обратите внимание, что имена, фигурирующие в выражении, всегда рассматриваются как имена входных, а не выходных столбцов.
В SQL:1999 и более поздних версиях используется несколько иное определение, которое не полностью совместимо с SQL-92. Однако в большинстве случаев Greengage DB интерпретирует выражения ORDER BY и GROUP BY так, как требует SQL:1999.
Функциональные зависимости
Greengage DB распознает функциональную зависимость (позволяя опускать столбцы в GROUP BY) только в том случае, если первичный ключ таблицы включен в список GROUP BY. Стандарт SQL определяет дополнительные условия, которые должны быть учтены.
LIMIT и OFFSET
Операторы LIMIT и OFFSET — синтаксис, специфичный для Greengage DB, поддерживаемый также в MySQL. В стандарте SQL:2008 для той же цели вводятся операторы OFFSET .. FETCH {FIRST|NEXT} …, рассмотренные ранее. Этот синтаксис также используется в IBM DB2. В приложениях для Oracle для реализации действия этих операторов часто используется обходной путь с применением автоматически генерируемого столбца rownum, который отсутствует в Greengage DB.
FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE
Хотя оператор FOR UPDATE присутствует в стандарте SQL, стандарт позволяет его использовать только в качестве опции оператора DECLARE CURSOR. Greengage DB допускает его использование в любом запросе SELECT, а также в подзапросах SELECT, но это является расширением. Варианты FOR NO KEY UPDATE, FOR SHARE и FOR KEY SHARE, а также опция NOWAIT в стандарте отсутствуют.
Изменение данных в WITH
В Greengage DB можно использовать INSERT, UPDATE и DELETE в качестве запросов WITH. Стандарт SQL этого не предусматривает.
Нестандартные выражения
В стандарте SQL не определено выражение DISTINCT ON.
Ограниченное использование функций STABLE и VOLATILE
Чтобы предотвратить рассинхронизацию данных между сегментами в Greengage DB, любая функция, классифицированная как STABLE или VOLATILE, не может быть запущена на уровне сегмента, если она содержит SQL или каким-либо образом изменяет базу данных. Дополнительную информацию можно посмотреть в статье CREATE FUNCTION.