×
Мы обрабатываем cookies, чтобы сделать наш сайт удобнее и персонализированнее для вас. Подробнее: политика использования «cookies» и «политики конфиденциальности».

Для самостоятельной настройки ознакомьтесь с инструкцией

Дополнительные настройки cookies в браузерах

Файлы cookie автоматически загружаются в ваш браузер при посещении веб-сайта. У вас есть возможность управлять этими файлами. Если Вы не согласны с использованием файлов cookies, запретите их сохранение на своём устройстве, удалите уже имеющиеся файлы cookies через настройки браузера или прекратите использование сайта.

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

Инструкция по отключению cookies
Принять
Настроить
Отклонить

ДОКУМЕНТАЦИЯ

Выберите версию, форк и язык для СУБД Digital Q.DataBase, чтобы прочитать или скачать всю документацию.
Техподдержка
Документация
Диасофт
Авторские права © 2016–2025 ООО "Диасофт Экосистема"
Скачать всю документацию:

2.4.2. Табличные выражения

2.4.2.1. Предложение FROM
2.4.2.2. Предложение WHERE
2.4.2.3. Предложения GROUP BY и HAVING
2.4.2.4. GROUPING SETS, CUBE, и ROLLUP
2.4.2.5. Обработка оконных функций

Табличное выражение табличное выражение вычисляет таблицу. Табличное выражение содержит FROM предложение, за которым при необходимости может следовать WHERE, GROUP BY, и HAVING предложения. Простые табличные выражения просто ссылаются на таблицу на диске, так называемую базовую таблицу, но более сложные выражения могут использоваться для изменения или объединения базовых таблиц различными способами.

Необязательные WHERE, GROUP BY, и HAVING предложения в табличном выражении определяют конвейер последовательных преобразований, выполняемых над таблицей, полученной в FROM предложении. Все эти преобразования создают виртуальную таблицу, строки которой передаются в список выбора для формирования выходных строк запроса.

2.4.2.1. Предложение FROM #

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

предложение FROM table_reference [, table_reference [, ...]]

Ссылка на таблицу может быть именем таблицы (возможно, дополненным именем схемы) или производной таблицей, такой как подзапрос, JOIN конструкцию или их сложные сочетания. Если в предложении FROM предложение, выполняется соединение CROSS JOIN (то есть формируется декартово произведение их строк; см. ниже). Результат FROM представляет собой промежуточную виртуальную таблицу, которая затем может быть подвергнута преобразованиям с помощью предложений WHERE, GROUP BY, и HAVING и в итоге становится результатом всего табличного выражения.

Когда ссылка на таблицу указывает на таблицу, являющуюся родительской в иерархии наследования, такая ссылка возвращает строки не только этой таблицы, но и всех её таблиц-потомков, если только ключевое слово ONLY не предшествует имени таблицы. Тем не менее, ссылка извлекает только те столбцы, которые присутствуют в указанной таблице — любые столбцы, добавленные в подтаблицах, игнорируются.

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

2.4.2.1.1. Соединенные таблицы #

Соединенная таблица — это таблица, сформированная на основе двух других (реальных или производных) таблиц в соответствии с правилами конкретного типа соединения. Доступны внутренние, внешние и перекрестные соединения. Общий синтаксис соединенной таблицы:

T1 join_type T2 [ join_condition ]

Соединения всех типов могут объединяться в цепочки или быть вложенными: T1 и T2 могут быть соединенными таблицами. Круглые скобки могут использоваться в JOIN предложениях для управления порядком соединений. При отсутствии скобок JOIN предложения вкладываются слева направо.

Типы соединений

Соединение CROSS JOIN
T1 CROSS JOIN T2

Для каждой возможной комбинации строк из T1 и T2 (т. е. декартова произведения), соединенная таблица будет содержать строку, состоящую из всех столбцов T1 , за которыми следуют все столбцы из T2. Если в таблицах имеется N и M строк соответственно, то соединенная таблица будет содержать N * M строк.

FROM T1 CROSS JOIN T2 эквивалентно FROM T1 INNER JOIN T2 ON TRUE (см. ниже). Это также эквивалентно FROM T1, T2.

Примечание

