2013-01-06 2 views
0

Так вот моя функция обновления:обновления каскадных таблиц с plpgsql

CREATE OR REPLACE FUNCTION api.book_update(
    in_id  BIGINT, 
    in_category VARCHAR, 
    in_published DATE, 
    in_author VARCHAR, 
    in_name  VARCHAR 
) RETURNS VOID AS $$ 
DECLARE 
in_category_id BIGINT; 
tmp   BIGINT; 
BEGIN 
    SELECT COUNT(*) INTO tmp FROM schemas.book; 
    IF (NOT (tmp <> 0)) THEN 
    RETURN; 
    END IF; 
    SELECT 
    category_id 
    INTO 
    in_category_id 
    FROM 
    schemas.book 
    WHERE 
    id = in_id; 

    SELECT category_id INTO tmp FROM schemas.category WHERE name = in_category; 
    IF (NOT FOUND) 
    THEN 
    SELECT nextval('schemas.category_category_id_seq') INTO tmp; 
    UPDATE schemas.book 
    SET 
     category_id = tmp, 
     published = in_published, 
     author  = in_author, 
     name  = in_name 
    WHERE 
     id   = in_id; 
    INSERT INTO schemas.category (
     category_id, 
     name 
    ) VALUES (
     tmp, 
     in_category 
    ); 
    ELSE 
    SELECT category_id INTO tmp FROM schemas.category WHERE name = in_category; 
    UPDATE schemas.book 
    SET 
     category_id = tmp, 
     published = in_published, 
     author  = in_author, 
     name  = in_name 
    WHERE 
     id   = in_id; 
    END IF; 
END; 
$$ 
LANGUAGE plpgsql; 

терминального выход:

pgdb=# select * from api.book_add('aaa', '10-12-13', 'Aauthor','Name'); 
book_add 
---------- 

(1 row) 

pgdb=# select * from api.book_list; 
id | category | published | author | name 
----+----------+------------+---------+------ 
    1 | aaa  | 2013-10-12 | Aauthor | Name 
(1 row) 

pgdb=# select * from api.book_update(1, 'bbb', '10-12-13', 'Aauthor','Name'); 
ERROR: insert or update on table "category" violates foreign key constraint "category_category_id_fkey" 
DETAIL: Key (category_id)=(2) is not present in table "book". 
CONTEXT: SQL statement "INSERT INTO schemas.category (
     category_id, 
     name 
    ) VALUES (
     tmp, 
     in_category 
    )" 
PL/pgSQL function "book_update" line 31 at SQL statement 
pgdb=# 

Почему нет, когда я обновляю CATEGORY_ID к ТМПУ в «книге» и после вставки этого ТМП в «категории» таблицы
Вот полная схема script Предложения по нему очень ценится, так как я

северо-запад
+0

Ваш весь ЕСЛИ НЕ НАЙДЕНО/ELSE логика не является необходимым, если вы делаете простой 'INSERT ... WHERE NOT EXISTS() 'first. UPDATE может быть «ОБНОВИТЬ книги ... ОТ категории», исключая переменную tmp и необходимость в plpgsql в целом. BTW - category_id серийный/bigserial тип данных? – wildplasser

+0

Я только что нашел вашу схему. По-моему, у вас есть ограничение внешнего ключа, обратное, книги IMHO должны ссылаться на category.category_id, который является серийным, поэтому * не может * быть внешним ключом. (по крайней мере, это было бы очень сложно) – wildplasser

+0

