PostgreSQL реализует наследование таблиц, что может быть полезным инструментом для разработчиков баз данных. (SQL:1999 и более поздние версии определяют функцию наследования типов, которая во многих отношениях отличается от функций, описанных здесь.)
Начнем с примера: предположим, мы пытаемся построить модель данных для городов. В каждом штате много городов, но только одна столица. Мы хотим иметь возможность быстро получить столицу любого конкретного штата. Это можно сделать, создав две таблицы: одну для столиц штатов и одну для городов, не являющихся столицами. Однако что происходит, когда мы хотим запросить данные о городе, независимо от того, является ли он столицей или нет? Функция наследования может помочь решить эту проблему. Мы определяем capitals
таблицу так, чтобы она наследовалась от cities
:
CREATE TABLE cities (
name text,
population float,
elevation int -- in feet
);
CREATE TABLE capitals (
state char(2)
) INHERITS (cities);
В этом случае capitals
таблица наследует все столбцы своей родительской таблицы cities
. В столицах штатов также есть дополнительный столбец, state
который показывает их штат.
В PostgreSQL таблица может наследовать от нуля или более других таблиц, а запрос может ссылаться либо на все строки таблицы, либо на все строки таблицы плюс все ее дочерние таблицы. Последнее поведение является поведением по умолчанию. Например, следующий запрос находит названия всех городов, включая столицы штатов, расположенных на высоте более 500 футов:
SELECT name, elevation
FROM cities
WHERE elevation > 500;
name | elevation
-----------+-----------
Las Vegas | 2174
Mariposa | 1953
Madison | 845
С другой стороны, следующий запрос находит все города, которые не являются столицами штатов и расположены на высоте более 500 футов:
SELECT name, elevation
FROM ONLY cities
WHERE elevation > 500;
name | elevation
-----------+-----------
Las Vegas | 2174
Mariposa | 1953
Здесь ONLY
ключевое слово указывает, что запрос должен применяться только к cities
, а не к каким-либо таблицам ниже cities
в иерархии наследования. Многие из уже рассмотренных нами команд — SELECT
и UPDATE
— DELETE
поддерживают ONLY
ключевое слово.
Вы также можете написать имя таблицы с окончанием, *
чтобы явно указать, что включены таблицы-потомки:
SELECT name, elevation
FROM cities*
WHERE elevation > 500;
Писать *
не нужно, так как это поведение всегда по умолчанию. Однако этот синтаксис по-прежнему поддерживается для совместимости со старыми версиями, в которых значение по умолчанию может быть изменено.
В некоторых случаях вам может понадобиться узнать, из какой таблицы возникла конкретная строка. В каждой таблице есть системный столбец tableoid
, который может указать вам исходную таблицу:
SELECT c.tableoid, c.name, c.elevation
FROM cities c
WHERE c.elevation > 500;
tableoid | name | elevation
----------+-----------+-----------
139793 | Las Vegas | 2174
139793 | Mariposa | 1953
139798 | Madison | 845
(Если вы попытаетесь воспроизвести этот пример, вы, вероятно, получите другие числовые OID.) Выполнив соединение с pg_class
вы можете увидеть фактические имена таблиц:
SELECT p.relname, c.name, c.elevation
FROM cities c, pg_class p
WHERE c.elevation > 500 AND c.tableoid = p.oid;
relname | name | elevation
----------+-----------+-----------
cities | Las Vegas | 2174
cities | Mariposa | 1953
capitals | Madison | 845
Другой способ получить тот же эффект — использовать regclass
псевдоним, который символически печатает OID таблицы:
SELECT c.tableoid::regclass, c.name, c.elevation
FROM cities c
WHERE c.elevation > 500;
Наследование не распространяет автоматически данные INSERT
или COPY
команды на другие таблицы в иерархии наследования. В нашем примере следующий INSERT
оператор не сработает:
INSERT INTO cities (name, population, elevation, state)
VALUES ('Albany', NULL, NULL, 'NY');
Можно было бы надеяться, что данные каким-то образом переправятся в capitals
таблицу, но этого не происходит: INSERT
всегда вставляется именно в указанную таблицу. В некоторых случаях можно перенаправить вставку с помощью правила. Однако это не помогает в приведенном выше случае, поскольку cities
таблица не содержит столбец state
, поэтому команда будет отклонена до того, как правило сможет быть применено.
Все проверочные ограничения и ненулевые ограничения родительской таблицы автоматически наследуются ее дочерними таблицами, если явно не указано иное в NO INHERIT
предложениях. Другие типы ограничений (уникальный, первичный ключ и ограничения внешнего ключа) не наследуются.
Таблица может наследоваться более чем от одной родительской таблицы, и в этом случае она имеет объединение столбцов, определенных родительскими таблицами. К ним добавляются любые столбцы, объявленные в определении дочерней таблицы. Если одно и то же имя столбца появляется в нескольких родительских таблицах или как в родительской таблице, так и в определении дочерней, то эти столбцы « объединяются » .чтобы в дочерней таблице был только один такой столбец. Для объединения столбцы должны иметь одинаковые типы данных, иначе возникает ошибка. Наследуемые проверочные ограничения и ненулевые ограничения объединяются аналогичным образом. Таким образом, например, объединенный столбец будет помечен как ненулевой, если любое из определений столбца, из которого он был получен, помечено как ненулевое. Проверочные ограничения объединяются, если они имеют одинаковое имя, и слияние завершится ошибкой, если их условия различаются.
Наследование таблиц обычно устанавливается при создании дочерней таблицы с использованием INHERITS
условия CREATE TABLE
оператора. В качестве альтернативы к таблице, которая уже определена совместимым образом, можно добавить новую родительскую связь, используя INHERIT
вариант ALTER TABLE
. Для этого новая дочерняя таблица уже должна включать столбцы с теми же именами и типами, что и столбцы родительской таблицы. Он также должен включать проверочные ограничения с теми же именами и проверочными выражениями, что и у родителя. Точно так же ссылка наследования может быть удалена из дочернего элемента с помощью NO INHERIT
варианта ALTER TABLE
. Подобное динамическое добавление и удаление ссылок наследования может быть полезно, когда отношение наследования используется для разделения таблицы.
Одним из удобных способов создания совместимой таблицы, которая позже станет новой дочерней, является использование LIKE
предложения в CREATE TABLE
. Это создает новую таблицу с теми же столбцами, что и исходная таблица. Если в исходной таблице определены какие-либо CHECK
ограничения, следует указать INCLUDING CONSTRAINTS
параметр to LIKE
, так как новый дочерний элемент должен иметь ограничения, соответствующие родительскому, чтобы считаться совместимым.
Родительскую таблицу нельзя удалить, пока остаются ее дочерние таблицы. Столбцы или проверочные ограничения дочерних таблиц также не могут быть удалены или изменены, если они унаследованы от каких-либо родительских таблиц. Если вы хотите удалить таблицу и всех ее потомков, один простой способ — удалить родительскую таблицу с CASCADE
опцией.
ALTER TABLE
будет распространять любые изменения в определениях данных столбцов и проверять ограничения вниз по иерархии наследования. Опять же, удаление столбцов, от которых зависят другие таблицы, возможно только при использовании этой CASCADE
опции. ALTER TABLE
следует тем же правилам для слияния и отклонения повторяющихся столбцов, которые применяются во время CREATE TABLE
.
Унаследованные запросы выполняют проверки прав доступа только к родительской таблице. Таким образом, например, предоставление UPDATE
разрешения на cities
таблицу подразумевает также разрешение на обновление строк в capitals
таблице, когда к ним обращаются через cities
. Это сохраняет видимость того, что данные (также) находятся в родительской таблице. Но capitals
таблицу нельзя было обновить напрямую без дополнительного гранта. Аналогичным образом политики безопасности строк родительской таблицы применяются к строкам, поступающим из дочерних таблиц во время унаследованного запроса. Политики дочерней таблицы, если таковые имеются, применяются только в том случае, если это таблица, явно названная в запросе; и в этом случае любые политики, прикрепленные к его родителю(ям), игнорируются.
Внешние таблицы также могут быть частью иерархии наследования, как родительские или дочерние таблицы, точно так же, как и обычные таблицы. Если внешняя таблица является частью иерархии наследования, то любые операции, не поддерживаемые внешней таблицей, не поддерживаются и во всей иерархии.
Предостережения
Обратите внимание, что не все команды SQL могут работать с иерархиями наследования. Команды, которые используются для запроса данных, модификации данных или модификации схемы (например, SELECT
, UPDATE
, DELETE
, большинство вариантов ALTER TABLE
, но не INSERT
или ALTER TABLE ... RENAME
), обычно по умолчанию включают дочерние таблицы и поддерживают ONLY
нотацию для их исключения. Команды, выполняющие обслуживание и настройку базы данных (например, REINDEX
, VACUUM
), обычно работают только с отдельными физическими таблицами и не поддерживают рекурсию по иерархиям наследования.
Серьезным ограничением функции наследования является то, что индексы (включая ограничения уникальности) и ограничения внешнего ключа применяются только к отдельным таблицам, а не к их дочерним элементам наследования. Это справедливо как для ссылочной, так и для ссылочной стороны ограничения внешнего ключа. Таким образом, с точки зрения приведенного выше примера:
- Если бы мы объявили
cities
.name
бытьUNIQUE
или aPRIMARY KEY
, это не помешаетcapitals
таблице иметь строки с именами, дублирующими строки вcities
. И эти повторяющиеся строки по умолчанию будут отображаться в запросах изcities
. Фактически, по умолчаниюcapitals
вообще не будет уникального ограничения, и поэтому может содержать несколько строк с одним и тем же именем. Вы можете добавить уникальное ограничение кcapitals
, но это не предотвратит дублирование по сравнению сcities
. - Точно так же, если бы мы указали, что
cities
.name
REFERENCES
какой-либо другой таблице, это ограничение не будет автоматически распространяться наcapitals
. В этом случае вы можете обойти это, вручную добавив такое жеREFERENCES
ограничение вcapitals
. - Указание столбца другой таблицы
REFERENCES cities(name)
позволит другой таблице содержать названия городов, но не названия столиц. Для этого случая нет хорошего обходного пути.
Некоторые функции, не реализованные для иерархий наследования, реализованы для декларативного секционирования. При принятии решения о том, полезно ли для вашего приложения секционирование с использованием устаревшего наследования, необходимо проявлять большую осторожность.
0 комментариев