Последняя эквивалентность не совсем точна, когда задействовано более двух таблицы присутствуют, так как JOIN связывается сильнее, чем запятая. Например FROM T1 CROSS JOIN T2 INNER JOIN T3 ON условие не то же самое, что и FROM T1, T2 INNER JOIN T3 ON условие так как условие может ссылаться T1 в первом случае, но не во втором.

Квалифицированные соединения
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 предложение ON boolean_expression
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( список столбцов соединения )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2

Слова INNER и OUTER являются необязательными во всех формах. INNER используется по умолчанию; LEFT, RIGHT, и FULL подразумевают внешнее соединение.

условие соединения указывается в ON или USING предложение, либо неявно с помощью слово NATURAL. Условие соединения определяет, какие строки из двух исходных таблиц считаются «соответствующими», как подробно описано ниже.

Возможные типы квалифицированного соединения:

соединение INNER JOIN

Для каждой строки R1 таблицы T1 соединенная таблица содержит строку для каждой строки в T2, удовлетворяющей условию соединения с R1.

LEFT OUTER JOIN

Сначала выполняется внутреннее соединение. Затем для каждой строки в T1, не удовлетворяющей условию соединения ни с одной строкой в T2, добавляется соединенная строка с NULL-значениями в столбцах T2. Таким образом, соединенная таблица всегда содержит как минимум одну строку для каждой строки в таблице T1.

RIGHT OUTER JOIN

Сначала выполняется внутреннее соединение. Затем для каждой строки в T2, которое не удовлетворяет условию соединения ни с одной строкой в T1, добавляется соединенная строка с null-значениями в столбцах T1. Это операция, обратная соединению left join: результирующая таблица всегда будет содержать строку для каждой строки таблицы T2.

FULL OUTER JOIN

Сначала выполняется внутреннее соединение. Затем для каждой строки в T1, не удовлетворяющей условию соединения ни с одной строкой в T2, добавляется соединенная строка с NULL-значениями в столбцах T2. Кроме того, для каждой строки таблицы T2, которая не удовлетворяет условию соединения ни с одной строкой в T1, добавляется соединенная строка с null-значениями в столбцах таблицы T1.

ON предложение является наиболее универсальным видом условия соединения: оно принимает логическое выражение такого же типа, какой используется в WHERE. Пара строк из T1 и T2 соответствуют друг другу, если ON выражение принимает значение true.

USING предложение является сокращенной формой записи, позволяющей воспользоваться преимуществами ситуации, когда в обеих частях соединения используются одинаковые имена для соединяемых столбцов. Оно принимает разделенный запятыми список имен общих столбцов и формирует условие соединения, включающее сравнение на равенство для каждого из них. Например, соединение T1 и T2 с USING (a, b) формирует условие соединения ON T1.a = T2.a AND T1.b = T2.b.

Кроме того, результат команды JOIN USING исключает избыточные столбцы: нет необходимости выводить оба совпадающих столбца, так как они должны иметь одинаковые значения. В то время как JOIN предложение ON возвращает все столбцы из T1 за которыми следуют все столбцы из T2, JOIN USING формирует один выходной столбец для каждой из перечисленных пар столбцов (в указанном порядке), за которыми следуют любые оставшиеся столбцы из T1, за которыми следуют любые оставшиеся столбцы из T2.

Наконец, NATURAL представляет собой сокращенную форму USING: оно формирует USING список состоящий из имен всех столбцов, присутствующих в обеих входных таблицах. Как и в случае с USING, эти столбцы выводятся в выходной таблице только один раз. Если общих имен столбцов нет, NATURAL JOIN ведет себя аналогично соединение CROSS JOIN.

Примечание

USING является достаточно безопасным при изменении состава столбцов в соединяемых отношениях, так как объединяются только перечисленные столбцы объединяются. NATURAL является значительно более рискованным, поскольку любые изменения схемы в любом из отношений, приводящие к появлению нового совпадающего имени столбца, приведут к тому, что соединение задействует и этот новый столбец тоже.

Чтобы объединить всё это, предположим, что у нас есть таблицы t1:

 число | имя
-----+------
   1 | a
   2 | b
   3 | c

и t2:

 число | значение
-----+-------
   1 | xxx
   3 | yyy
   5 | zzz

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

=> SELECT * FROM t1 CROSS JOIN t2;
 число | имя | число | значение
