Являются ли «обновляемые представления» правильным подходом для ручного редактирования нескольких таблиц

У меня есть база данных PostgreSQL со следующими таблицами

person
city
country

Каждый человек связан с городом через внешний ключ city_id, и каждый город связан со страной через country_id аналогичным образом.

Чтобы легко просматривать названия городов и стран каждого человека, я создаю представление:

CREATE VIEW person_view AS
  SELECT
    person.id, 
    person.name,
    city.name as city,
    country.name as country
  FROM person
  LEFT JOIN city ON person.city_id = city.id
  LEFT JOIN country ON city.country_id  = country.id

Что дает что-то легкое для чтения.

| id | name   | city     | country       |
------------------------------------------
| 1  | Steve  | New York | United States |
| 2  | Rachel | Paris    | France        |

Теперь, используя такую ​​программу, как dbeaver, я надеялся управлять этими записями с помощью этого представления. Вместо того, чтобы искать идентификаторы всякий раз, когда необходимо изменить город/страну человека, было бы намного проще просто ввести изменения в представлении и перенести эти изменения в исходные таблицы.

Я думал, что именно для этого предназначено обновляемое представление, но dbeaver не позволяет напрямую обновлять это представление и предлагает реализовать INSTEAD OF UPDATE триггеры или ON UPDATE DO INSTEAD правила.

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

Именно Postgres, а не dbeaver, препятствует обновлению этого представления. Это не обновляемый вид. Обновляемые представления не могут иметь присоединение и другие ограничения. (см. Обновляемые виды). Так что вы не подходите к этому правильно.

Belayer 25.11.2022 19:25

Предложение DBeaver правильное. Представление, использующее соединения, можно сделать обновляемым только в том случае, если вы реализуете триггер INSTEAD OF.

a_horse_with_no_name 25.11.2022 19:50

@Belayer спасибо, у вас есть предложения, как к этому можно подойти?

Andrew Plowright 26.11.2022 01:11

Вы реализуете триггер Instead of. Триггер определяет, какие столбцы и какие таблицы должны быть обновлены, и делает это соответствующим образом. Это может включать обновление одного столбца или нескольких столбцов в нескольких таблицах. (По предоставленным данным похоже, что только persons таблица нуждается в обновлении, но возможно, что все 3 нуждаются в обновлении. Только вы можете решить это.)

Belayer 26.11.2022 05:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
82
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

CREATE OR REPLACE FUNCTION person_view_upd_trig_fn ()
    RETURNS TRIGGER
    AS $$
BEGIN
    IF tg_op = 'UPDATE' THEN
        IF NEW.name <> OLD.name THEN
            RAISE NOTICE 'update person name';
            UPDATE
                person
            SET
                name = NEW.name
            WHERE
                id = OLD.id;
            RETURN new;
        END IF;
        IF NEW.city <> OLD.city AND NEW.country = OLD.country THEN
            RAISE NOTICE 'update city name';
            IF (
                SELECT
                    count(DISTINCT country_id)
                FROM
                    city
                WHERE
                    name = OLD.city OR name = NEW.city) = 2 THEN
                RAISE EXCEPTION 'not good';
            END IF;
            IF (
                SELECT
                    city_id
                FROM
                    city
                WHERE
                    name = NEW.city) IS NULL THEN
                RAISE EXCEPTION 'city not in the list';
            END IF;
            UPDATE
                person
            SET
                city_id = (
                    SELECT
                        city_id
                    FROM
                        city
                    WHERE
                        name = NEW.city)
            WHERE
                id = OLD.id;
            RETURN new;
        END IF;
        IF NEW.country <> OLD.country AND NEW.city <> OLD.city THEN
            RAISE NOTICE 'updating person country & city';
            IF NOT EXISTS (
                SELECT
                FROM
                    country
                WHERE
                    name = NEW.country) THEN
            RAISE EXCEPTION 'not good';
        END IF;
        UPDATE
            person
        SET
            city_id = (
                SELECT
                    city_id
                FROM
                    city
                WHERE
                    name = NEW.city)
        WHERE
            id = OLD.id;
        RETURN new;
    END IF;
    RAISE NOTICE 'new.person_view:%', new;
    RAISE NOTICE 'old.person_view:%', old;
    RETURN NULL;
END IF;
END
$$
LANGUAGE plpgsql;

создать триггер:

CREATE TRIGGER person_view_upd_trig
    INSTEAD OF  UPDATE  ON person_view
    FOR EACH ROW EXECUTE PROCEDURE person_view_upd_trig_fn();

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


IF tg_op = 'UPDATE' THEN не так уж и избыточен. Вы можете добавить дополнительный блок управления, например IF tg_op = 'DELETE THEN в эту функцию. Затем одна функция, вы можете управлять 3 действиями/триггерами (удалить, обновить, вставить) вместо 3 функций и 3 триггеров.


https://www.postgresql.org/docs/current/plpgsql-trigger.html

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

и

INSTEAD OF триггеры (которые всегда являются триггерами уровня строки и могут использоваться в представлениях) могут возвращать значение null, чтобы сигнализировать о том, что они не выполнялись. любые обновления, и что остальная часть операции для этой строки должна быть пропущены (т. е. последующие триггеры не срабатывают, и строка не учитываются в статусе затронутых строк для окружающих ВСТАВИТЬ/ОБНОВИТЬ/УДАЛИТЬ). В противном случае должно быть возвращено ненулевое значение, чтобы сигнализировать о том, что триггер выполнил запрошенную операцию. Для INSERT и UPDATE, возвращаемое значение должно быть NEW, что функция триггера может измениться для поддержки INSERT RETURNING и UPDATE RETURNING (это также повлияет на значение строки, передаваемое в любой последующие триггеры или переданы в специальную ссылку псевдонима EXCLUDED в операторе INSERT с предложением ON CONFLICT DO UPDATE). Для DELETE, возвращаемое значение должно быть OLD.

Блестящий. Это именно то, что я искал. Спасибо за демонстрацию скрипки.

Andrew Plowright 02.12.2022 21:51

Можете ли вы дать мне какое-либо представление о том, что эти функции должны возвращать? Я вижу, что он выполняет UPDATE в таблице person, что является ожидаемым поведением. В чем разница между возвратом new и NULL? Что он делает с new?

Andrew Plowright 02.12.2022 21:55

Второй вопрос: является ли избыточным tg_op = 'UPDATE', учитывая, что при создании триггера мы указываем INSTEAD OF UPDATE ON person_view

Andrew Plowright 02.12.2022 22:41

@AndrewPlowright Я обновляю вопрос. Надеюсь, это ответит на ваш вопрос.

jian 03.12.2022 06:11

Другие вопросы по теме