(Note: updated with adopted answer below.)

对于PostgreSQL 8.1(或更高版本)分区表,如果UPDATE意味着对定义分区隔离的受约束字段进行更改,如何定义UPDATE触发器和将记录从一个分区"移动"到另一个分区的过程?

例如,我将表记录划分为活动记录和非活动记录,如下所示:

create table RECORDS (RECORD varchar(64) not null, ACTIVE boolean default true);
create table ACTIVE_RECORDS   ( check (ACTIVE) ) inherits RECORDS;
create table INACTIVE_RECORDS ( check (not ACTIVE) ) inherits RECORDS;

INSERT触发器和函数运行良好:新的活动记录放在一个表中,新的非活动记录放在另一个表中.我希望将UPDATEs移动到活动字段,以便将记录从一个子代表"移动"到另一个子代表,但我遇到了一个错误,这表明这可能是不可能的.

触发器规范和错误消息:

pg=> CREATE OR REPLACE FUNCTION record_update()
     RETURNS TRIGGER AS $$
     BEGIN
       IF (NEW.active = OLD.active) THEN
         RETURN NEW;
       ELSIF (NEW.active) THEN
         INSERT INTO active_records VALUES (NEW.*);
         DELETE FROM inactive_records WHERE record = NEW.record;
       ELSE
         INSERT INTO inactive_records VALUES (NEW.*);
         DELETE FROM active_records WHERE record = NEW.record;
       END IF;
       RETURN NULL;
     END;
     $$
     LANGUAGE plpgsql;

pg=> CREATE TRIGGER record_update_trigger
       BEFORE UPDATE ON records
       FOR EACH ROW EXECUTE PROCEDURE record_update();

pg=> select * from RECORDS;
record | active 
--------+--------
foo    | t         -- 'foo' record actually in table ACTIVE_RECORDS
bar    | f         -- 'bar' record actually in table INACTIVE_RECORDS
(2 rows)

pg=> update RECORDS set ACTIVE = false where RECORD = 'foo';
ERROR:  new row for relation "active_records" violates check constraint "active_records_active_check"

使用触发器过程(返回NULL等)向我建议在调用触发器之前判断约束并引发错误,这意味着我当前的方法无法工作.这能起作用吗?

UPDATE/ANSWER

下面是我最后使用的UPDATE触发器过程,与分配给每个分区的过程相同.完全归功于Bell,他的回答让我对分区有了关键的了解:

CREATE OR REPLACE FUNCTION record_update()
RETURNS TRIGGER AS $$
BEGIN
  IF ( (TG_TABLE_NAME = 'active_records' AND NOT NEW.active)
        OR
       (TG_TABLE_NAME = 'inactive_records' AND NEW.active) ) THEN
    DELETE FROM records WHERE record = NEW.record;
    INSERT INTO records VALUES (NEW.*);
    RETURN NULL;
  END IF;

  RETURN NEW;
END;
$$
LANGUAGE plpgsql;

推荐答案

它可以工作,执行移动的触发器只需要 for each 分区定义,而不是为整个表定义.所以,从表定义和插入触发器开始

CREATE TABLE records (
 record varchar(64) NOT NULL,
 active boolean default TRUE
);

CREATE TABLE active_records (CHECK (active)) INHERITS (records);
CREATE TABLE inactive_records (CHECK (NOT active)) INHERITS (records);

CREATE OR REPLACE FUNCTION record_insert()
RETURNS TRIGGER AS $$
BEGIN
  IF (TRUE = NEW.active) THEN
    INSERT INTO active_records VALUES (NEW.*);
  ELSE
    INSERT INTO inactive_records VALUES (NEW.*);
  END IF;
  RETURN NULL;
END;
$$
LANGUAGE plpgsql;

CREATE TRIGGER record_insert_trigger
 BEFORE INSERT ON records
 FOR EACH ROW EXECUTE PROCEDURE record_insert();

... 让我们来看看测试数据...

INSERT INTO records VALUES ('FirstLittlePiggy', TRUE);
INSERT INTO records VALUES ('SecondLittlePiggy', FALSE);
INSERT INTO records VALUES ('ThirdLittlePiggy', TRUE);
INSERT INTO records VALUES ('FourthLittlePiggy', FALSE);
INSERT INTO records VALUES ('FifthLittlePiggy', TRUE);

现在是分区上的触发器.如果新的话.活跃=旧.active check隐含在判断active的值中,因为我们首先知道表中允许的内容.

CREATE OR REPLACE FUNCTION active_partition_constraint()
  RETURNS TRIGGER AS $$
    BEGIN
      IF NOT (NEW.active) THEN
        INSERT INTO inactive_records VALUES (NEW.*);
        DELETE FROM active_records WHERE record = NEW.record;
        RETURN NULL;
      ELSE
        RETURN NEW;
      END IF;
    END;
    $$
    LANGUAGE plpgsql;

CREATE TRIGGER active_constraint_trigger
  BEFORE UPDATE ON active_records
  FOR EACH ROW EXECUTE PROCEDURE active_partition_constraint();

CREATE OR REPLACE FUNCTION inactive_partition_constraint()
  RETURNS TRIGGER AS $$
    BEGIN
      IF (NEW.active) THEN
        INSERT INTO active_records VALUES (NEW.*);
        DELETE FROM inactive_records WHERE record = NEW.record;
        RETURN NULL;
      ELSE
        RETURN NEW;
      END IF;
    END;
    $$
    LANGUAGE plpgsql;

CREATE TRIGGER inactive_constraint_trigger
  BEFORE UPDATE ON inactive_records 
  FOR EACH ROW EXECUTE PROCEDURE inactive_partition_constraint();

... 测试结果...

scratch=> SELECT * FROM active_records;
      record      | active 
------------------+--------
 FirstLittlePiggy | t
 ThirdLittlePiggy | t
 FifthLittlePiggy | t
(3 rows)

scratch=> UPDATE records SET active = FALSE WHERE record = 'ThirdLittlePiggy';
UPDATE 0
scratch=> SELECT * FROM active_records;
      record      | active 
------------------+--------
 FirstLittlePiggy | t
 FifthLittlePiggy | t
(2 rows)

scratch=> SELECT * FROM inactive_records;
      record       | active 
-------------------+--------
 SecondLittlePiggy | f
 FourthLittlePiggy | f
 ThirdLittlePiggy  | f
(3 rows)

Postgresql相关问答推荐

在输入稍有错误的PostgreSQL表中进行快速字符串搜索

在Haskell中定义新类型与持久化类型的惯用方法

Org.postgresql.util.PSQLException:错误:函数LOWER(BYTEA)不存在

GORM Golang SQL查询执行不区分大小写的搜索不起作用

在 SQL 上返回负值

MERGE 语句的锁定级别

是否可以在 postgresql 中添加表元数据?

如何在 postgresql 中创建一个空的 JSON 对象?

Postgres 会话处于空闲状态,query = COMMIT 或 ROLLBACK

heroku、postgreSQL、django、comments、tastepie:没有运算符匹配给定的名称和参数类型

在远程机器上Restore dump

如何引用使用 ON CONFLICT 中的函数的唯一索引?

sql语句错误:column .. does not exist

Sidekiq - 无法在 5.000 秒内获得数据库连接

在不存在的行上有select for update块

在 postgresql 中,如何在 jsonb 键上返回布尔值而不是字符串?

PostgreSQL 嵌套 CTE 和 UNION

如何将 CSV 数据插入 PostgreSQL 数据库(远程数据库)

python postgres 我可以 fetchall() 100 万行吗?

JOIN (SELECT ... ) ue ON 1=1?