-----+------+-----+-------
   1 | a    |   1 | xxx
   1 | a    |   3 | yyy
   1 | a    |   5 | zzz
   2 | b    |   1 | xxx
   2 | b    |   3 | yyy
   2 | b    |   5 | zzz
   3 | c    |   1 | xxx
   3 | c    |   3 | yyy
   3 | c    |   5 | zzz
(9 строк)

=> SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;
 число | имя | число | значение
-----+------+-----+-------
   1 | a    |   1 | xxx
   3 | c    |   3 | yyy
(2 строки)

=> SELECT * FROM t1 INNER JOIN t2 USING (num);
 num | name | value
-----+------+-------
   1 | a    | xxx
   3 | c    | yyy
(2 строки)

=> SELECT * FROM t1 NATURAL INNER JOIN t2;
 num | name | value
-----+------+-------
   1 | a    | xxx
   3 | c    | yyy
(2 строки)

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;
 число | имя | число | значение
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |   3 | yyy
(3 строки)

=> SELECT * FROM t1 LEFT JOIN t2 USING (num);
 num | name | value
-----+------+-------
   1 | a    | xxx
   2 | b    |
   3 | c    | yyy
(3 строки)

=> SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;
 число | имя | число | значение
-----+------+-----+-------
   1 | a    |   1 | xxx
   3 | c    |   3 | yyy
     |      |   5 | zzz
(3 строки)

=> SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;
 число | имя | число | значение
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |   3 | yyy
     |      |   5 | zzz
(4 строки)

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

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx';
 число | имя | число | значение
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |     |
(3 строки)

Обратите внимание, что размещение условия в WHERE предложении приводит к иному результату:

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';
 число | имя | число | значение
-----+------+-----+-------
   1 | a    |   1 | xxx
(1 строка)

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

2.4.2.1.2. Псевдонимы таблиц и столбцов #

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

Чтобы создать псевдоним таблицы, укажите

предложение FROM table_reference AS псевдоним

или

предложение FROM table_reference псевдоним

Ключевое AS слово является необязательным синтаксическим шумом. псевдоним может быть любым идентификатором.

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

SELECT * FROM some_very_long_table_name s JOIN another_fairly_long_name a ON s.id = a.num;

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

SELECT * FROM my_table AS m WHERE my_table.a > 5;    -- неправильно

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

SELECT * FROM people AS mother JOIN people AS child ON mother.id = child.mother_id;

Для устранения неоднозначности используются скобки. В следующем примере первая инструкция присваивает псевдоним b второму экземпляру my_table, а вторая инструкция присваивает псевдоним результату соединения:

SELECT * FROM my_table AS a CROSS JOIN my_table AS b ...
SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b ...

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

предложение FROM table_reference [AS] псевдоним ( column1 [, column2 [, ...]] )

Если указано меньше псевдонимов столбцов, чем фактически имеется в таблице, остальные столбцы не переименовываются. Данный синтаксис особенно полезен для самосоединений или подзапросов.

Когда псевдоним применяется к результату предложения JOIN предложение, этот псевдоним скрывает исходные имена внутри JOIN. Например:

SELECT a.* FROM my_table AS a JOIN your_table AS b ON ...

является корректным SQL-запросом, но инструкция:

SELECT a.* FROM (my_table AS a JOIN your_table AS b ON ...) AS c

недопустима; псевдоним таблицы a не виден вне области действия псевдонима c.

2.4.2.1.3. Подзапросы #

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

FROM (SELECT * FROM table1) AS alias_name

Этот пример эквивалентен записи FROM table1 AS alias_name. Более интересные случаи, которые нельзя свести к обычному соединению, возникают, когда подзапрос использует группировку или агрегатные функции.

Подзапрос также может представлять собой список VALUES:

FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow'))
     AS names(first, last)

Использование псевдонима таблицы снова является необязательным. Присвоение имен псевдонимов столбцам списка список необязательно, но считается хорошей практикой. Для получения дополнительной информации см. Раздел 2.4.7.

Согласно стандарту SQL, для подзапроса обязательно должно быть указано имя псевдонима таблицы. Digital Q.DataBase позволяет AS опускать имя и псевдоним, однако их указание является хорошей практикой для SQL-кода, который может быть перенесен в другую систему.

2.4.2.1.4. Табличные функции #

