【问题标题】:Whats is the best workaround to inherit unique contraints in Postgres?在 Postgres 中继承唯一约束的最佳解决方法是什么?
【发布时间】:2019-03-18 12:13:53
【问题描述】:

在 Postgres 中,我试图将一个独特的属性从父级继承到其子级。父表是一个抽象表,其中没有条目。所有孩子的名字都应该是唯一的。参考下面这个小(愚蠢)的例子:不应该有与香蕉同名的苹果(当然也可以是其他苹果)。

此场景的简约 Postgres 示例:

CREATE TABLE fruit(CONSTRAINT fruit_uniq_name UNIQUE (name))
CREATE TABLE banana(name text, length integer) INHERITS (fruit)
CREATE TABLE apple(name, diameter integer NOT NULL,) INHERITS (fruit)

在阅读了很多关于这个问题的帖子之后。他们都得出的结论是,仅使用 Postgres 的继承是不可能掌握这种情况的,我想知道是否有最佳实践解决方法,例如使用触发器和函数来解决这个问题?

我会为每一个小sn-p代码感到非常高兴,这可以帮助我摆脱这个烦人的陷阱。

【问题讨论】:

  • 没有简单的解决方案。使用扳机,卢克。但它可能是一个枚举子表的触发器......
  • 我目前看到的一种方式是孩子们是否向他们的爸爸扣动扳机。这听起来比它更残酷。如果父级上的插入在重复名称上失败,则停止拍摄。但我很好奇是否有人提出了更安全的解决方案。

标签: sql postgresql inheritance constraints unique


【解决方案1】:

我听从了 Laurenz Albe 的建议,我认为我通过在表 apple 和banana 上使用触发器以及测试唯一性的触发器函数tgf_name_exists() 解决了这个问题。

这是测试孩子名字唯一性的触发函数:

CREATE OR REPLACE FUNCTION tgf_name_exits()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    VOLATILE
    COST 100
AS $BODY$
    declare
    count_apple integer;
    count_banana integer;
    name text;
    schema text;
    error_text text;
BEGIN
    -- Variables
    error_text = '';
    schema = TG_TABLE_SCHEMA; -- actual schema
    name = NEW.name; --- actual attribute name

    -- Check
    EXECUTE format('SELECT count(*) FROM %s.apple apl WHERE apl.name=%L', schema, name) INTO count_apple;
    EXECUTE format('SELECT count(*) FROM %s.banana ban WHERE ban.name=%L', schema, name) INTO count_banana;

    -- Info 
    RAISE NOTICE 'Schema: %', schema;
    RAISE NOTICE 'Name: %', name;
    RAISE NOTICE 'Count: %', count_apple;
    RAISE NOTICE 'Count: %', count_banana;

    IF count_apple > 0 OR count_banana > 0 THEN
        -- Name ist already used
        if count_apple > 0 then
            error_text = error_text || "apple "
        end if;
        if count_banana > 0 then
            error_text = error_text || "banana "
        end if;
        RAISE EXCEPTION 'Name % already existing in table %', name, error_text;
    ELSE
        -- Name is unused -> OK 
        RETURN NEW;
    END IF;
END;
$BODY$;

这些是苹果和香蕉表的触发器

CREATE TRIGGER tg_apple_name_instert_update
    BEFORE INSERT OR UPDATE 
    ON apple
    FOR EACH ROW
    EXECUTE PROCEDURE tgf_name_exits();

CREATE TRIGGER tg_banana_name_uniq
    BEFORE INSERT OR UPDATE 
    ON banana
    FOR EACH ROW
    EXECUTE PROCEDURE tgf_name_exits();

如果有人能检查一下,那就太好了。从这里看起来它可以工作。

【讨论】:

    【解决方案2】:

    也许我想得太简单了,但似乎可以像这样重新创建此功能:

    CREATE FUNCTION fruit_check ( _name_ text )
    RETURNS
    boolean AS
            $$ SELECT _name_ NOT IN ( SELECT name FROM fruit )
            $$
            STABLE LANGUAGE SQL ;
    
    ALTER TABLE fruit ADD CHECK ( fruit_check ( name ) ) ;
    
    CREATE TABLE banana ( LIKE fruit INCLUDING ALL , length int            ) INHERITS ( fruit ) ;
    CREATE TABLE  apple ( LIKE fruit INCLUDING ALL , diameter int NOT NULL ) INHERITS ( fruit ) ;
    
    -- now test ...
    
      INSERT INTO banana VALUES ( 'ripe'  , 21 ) ; -- INSERT 0 1
      INSERT INTO apple  VALUES ( 'rosie' , 21 ) ; -- INSERT 0 1
    
      INSERT INTO banana VALUES ( 'rosie' , 21 ) ;
    --
    --  ERROR:  new row for relation "banana" violates check constraint "fruit_name_check"
    
      INSERT INTO apple  VALUES ( 'ripe'  , 21 ) ;
    --
    --  ERROR:  new row for relation "apple" violates check constraint "fruit_name_check"
    
      UPDATE apple set name = 'ripe' where name = 'rosie' ;
    --
    -- ERROR:  new row for relation "apple" violates check constraint "fruit_name_check"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-15
      • 1970-01-01
      • 2015-03-06
      • 2010-11-08
      相关资源
      最近更新 更多