PostgreSQL: сгенерированные столбцы

16

Поддерживает ли PostgreSQL сгенерированные столбцы ? Также известен как виртуальные столбцы . Я не говорю о IDENTITYстолбцах .

Я не могу найти никакой информации об этой замечательной функции, но знаю, что она доступна на SQL Server и в последних версиях MariaDB и MySQL.

Эта функция упоминается в стандарте SQL: 2003 , и в 2006 году на форумах по PostgreSQL было несколько обсуждений, но я не могу найти ничего существенного по этому вопросу.

Существует некоторое обсуждение SO, но сейчас оно довольно старое, поэтому вполне может устареть.

Manngo
источник
2
Этот связанный ответ от 2012 года по SO может быть полезным: stackoverflow.com/questions/11165450/… Все еще в силе.
Эрвин Брандштеттер
@ErwinBrandstetter Извините, я пропустил этот комментарий. Это полезный трюк. Благодарю.
Manngo

Ответы:

17

Не уверен, что это то, что вам нужно, но обозначения атрибутов и обозначения row.full_nameфункций full_name(row)в postgresql эквивалентны.

Это означает, что вы берете за стол

CREATE TABLE people (
  first_name text,
  last_name text
);

и функция:

CREATE FUNCTION full_name(people) RETURNS text AS $$
  SELECT $1.first_name || ' ' || $1.last_name;
$$ LANGUAGE SQL;

и назовите это так:

select full_name from people

Это то, что вам нужно?

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

CREATE INDEX people_full_name_idx ON people
USING GIN (to_tsvector('english', full_name(people)));

Или храните все в материализованном виде.

Пример взят здесь: http://bernardoamc.github.io/sql/2015/05/11/postgres-virtual-columns/

Фабиан Цейндль
источник
2
Это правильный ответ. Посмотрите, например, как Postgrest называет это поведение «вычисляемыми столбцами».
Fiatjaf
Опечатка, я думаю - выбор должен быть select people.full_name from peopleили select full_name(people) from people?
Баргаст
Нет, это так работает. Префикс в "select people.full_name from people" можно оставить как в обычном SQL.
Фабиан Цейндль
Я пропустил этот ответ, придя, как бы то ни было, задолго до того, как сдался. Спасибо за предложение.
Manngo
1
Не могли бы вы изменить принятый ответ?
Фабиан Цейндль
6

Нет, это в настоящее время (с Postgres 9.6) не поддерживается.

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

a_horse_with_no_name
источник
Крысы. Я полагаю, я мог бы пойти на материализованное представление, если мне нужно представление. Я добавил запрос на функцию, так как она уже доступна в конкурсе.
Манго
1
Нет необходимости в MVIEW. Столбец с триггером также позволит вам индексировать содержимое столбца
a_horse_with_no_name
У меня есть философская проблема с хранением дополнительных реальных столбцов, которые в основном повторяют другие данные. Это нормализует таблицу.
Маннго
5
Ну, вычисляемый столбец - это именно то, что хранит ненормализованные данные. Как генерируется значение вычисляемого столбца, не имеет значения. Я не вижу концептуальной разницы между «реальным» вычисляемым столбцом и столбцом, который генерируется с помощью триггера
a_horse_with_no_name
Другой обходной путь (в некоторых случаях) - индексирование выражения.
ypercubeᵀᴹ
5

Да: GENERATED ALWAYS AS … STORED

Postgres 12 добавляет функциональность для сгенерированных столбцов, как указано в стандарте SQL: 2003 .

Значение генерируется во время INSERTили или UPDATEзатем сохраняется со строкой, как и любое другое значение.

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

Синтаксис прост, предложение на CREATE TABLE:

GENERATED ALWAYS AS ( generation_expr ) STORED 

Пример:

CREATE TABLE people (
    ...,
    height_cm NUMERIC,
    height_in NUMERIC GENERATED ALWAYS AS ( height_cm / 2.54 ) STORED
);

Функции:

  • Может быть проиндексирован.
  • Часть стандарта SQL.

Предостережения:

  • На основе столбцов одной таблицы (не связанных таблиц)
  • Не допускается для разделения (не может быть частью ключа раздела)
  • Данные всегда записываются в строку, занимая место в хранилище
    • Будущая функция может предложить VIRTUAL для значений, рассчитанных на лету без хранения
  • Глубина одного поколения (используйте базовый столбец, а не другой сгенерированный столбец)
  • GENERATED BY DEFAULT (вы не можете переопределить значение)
  • Невозможно получить доступ к gen-col в триггере ДО (значение еще не определено)
  • Функции должны быть неизменными

Видеть:

Базилик Бурк
источник
Спасибо за эту информацию. Я вижу, что версия 12 еще не полностью выпущена, но я с нетерпением жду этого. Я отмечаю, что PostgreSQL использует более стандартный синтаксис, но в остальном он такой же, как MSSQL. Я нашел спецификации SQL2003 здесь: sigmodrecord.org/publications/sigmodRecord/0403/… . Я всегда говорил, что SQL - очень медленный стандарт, а реализации СУБД даже медленные.
Манго
0

В зависимости от вашего варианта использования, вы можете достичь такого поведения, объявив новый столбец и заполнив его триггером при вставке / обновлении.

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

Я рассматривал этот подход для решения проблемы, когда у меня иногда было только 15 цифр 18-значного ключа (последние 3 цифры - просто контрольная сумма), но я хотел иметь возможность принудительно установить связь с внешним ключом.

PG документы по триггерам: https://www.postgresql.org/docs/9.6/sql-createtrigger.html

Пример W3: https://www.w3resource.com/PostgreSQL/postgresql-triggers.php

Аллен Фелпс
источник