Табличные функции — это функции, которые возвращают набор строк, состоящий либо из базовых типов данных (скалярных типов), либо из составных типов данных (строк таблицы). Они используются аналогично таблице, представлению или подзапросу в предложении FROM запроса. Столбцы, возвращаемые табличными функциями, могут быть включены в SELECT, JOIN, или WHERE предложения так же, как столбцы таблицы, представления или подзапроса.

Табличные функции также могут быть объединены с использованием функция ROWS FROM синтаксиса, при этом результаты возвращаются в параллельных столбцах; количество результирующих строк в этом случае определяется наибольшим количеством строк из результатов функций, а результаты меньшего размера дополняются значениями NULL.

function_call [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]
ROWS FROM( function_call [, ... ] ) [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]

Если WITH ORDINALITY предложении указано, дополнительный столбец типа bigint will be added to the function result columns. В этом столбце строки результирующего набора функции нумеруются по порядку, начиная с 1. (Это расширение синтаксиса стандарта SQL для команды UNNEST ... WITH ORDINALITY.) По умолчанию столбец порядковых номеров называется ordinality, но ему можно присвоить другое имя, используя предложение AS предложение.

Специальная табличная функция UNNEST может быть вызвана с произвольным количеством параметров-массивов и возвращает соответствующее количество столбцов, как если бы функция UNNEST (Раздел 2.6.19) была вызвана для каждого параметра в отдельности и результаты были объединены с помощью функция ROWS FROM конструкции.

UNNEST( array_expression [, ... ] ) [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]

Если имя не table_alias указано, имя функции используется в качестве имени таблицы; в случае использования ROWS FROM() конструкции, используется имя первой функции.

Если псевдонимы столбцов не указаны, то для функции, возвращающей базовый тип данных, имя столбца также совпадает с именем функции. Для функции, возвращающей составной тип, результирующие столбцы получают имена отдельных атрибутов этого типа.

Примеры:

CREATE TABLE foo (fooid int, foosubid int, fooname text);

CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$
    SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT * FROM getfoo(1) AS t1;

SELECT * FROM foo
    WHERE foosubid IN (
                        SELECT foosubid
                        FROM getfoo(foo.fooid) z
                        WHERE z.fooid = foo.fooid
                      );

CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);

SELECT * FROM vw_getfoo;

В некоторых случаях полезно определить табличные функции, которые могут возвращать различные наборы столбцов в зависимости от способа их вызова. Для этого табличную функцию можно объявить как возвращающую псевдотип record без OUT параметров. Когда такая функция используется в запросе, ожидаемая структура строки должна быть указана в самом запросе, чтобы система могла разобрать и спланировать этот запрос. Этот синтаксис выглядит следующим образом:

function_call [AS] псевдоним (column_definition [, ... ])
function_call AS [псевдоним] (column_definition [, ... ])
ROWS FROM( ... function_call AS (column_definition [, ... ]) [, ... ] )

Если не используется синтаксис ROWS FROM() , список column_definition список заменяет список псевдонимов столбцов, который в противном случае мог быть присоединен к FROM предложения FROM; имена в определениях столбцов служат псевдонимами столбцов. При использовании ROWS FROM() синтаксиса, а column_definition список может быть присоединен к каждой функции-члену в отдельности; или если имеется только одна функция-член и отсутствует WITH ORDINALITY предложения, а column_definition список может быть указан вместо списка псевдонимов столбцов после ROWS FROM().

Рассмотрим следующий пример:

SELECT *
    FROM dblink('dbname=mydb', 'SELECT proname, prosrc FROM pg_proc')
      AS t1(proname name, prosrc text)
    WHERE proname LIKE 'bytea%';

Ключевое dblink функция (часть dblink модуля) выполняет удаленный запрос. Она объявлена как возвращающая значение типа record, record поскольку она может использоваться для любого вида запроса. Фактический набор столбцов должен быть определен в вызывающем запросе, чтобы анализатор знал, например, во что * должен разворачиваться символ *.

В данном примере используется функция ROWS FROM:

SELECT *
FROM ROWS FROM
    (
        json_to_recordset('[{"a":40,"b":"foo"},{"a":"100","b":"bar"}]')
            AS (a INTEGER, b TEXT),
        generate_series(1, 3)
    ) AS x (p, q, s)
