Digital Q.DataBase реализует наследование таблиц, которое может быть полезным инструментом для проектировщиков баз данных. (В SQL:1999 и более поздних версиях определена функция наследования типов, которая во многих отношениях отличается от функциональности, описываемой здесь.)
Рассмотрим пример: предположим, что мы строим модель данных для городов. В каждом штате много городов, но только одна столица. Нам необходимо иметь возможность быстро получать данные о столице любого конкретного штата. Это можно сделать путем создания двух таблиц: одной для столиц штатов и другой для городов, не являющихся столицами. Однако что делать, если необходимо запросить данные о городе независимо от того, является он столицей или нет? Функция
наследования может помочь в решении этой проблемы. Определим
capitals таблицу так, чтобы она наследовала свойства
cities:
CREATE TABLE cities (
name text,
population float,
elevation int -- в футах
);
CREATE TABLE capitals (
state char(2)
) INHERITS (cities);
В данном случае capitals таблица наследует
все столбцы своей родительской таблицы, cities. Столицы штатов также имеют дополнительный столбец state, в котором указывается
их штат.
В Digital Q.DataBase, таблица может наследоваться от нуля или более других таблиц, а запрос может обращаться либо ко всем строкам таблицы, либо ко всем строкам таблицы вместе со всеми её таблицами-потомками. Последнее поведение используется по умолчанию. Например, следующий запрос находит имена всех городов, включая столицы штатов, которые расположены на высоте более 500 футов:
SELECT name, elevation
FROM cities
WHERE elevation > 500;
На основе примеров данных из Digital Q.DataBase руководства (см. Раздел 1.2.1), этот запрос возвращает:
name | elevation -----------+----------- Лас-Вегас | 2174 Марипоса | 1953 Мадисон | 845
С другой стороны, следующий запрос находит все города, не являющиеся столицами штатов и расположенные на высоте более 500 футов:
SELECT name, elevation
FROM ONLY cities
WHERE elevation > 500;
name | elevation
-----------+-----------
Лас-Вегас | 2174
Марипоса | 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 | Лас-Вегас | 2174 139793 | Марипоса | 1953 139798 | Мадисон | 845
(При попытке воспроизвести этот пример вы, скорее всего, получите другие значения OID типа numeric). Выполнив соединение с
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 | Лас-Вегас | 2174 cities | Марипоса | 1953 capitals | Мадисон | 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 всегда выполняет вставку строго в указанную таблицу. В некоторых случаях вставку можно перенаправить с помощью правила (см. Глава 5.4). Однако это не помогает в вышеуказанном случае, так как cities таблица
не содержит данный столбец state, и поэтому команда будет отклонена до того, как правило может быть применено.
Все ограничения CHECK и ограничения NOT NULL в родительской таблице автоматически наследуются её дочерними таблицами, если явно не указано иное с помощью NO INHERIT clauses. Другие типы ограничений
(уникальности, PRIMARY KEY и ограничения внешних ключей) не наследуются.
Таблица может наследоваться от нескольких родительских таблиц; в этом случае она будет содержать объединение столбцов, определенных родительскими таблицами. К ним добавляются любые столбцы, объявленные в определении самой дочерней таблицы. Если одно и то же имя столбца встречается в нескольких родительских таблицах или одновременно в родительской таблице и определении дочерней таблицы, то такие столбцы «объединяются» так, что в дочерней таблице остается только один такой столбец. Для объединения столбцы должны иметь одинаковые типы данных, в противном случае будет выдана ошибка. Наследуемые ограничения CHECK и ограничения NOT NULL объединяются аналогичным образом. Так, например, объединенный столбец будет помечен как NOT NULL, если хотя бы одно из исходных определений этого столбца имеет пометку NOT NULL. Ограничения CHECK объединяются, если они имеют одинаковое имя; при этом объединение завершится ошибкой, если их условия различаются.
Наследование таблиц обычно устанавливается при создании дочерней таблицы с использованием INHERITS предложения
CREATE TABLE
команды. Кроме того, для таблицы, которая уже определена совместимым образом, можно добавить новую родительскую связь, используя INHERIT
вариант ALTER TABLE. Для этого новая дочерняя таблица уже должна содержать столбцы с теми же именами и типами, что и столбцы родительского элемента. Она также должна включать ограничения CHECK с теми же именами и проверочными выражениями, что и у родительской таблицы. Аналогичным образом связь наследования может быть удалена у дочерней таблицы с помощью
NO INHERIT варианта ALTER TABLE. Динамическое добавление и удаление связей наследования подобным образом может быть полезно в случаях, когда механизм наследования используется для секционирования таблиц (см. Раздел 2.2.12).
Удобный способ создания совместимой таблицы, которая в дальнейшем станет дочерней, — использование предложения LIKE в команды
CREATE TABLE. При этом создается новая таблица с теми же столбцами, что и в исходной таблице. Если для исходной таблицы определены какие-либо CHECK
ограничения, то параметр
INCLUDING CONSTRAINTS в LIKE должны быть указаны, так как новая дочерняя таблица должна иметь ограничения, соответствующие родительскому элементу, для обеспечения совместимости.
Родительскую таблицу нельзя удалить, пока существуют какие-либо из её дочерних таблиц. Также нельзя удалять или изменять столбцы или ограничения CHECK дочерних таблиц, если они унаследованы от родительских таблиц. Если требуется удалить таблицу вместе со всеми её потомками, один из простых способов — удалить родительскую таблицу с помощью
CASCADE опция (см. Раздел 2.2.15).
ALTER TABLE распространяет любые изменения в определениях данных столбцов и ограничения CHECK вниз по иерархии наследования. Стоит отметить, что удаление столбцов, от которых зависят другие таблицы, возможно только при использовании CASCADE опции. ALTER
TABLE следует тем же правилам объединения и отклонения дублирующихся столбцов, которые применяются во время CREATE TABLE.
Унаследованные запросы выполняют проверку прав доступа только для родительской таблицы. Так, например, предоставление UPDATE прав доступа к
таблице cities подразумевает разрешение на обновление строк в
таблице capitals в том числе и тогда, когда доступ к ним осуществляется через cities. Это поддерживает видимость
того, что данные (также) находятся в родительской таблице. Однако
дочерняя capitals таблица не может быть обновлена напрямую
без дополнительного предоставления прав. Аналогичным образом политики защиты строк родительской таблицы (см. Раздел 2.2.9) применяются к
строкам, полученным из дочерних таблиц в ходе выполнения унаследованного запроса. Политики дочерней таблицы,
при их наличии, применяются только в том случае, если она явно указана
в запросе; в этой ситуации любые политики, относящиеся к ее родительским элементам,
игнорируются.
Сторонние таблицы (см. Раздел 2.2.13) также могут входить в иерархии наследования в качестве родительских или дочерних таблиц, так же как и обычные таблицы. Если сторонняя таблица является частью иерархии наследования, то любые операции, не поддерживаемые этой сторонней таблицей, будут не поддерживаться и для всей иерархии в целом.
Следует учитывать, что не все команды SQL поддерживают работу с иерархиями наследования. Команды, используемые для выборки,
изменения данных или изменения схемы
(например, SELECT, UPDATE, DELETE,
большинство вариантов ALTER TABLE, но не INSERT или ALTER TABLE ...
RENAME) обычно по умолчанию включают дочерние таблицы и
поддерживают синтаксис ONLY для их исключения.
Команды, предназначенные для обслуживания и настройки базы данных
(например, REINDEX, VACUUM) обычно работают только с отдельными физическими таблицами и не поддерживают рекурсивный обход иерархий наследования. Особенности поведения каждой конкретной команды описаны в соответствующем разделе документации (Команды SQL).
Серьезным ограничением функциональности наследования является то, что индексы (включая ограничения уникальности) и ограничения внешних ключей применяются только к отдельным таблицам, а не к их дочерним элементам наследования. Это справедливо как для ссылающейся, так и для ссылочной стороны ограничения внешнего ключа. Таким образом, в контексте вышеприведенного примера:
Если мы объявим команду cities.имя как
UNIQUE или тип PRIMARY KEY, это не предотвратит появление в таблице
capitals строк с именами, дублирующими
строки в cities. И эти дублирующиеся строки по умолчанию будут отображаться в запросах из cities. Фактически, по
умолчанию capitals не будет иметь ограничения уникальности вообще,
и поэтому может содержать несколько строк с тем же именем.
Вы могли бы добавить ограничение уникальности в capitals, но это
не предотвратит дублирование по сравнению с cities.
Аналогично, если бы мы указали, что тип
cities.имя REFERENCES ссылается на некоторую
другую таблицу, это ограничение не будет автоматически распространяться на
capitals. В этом случае можно было бы обойти это ограничение, вручную добавив такое же REFERENCES ограничение к
capitals.
Указание того, что столбец другой таблицы REFERENCES
cities(name) позволит другой таблице содержать названия городов, но не названия столиц. Для данного случая не существует эффективного обходного решения.
Некоторая функциональность, не реализованная для иерархий наследования, реализована для декларативного секционирования. При принятии решения о целесообразности использования секционирования на основе устаревшего механизма наследования в конкретном приложении требуется особая осторожность.