【问题标题】:Get row to swap tables on a certain condition在特定条件下获取行以交换表
【发布时间】:2012-04-22 12:50:04
【问题描述】:

我目前有一个父表:

CREATE TABLE members (
    member_id SERIAL NOT NULL, UNIQUE, PRIMARY KEY
    first_name varchar(20)
    last_name varchar(20)
    address address (composite type)
    contact_numbers varchar(11)[3]
    date_joined date
    type varchar(5)
);

还有两个相关的表:

CREATE TABLE basic_member (
    activities varchar[3])
    INHERITS (members)
);

CREATE TABLE full_member ( 
    activities varchar[])
    INHERITS (members)
);

如果类型为full,则将详细信息输入到full_member 表中,或者如果类型为basic,则将详细信息输入到basic_member 表中。我想要的是,如果我运行更新并将类型更改为basicfull,则元组将进入相应的表。

我想知道我是否可以使用如下规则来做到这一点:

 CREATE RULE tuple_swap_full
 AS ON UPDATE TO full_member
 WHERE new.type = 'basic'
 INSERT INTO basic_member VALUES (old.member_id, old.first_name, old.last_name,
 old.address, old.contact_numbers, old.date_joined, new.type, old.activities);

...然后从full_member中删除记录

只是想知道我的规则是否在附近,或者是否有更好的方法。

【问题讨论】:

    标签: postgresql inheritance triggers constraints plpgsql


    【解决方案1】:
    • 你不需要

      member_id SERIAL NOT NULL, UNIQUE, PRIMARY KEY
      

      PRIMARY KEY 自动暗示 UNIQUE NOT NULL

      member_id SERIAL PRIMARY KEY
      
    • 我不会使用硬编码的最大长度 varchar(20)。如果您确实必须强制执行最大长度,只需使用 text 并添加检查约束。更容易改变。

    • INHERITS 的语法被破坏。关键字位于列周围的括号之外。

      CREATE TABLE full_member ( 
          activities text[]
      ) INHERITS (members);
      
    • 表名不一致 (members member)。在我的测试用例中,我到处都使用单数形式。

    • 最后,我不会对任务使用规则。 trigger AFTER UPDATE 似乎更可取。

    考虑以下

    测试用例:

    表格:

    CREATE SCHEMA x;  -- I put everything in a test schema named "x".
    
    -- DROP TABLE x.members CASCADE;
    CREATE TABLE x.member (
         member_id SERIAL PRIMARY KEY
        ,first_name text
        -- more columns ...
        ,type text);
    
    CREATE TABLE x.basic_member (
        activities text[3]
    ) INHERITS (x.member);
    
    CREATE TABLE x.full_member ( 
        activities text[]
    ) INHERITS (x.member);
    

    触发功能:

    Data-modifying CTEs (WITH x AS ( DELETE ..) 是实现此目的的最佳工具。需要 PostgreSQL 9.1 或更高版本。
    对于旧版本,首先是INSERT,然后是DELETE

    CREATE OR REPLACE FUNCTION x.trg_move_member()
      RETURNS trigger AS
    $BODY$
    BEGIN
    
    CASE NEW.type
    WHEN 'basic' THEN
        WITH x AS (
            DELETE FROM x.member
            WHERE member_id = NEW.member_id
            RETURNING *
            )
        INSERT INTO x.basic_member (member_id, first_name, type) -- more columns
        SELECT member_id, first_name, type -- more columns
        FROM   x;
    
    WHEN 'full' THEN
        WITH x AS (
            DELETE FROM x.member 
            WHERE member_id = NEW.member_id
            RETURNING *
            )
        INSERT INTO x.full_member (member_id, first_name, type) -- more columns
        SELECT member_id, first_name, type -- more columns
        FROM   x;
    END CASE;
    
    RETURN NULL;
    
    END;
    $BODY$
      LANGUAGE plpgsql VOLATILE;
    

    触发器:

    请注意,它是一个AFTER 触发器并具有WHEN 条件。 WHEN 条件需要 PostgreSQL 9.0 或更高版本。对于早期版本,您可以不使用它,由触发器中的 CASE 语句自行处理。

    CREATE TRIGGER up_aft
      AFTER UPDATE
      ON x.member
      FOR EACH ROW
      WHEN (NEW.type IN ('basic ','full')) -- OLD.type cannot be IN ('basic ','full')
      EXECUTE PROCEDURE x.trg_move_member();
    

    测试:

    INSERT INTO x.member (first_name, type) VALUES ('peter', NULL);
    
    UPDATE x.member SET type = 'full' WHERE first_name = 'peter';
    SELECT * FROM ONLY x.member;
    SELECT * FROM x.basic_member;
    SELECT * FROM x.full_member;
    

    【讨论】:

    • 完全同意使用触发器而不是规则。规则有很多违反直觉的行为,并且比触发器更有可能以令人惊讶的方式灼伤你。我有人想知道为什么他们会包含一个多余的WHEN 子句——通过避免触发触发器的开销,它有助于提高性能。我对主键列和 varchar(n) 与带有约束的文本的 NOT NULL 偏好匹配问题,而不是答案,但这些点在双方都有合理的论据。
    • 感谢您的回答,但它不起作用 Fuction 和 Trigger 可以正确输入但它不起作用,我可以进入将成员输入 x.member 的部分那行得通,但是当我运行更新时,我得到了 UPDATE 0 ,这显然意味着什么都没有发生。它不会更新 x.member 表或将其移动到 x.full_member 表:(
    • 我也希望对 basic_member 表或 full_member 表进行编辑。当我添加成员时,如果它们是基本成员,则使用 INSERT INTO basic_member VALUES (ect..),如果它们已满,则使用 INSERT INTO full_member VALUES (ect...)。如果我将基本表中的类型更改为完整类型或将完整表中的完整更改为基本,我只需要它们交换
    • @DannyCalladine:一定有误会。我在 PostgreSQL 9.1 中测试了代码,它按照你原来的问题工作——你只提到了 UPDATE。我会考虑完全防止 INSERT 中的不匹配类型(在 INSERT 之前添加一个触发器)。并为子表上的 AFTER UPDATE 添加类似的触发器。
    • 我设法让它基本上工作,我将第一个 x.member 更改为 x.full_member,第二个 x.member 更改为 x.basic_member。然后创建相同的 tirgger,但只是 x.member 表的两个表。我现在唯一想知道的是我如何在没有架构的情况下让它工作,或者架构是必须的。
    猜你喜欢
    • 2013-01-22
    • 1970-01-01
    • 2022-12-07
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-04
    相关资源
    最近更新 更多