ORDER BY p;

  p  |  q  | s
-----+-----+---
  40 | foo | 1
 100 | bar | 2
     |     | 3

Это объединяет две функции в одну FROM цель. json_to_recordset() указывается возвращать два столбца: первый имеет тип integer а второй — text. Результат функции generate_series() используется напрямую. Предложение ORDER BY сортирует значения столбца как целочисленные.

2.4.2.1.5. Подзапросы LATERAL #

Подзапросы, встречающиеся в FROM могут предваряться ключевым словом LATERAL. Это позволяет им ссылаться на столбцы, предоставленные предшествующими FROM элементами. (Без LATERAL, каждый подзапрос вычисляется независимо и, следовательно, не может содержать перекрёстных ссылок на любые другие FROM элементы.)

Табличные функции, встречающиеся в FROM , также могут предваряться ключевым словом LATERAL, но для функций это ключевое слово является необязательным; аргументы функции в любом случае могут содержать ссылки на столбцы, предоставленные предшествующими FROM элементами.

Элемент LATERAL может находиться на верхнем уровне в FROM списке или внутри JOIN дерева. В последнем случае он также может ссылаться на любые элементы, которые находятся в левой части JOIN , по отношению к которому он находится в правой части.

Когда элемент FROM содержит LATERAL перекрёстные ссылки, вычисление выполняется следующим образом: для каждой строки FROM элемент, предоставляющий столбцы для перекрестных ссылок, или набор строк из нескольких FROM элементы, предоставляющие столбцы, команда LATERAL элемент вычисляется с использованием значений столбцов этой строки или набора строк. Результирующие строки объединяются обычным образом со строками, на основе которых они были вычислены. Данная операция повторяется для каждой строки или набора строк из исходных таблиц.

Простой пример использования LATERAL — это

SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;

Это не дает особых преимуществ, так как результат идентичен результату более традиционной команды

SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;

LATERAL в первую очередь полезно в тех случаях, когда столбец с перекрестной ссылкой необходим для вычисления строк, подлежащих объединению. Типичным применением является передача значения аргумента функции, возвращающей набор строк. Например, если предположить, что vertices(polygon) возвращает набор вершин многоугольника, мы можем найти близко расположенные вершины многоугольников, хранящихся в таблице, с помощью следующей команды:

SELECT p1.id, p2.id, v1, v2
FROM polygons p1, polygons p2,
     LATERAL vertices(p1.poly) v1,
     LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

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

SELECT p1.id, p2.id, v1, v2
FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1,
     polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

или в нескольких других эквивалентных формулировках. (Как уже упоминалось, ключевое слово LATERAL в данном примере не является обязательным, но мы используем его для ясности.)

Часто бывает особенно удобно применить соединение LEFT JOIN к LATERAL, чтобы в результате появлялись исходные строки, даже если LATERAL подзапрос не возвращает для них ни одной строки. Например, если функция get_product_names() возвращает имена продуктов, выпущенных конкретным производителем, но так как некоторые производители в нашей таблице в настоящее время не выпускают никакой продукции, выяснить, о каких именно производителях идет речь, можно следующим образом:

SELECT m.name
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true
WHERE pname IS NULL;

2.4.2.2. Предложение WHERE #

Синтаксис WHERE предложения следующий:

WHERE search_condition

где search_condition представляет собой любое выражение значения (см. Раздел 2.1.2), которое возвращает значение типа boolean.

После того как обработка FROM предложения WHERE завершена, каждая строка производной виртуальной таблицы проверяется на соответствие условию поиска. Если результат условия — истина (true), строка сохраняется в выходной таблице, в противном случае (то есть если результат — ложь (false) или null) она отбрасывается. Условие поиска обычно ссылается как минимум на один столбец таблицы, сформированной в FROM предложении; это не является обязательным требованием, но без него данное WHERE предложение будет практически бесполезным.

Примечание

Условие соединения для внутреннего соединения может быть указано либо в WHERE предложении, либо в JOIN предложении. Например, эти табличные выражения эквивалентны:

FROM a, b WHERE a.id = b.id AND b.val > 5

и:

FROM a INNER JOIN b ON (a.id = b.id) WHERE b.val > 5

или даже:

