0

だからここに私の更新機能があります:

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をtmpに更新し、その後このtmpを「category」テーブルに挿入すると、なぜ存在しないのですか?
ここに完全なスキーマスクリプト があります。

4

1 に答える 1

2

通常、書籍はカテゴリを参照するのではなく、カテゴリを参照する必要があります。本とカテゴリの N::1 の関係があります。2 冊の本が同じカテゴリを参照している可能性があります。

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 <> '')
);

カスケードのものを削除したことに注意してください。カテゴリはかなり安定していると思います(とにかくシリアル列を更新したい人) 削除時のカスケードはさらに難しいでしょう。「ガーデニング」というカテゴリーがなくなったからといって、ガーデニングに関する本をすべて捨ててしまうわけにはいかないと思います。カテゴリーは決して削除してはなりません。

注: 本を複数のカテゴリに属する​​ことができるようにする場合は、N::M ジャンクション テーブルを使用する必要があります。

更新: 縮小された関数:

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)

ご覧のとおり、両方の本が同じ category_id を参照しています。

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)
于 2013-01-06T15:46:41.533 に答える