Пожалуйста, рассмотрите такую ​​проблему, у меня есть две книги под категорией ccc, если я обновляю ccc до ddd для первой книги, вторая книга автоматически получает ddd, что неверно :( – juk

ответ

2

Обычно книга должна относиться к категории вместо наоборот. Существует отношение N :: 1 книг к категориям; две книги могут относиться к одной и той же категории.

DROP SCHEMA IF EXISTS tmpschemas CASCADE; 
CREATE SCHEMA tmpschemas; 

CREATE TABLE tmpschemas.category 
    (category_id BIGSERIAL PRIMARY KEY 
    , catname   VARCHAR NOT NULL 
    , UNIQUE(catname) 
    , CHECK (catname <> '') 
); 

CREATE TABLE tmpschemas.book 
    (id   BIGSERIAL PRIMARY KEY 
    , category_id BIGINT NOT NULL REFERENCES tmpschemas.category(category_id) -- ON DELETE CASCADE ON UPDATE RESTRICT 
    , published DATE  NOT NULL 
    , author  VARCHAR NOT NULL 
    , bookname   VARCHAR NOT NULL 
    , UNIQUE(category_id, published, author, bookname) 
    , CHECK (TEXT(published) <> '') 
    , CHECK (author <> '') 
    , CHECK (bookname <> '') 
); 

Примечание Я удалил каскадном материал, я бы предположить, что категории являются достаточно стабильными (кто хотел бы обновить серийный столбец в любом случае) Cascade на удаление будет еще сложнее; Я не думаю, что вы хотите, чтобы выбросить все книги по gardeing, только потому, что категория «садоводство перестает существует Категория должна никогда быть удалена

ПРИМЕЧАНИЯ.. Если вы хотите книгу, чтобы быть в состоянии принадлежать к более одной категории, вы должны использовать таблицу N :: M распределительную

UPDATE:. пониженную функцию:

CREATE OR REPLACE FUNCTION tmpapi.book_add(
    in_catname VARCHAR, 
    in_published DATE, 
    in_author VARCHAR, 
    in_bookname  VARCHAR 
) RETURNS VOID AS $$ 
BEGIN 

    INSERT INTO tmpschemas.category(catname) 
    SELECT in_catname 
    WHERE NOT EXISTS (
     SELECT * 
     FROM tmpschemas.category nx 
     WHERE nx.catname = in_catname 
     ); 

    INSERT INTO tmpschemas.book (category_id, published, author, bookname) 
    SELECT cc.category_id 
     , in_published,in_author, in_bookname 
    FROM tmpschemas.category cc 
    WHERE cc.catname = in_catname 
     ; 

END; 
$$ 
LANGUAGE plpgsql; 

SELECT tmpapi.book_add('gardening', '2013-01-06' , 'Maggie Thatcher' , 'Destructing flowers Thoroughly'); 
SELECT tmpapi.book_add('gardening', '1980-05-26' , 'Umberto Eco' , 'The name of the rose'); 

SELECT * FROM tmpschemas.book; 

РЕЗУЛЬТАТ:

id | category_id | published |  author  |   bookname    
----+-------------+------------+-----------------+-------------------------------- 
    1 |   1 | 2013-01-06 | Maggie Thatcher | Destructing flowers Thoroughly 
    2 |   1 | 1980-05-26 | Umberto Eco  | The name of the rose 
(2 rows) 

Как вы можете видеть, обе книги относятся к той же категории_ид.

UPDATE2: здесь функция обновления:

CREATE OR REPLACE FUNCTION tmpapi.book_update(
    in_id  BIGINT, 
    in_catname VARCHAR, 
    in_published DATE, 
    in_author VARCHAR, 
    in_bookname  VARCHAR 
) RETURNS VOID AS $$ 
BEGIN 

    INSERT INTO tmpschemas.category(catname) 
    SELECT in_catname 
    WHERE NOT EXISTS (
     SELECT * 
     FROM tmpschemas.category nx 
     WHERE nx.catname = in_catname 
     ) 
     -- avoid inserting a category 
     -- if the book to be updated does not exist 
    AND EXISTS (
     SELECT * FROM tmpschemas.book bk 
     WHERE bk.id = in_id 
     ) 
     ; 

    UPDATE tmpschemas.book bk 
     SET category_id = cc.category_id 
     , published = in_published 
     , author = in_author 
     , bookname = in_bookname 
    FROM tmpschemas.category cc 
    WHERE cc.catname = in_catname 
     AND bk.id = in_id 
     ; 

END; 
$$ 
LANGUAGE plpgsql; 

SELECT tmpapi.book_update(1, 'politics', '2013-01-06' , 'Maggie Thatcher' , 'Destructing flowers Thoroughly'); 
SELECT * FROM tmpschemas.book; 

И результаты:

CREATE FUNCTION 
book_update 
------------- 

(1 row) 

id | category_id | published |  author  |   bookname    
----+-------------+------------+-----------------+-------------------------------- 
    2 |   1 | 1980-05-26 | Umberto Eco  | The name of the rose 
    1 |   2 | 2013-01-06 | Maggie Thatcher | Destructing flowers Thoroughly 
(2 rows) 

CREATE VIEW 
id | category | published |  author  |   bookname    
----+-----------+------------+-----------------+-------------------------------- 
    2 | gardening | 1980-05-26 | Umberto Eco  | The name of the rose 
    1 | politics | 2013-01-06 | Maggie Thatcher | Destructing flowers Thoroughly 
(2 rows) 
+0

Как вы можете объяснить, почему это говоря, что ключ отсутствует в таблице «book»? – juk

+0

Поскольку у вас было ограничение внешнего ключа в инвертированном направлении? У вас есть последовательный функционал как FK. При вставке сериал создает новое значение, которое ** не обязательно должно присутствовать * * в таблице книг. – wildplasser

+0

Спасибо мужчине loooot! – juk

Смежные вопросы