FROM a NATURAL JOIN b WHERE b.val > 5

Выбор одного из этих вариантов является в основном вопросом стиля. Использование JOIN синтаксиса в FROM предложении, вероятно, не так удобно для переноса в другие системы управления базами данных SQL, хотя оно и описано в стандарте SQL. Для внешних соединений выбора нет: они должны выполняться в FROM предложение. Оно ON или USING предложение внешнего соединения не эквивалентно WHERE условию, так как оно приводит к добавлению строк (для несовпадающих входных строк), а также к удалению строк в итоговом результате.

Вот несколько примеров WHERE предложений:

SELECT ... из предложения FROM fdt WHERE c1 > 5

команда SELECT ... из предложения FROM fdt WHERE c1 IN (1, 2, 3)

команда SELECT ... из предложения FROM fdt WHERE c1 IN (команда SELECT c1 FROM t2)

команда SELECT ... из предложения FROM fdt WHERE c1 IN (команда SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10)

команда SELECT ... из предложения FROM fdt WHERE c1 BETWEEN (команда SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10) AND 100

команда SELECT ... из предложения FROM fdt WHERE EXISTS (команда SELECT c1 FROM t2 WHERE c2 > fdt.c1)

fdt — это таблица, сформированная в FROM предложении. Строки, не соответствующие условию поиска предложения WHERE исключаются из fdt. Обратите внимание на использование скалярных подзапросов в качестве выражений значений. Как и любой другой запрос, подзапросы могут использовать сложные табличные выражения. Также обратите внимание на то, как fdt используется в качестве ссылки в подзапросах. Уточнение имени c1 как fdt.c1 необходимо только в том случае, если c1 также является именем столбца в производной входной таблице подзапроса. Однако уточнение имени столбца добавляет ясности, даже когда оно не является обязательным. Этот пример показывает, как область видимости имен столбцов внешнего запроса распространяется на его внутренние запросы.

2.4.2.3. Предложения GROUP BY и HAVING #

После применения фильтра WHERE производная входная таблица может быть подвергнута группировке с использованием предложения GROUP BY предложения и исключения строк группы с помощью HAVING предложения.

команда SELECT select_list
    FROM ...
    [WHERE ...]
    предложение GROUP BY grouping_column_reference [, grouping_column_reference]...

GROUP BY предложение используется для группировки тех строк в таблице, которые имеют одинаковые значения во всех перечисленных столбцах. Порядок, в котором перечислены столбцы, не имеет значения. Результат заключается в объединении каждого набора строк, имеющих общие значения, в одну строку группы, которая представляет все строки в группе. Это делается для устранения избыточности в выходных данных и/или вычисления агрегатов, применяемых к этим группам. Например:

=> SELECT * FROM test1;
 x | y
---+---
 a | 3
 c | 2
 b | 5
 a | 1
(4 rows)

=> SELECT x FROM test1 GROUP BY x;
 x
---
 a
 b
 c
(3 rows)

Во втором запросе мы не могли бы написать SELECT * FROM test1 GROUP BY x, так как не существует единого значения для столбца y которое могло бы быть сопоставлено с каждой группой. На столбцы, по которым производится группировка, можно ссылаться в списке выбора, так как они имеют единое значение в каждой группе.

В общем случае, если таблица сгруппирована, на столбцы, которые не указаны в GROUP BY нельзя ссылаться, за исключением агрегатных выражений. Пример с использованием агрегатных выражений:

=> SELECT x, sum(y) FROM test1 GROUP BY x;
 x | сумма
---+-----
 a |   4
 b |   5
 c |   2
(3 строки)

Здесь сумма является агрегатной функцией, которая вычисляет единое значение для всей группы. Дополнительную информацию о доступных агрегатных функциях можно найти в Раздел 2.6.21.

Подсказка

Группировка без агрегатных выражений фактически вычисляет набор уникальных значений в столбце. Это также может быть реализовано с помощью DISTINCT предложение (см. Раздел 2.4.3.3).

Вот еще один пример: вычисляется общая сумма продаж для каждого продукта (а не общая сумма продаж всех продуктов):

SELECT product_id, p.name, (sum(s.units) * p.price) AS sales
    FROM products p LEFT JOIN sales s USING (product_id)
    GROUP BY product_id, p.name, p.price;

В данном примере столбцы product_id, p.name, и p.price должны присутствовать в GROUP BY предложении, так как на них есть ссылки в списке выбора команды SELECT (но см. ниже). Столбец s.units не обязательно должен быть в GROUP BY , так как он используется только в агрегатном выражении (sum(...)), которое представляет продажи продукта. Для каждого продукта запрос возвращает итоговую строку о всех продажах этого продукта.

Если таблица продуктов настроена так, что, скажем, product_id является первичным ключом, то было бы достаточно выполнить группировку по product_id в приведенном выше примере, поскольку имя и цена будут функционально зависимы от идентификатора продукта, и поэтому не возникнет двусмысленности относительно того, какое имя и значение цены возвращать для каждой группы идентификаторов продуктов.

В строгом соответствии со стандартом SQL, GROUP BY можно выполнять группировку только по столбцам исходной таблицы, однако Digital Q.DataBase расширяет эту возможность, позволяя также GROUP BY выполнять группировку по столбцам, указанным в списке выбора. Также допускается группировка по выражениям значений вместо простых имен столбцов.

Если таблица была сгруппирована с использованием GROUP BY, но интерес представляют только определенные группы, может быть использовано HAVING предложение, во многом похожее на WHERE предложение, для исключения групп из результата. Синтаксис следующий:

команда SELECT select_list FROM ... [WHERE ...] GROUP BY ... HAVING boolean_expression

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

Пример:

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;
 x | сумма
---+-----
 a |   4
 b |   5
(2 строки)

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';
 x | сумма
---+-----
 a |   4
 b |   5
(2 строки)

Еще один, более реалистичный пример:

SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit
    FROM products p LEFT JOIN sales s USING (product_id)
    WHERE s.date > CURRENT_DATE - INTERVAL '4 weeks'
    GROUP BY product_id, p.name, p.price, p.cost
    HAVING sum(p.price * s.units) > 5000;

В приведенном выше примере WHERE предложение выбирает строки по столбцу, который не участвует в группировке (выражение истинно только для продаж за последние четыре недели), в то время как HAVING предложение ограничивает выходные данные только теми группами, в которых суммарный объем продаж превышает 5000. Обратите внимание, что агрегатные выражения не обязательно должны совпадать во всех частях запроса.

Если запрос содержит вызовы агрегатных функций, но в нем отсутствует GROUP BY предложение, группировка всё равно выполняется: результатом будет одна строка группы (или, возможно, ни одной строки, если эта единственная строка затем отсеивается предложением HAVING). То же самое верно, если запрос содержит HAVING предложение, даже без каких-либо вызовов агрегатных функций или GROUP BY предложения.

2.4.2.4. GROUPING SETS, CUBE, и ROLLUP #

Возможны более сложные операции группировки, чем описанные выше, с использованием концепции наборов группирования. Данные, выбранные с помощью FROM и WHERE предложений группируются отдельно для каждого указанного набора группировки, а агрегаты вычисляются для каждой группы так же, как и для простых GROUP BY предложений, а затем возвращаются результаты. Например:

=> SELECT * FROM items_sold;
 brand | size | sales
-------+------+-------
 Foo   | L    |  10
 Foo   | M    |  20
 Bar   | M    |  15
 Bar   | L    |  5
(4 строки)

=> SELECT brand, size, sum(sales) FROM items_sold GROUP BY GROUPING SETS ((brand), (size), ());
 brand | size | сумма
-------+------+-----
 Foo   |      |  30
 Bar   |      |  20
       | L    |  15
       | M    |  35
       |      |  50
(5 строк)

Каждый подсписок в GROUPING SETS может содержать ноль или более столбцов или выражений и интерпретируется так же, как если бы он был указан непосредственно в предложении GROUP BY предложении. Пустой набор группировки означает, что все строки агрегируются в одну общую группу (которая выводится, даже если входные строки отсутствуют), как описано выше для агрегатных функций без предложения GROUP BY предложения.

Ссылки на столбцы или выражения группировки заменяются значениями NULL в результирующих строках для тех наборов группирования, в которых эти столбцы отсутствуют. Чтобы определить, результатом какой именно группировки стала конкретная выходная строка, см. Таблица 2.6.64.

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

ROLLUP ( e1, e2, e3, ... )

представляет заданный список выражений и все префиксы этого списка, включая пустой список; таким образом, оно эквивалентно

GROUPING SETS (
    ( e1, e2, e3, ... ),
    ...
    ( e1, e2 ),
    ( e1 ),
    ( )
)

Это обычно используется для анализа иерархических данных; например, суммарная зарплата по отделам, подразделениям и общий итог по всей компании.

Предложение вида

CUBE ( e1, e2, ... )

представляет заданный список и все его возможные подмножества (т. е. булеан). Таким образом,

CUBE ( a, b, c )

эквивалентно

GROUPING SETS (
    ( a, b, c ),
    ( a, b    ),
    ( a,    c ),
    ( a       ),
    (    b, c ),
    (    b    ),
    (       c ),
    (         )
)

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

CUBE ( (a, b), (c, d) )

эквивалентно

GROUPING SETS (
    ( a, b, c, d ),
    ( a, b       ),
    (       c, d ),
    (            )
)

и

ROLLUP ( a, (b, c), d )

эквивалентно

GROUPING SETS (
    ( a, b, c, d ),
    ( a, b, c    ),
    ( a          ),
    (            )
)

CUBE и ROLLUP конструкции могут использоваться либо непосредственно в GROUP BY предложение или будучи вложенным в другое GROUPING SETS предложение. Если одно GROUPING SETS предложение вложено в другое, результат будет таким же, как если бы все элементы внутреннего предложения были указаны непосредственно во внешнем предложении.

Если несколько элементов группировки указаны в одном GROUP BY предложении, то итоговый список наборов группировки представляет собой декартово произведение отдельных элементов. Например:

GROUP BY a, CUBE (b, c), GROUPING SETS ((d), (e))

эквивалентно

GROUP BY GROUPING SETS (
    (a, b, c, d), (a, b, c, e),
    (a, b, d),    (a, b, e),
    (a, c, d),    (a, c, e),
    (a, d),       (a, e)
)

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

GROUP BY ROLLUP (a, b), ROLLUP (a, c)

эквивалентно

GROUP BY GROUPING SETS (
    (a, b, c),
    (a, b),
    (a, b),
    (a, c),
    (a),
    (a),
    (a, c),
    (a),
    ()
)

Если наличие дубликатов нежелательно, их можно удалить с помощью DISTINCT предложения непосредственно в GROUP BY. Следовательно:

предложение GROUP BY DISTINCT ROLLUP (a, b), ROLLUP (a, c)

эквивалентно

GROUP BY GROUPING SETS (
    (a, b, c),
    (a, b),
    (a, c),
    (a),
    ()
)

Это не то же самое, что использование SELECT DISTINCT поскольку результирующие строки всё ещё могут содержать дубликаты. Если какой-либо из негруппированных столбцов содержит значение NULL, его будет невозможно отличить от значения NULL, возникающего при группировке по этому же столбцу.

Примечание

Конструкция (a, b) обычно распознаётся в выражениях как конструктор строк. В GROUP BY предложении это не относится к верхним уровням выражений, и (a, b) разбирается как список выражений, как описано выше. Если по какой-то причине вам требуется конструктор строк в выражении группировки, используйте ROW(a, b).

2.4.2.5. Обработка оконных функций #

Если запрос содержит какие-либо оконные функции (см. Раздел 1.3.5, Раздел 2.6.22 и Раздел 2.1.2.8), эти функции вычисляются после выполнения любой группировки, агрегации и HAVING выполняется фильтрация. То есть, если запрос использует какие-либо агрегаты, GROUP BY, или HAVING, то строки, обрабатываемые оконными функциями, представляют собой строки группы, а не исходные строки таблицы из предложения FROM FROM/WHERE.

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

В настоящее время для работы оконных функций всегда требуются предварительно отсортированные данные, поэтому результат запроса будет упорядочен в соответствии с одним из PARTITION BY/ORDER BY предложений. Однако полагаться на это не рекомендуется. Используйте явное предложение верхнего уровня ORDER BY если необходимо гарантировать сортировку результатов определенным образом.

Наверх
свяжитесь
с нами
контакты
Для прямой связи с нами вы можете использовать контакты ниже, либо оставить заявку через форму обратной связи, и мы обязательно свяжемся с вами

*поля обязательные к заполнению