如何防止在父表中插入?

时间:2012-03-03 11:32:28

标签: postgresql database-design inheritance triggers foreign-key-relationship

有一个表t继承了孩子。我希望只有孩子才能收到插页。强制父表拒绝插入的最佳方法是什么?

create table t (c int);
create table t1 () inherits (t);

这不应该是可能的:

insert into t (c) values (1);

编辑:

除了@wildplasser:

之外,我找到了一个模型可见的解决方案
create table tfk (c integer unique check(false));
create table t (c integer, foreign key (c) references tfk(c));

现在不可能insert into t除非它是空值,并且仍然可以insert into其子项。如果该列已经被约束为not null,那么它可以是一个很好的解决方案,否则就不够了。或者有人知道如何使上述工作为空值吗?

新闻:

我在postgresql列表中asked for a new syntax,它已经完成了9.2:

  

允许CHECK约束被声明为NO INHERIT(Nikhil Sontakke,Alex Hunsaker)

     

这使得它们只能在父表上强制执行,而不能在子表上强制执行。

4 个答案:

答案 0 :(得分:5)

create table t (c int, check (false) no inherit);

这将阻止插入表t。它添加了一个永远不会成立的约束,因此不能插入任何数据。 no inherit将阻止该约束影响子表。

答案 1 :(得分:3)

您可以使用before insert触发器引发错误或重定向到正确的表。

答案 2 :(得分:1)

这很难看,但似乎有效:

--SET search_path='tmp';

DROP TABLE dontinsert CASCADE;
CREATE TABLE dontinsert
        ( id INTEGER NOT NULL PRIMARY KEY
        );

DROP TABLE doinsert CASCADE;
CREATE TABLE doinsert ()
        INHERITS (dontinsert)
        ;

CREATE RULE dont_do_it AS
        ON INSERT TO dontinsert
        DO INSTEAD NOTHING
        ;

INSERT INTO dontinsert(id) VALUES( 13) ;

INSERT INTO doinsert(id) VALUES( 42) ;

SELECT id AS id_from_dont FROM dontinsert;

SELECT id AS id_from_do FROM doinsert;

结果:

SET
NOTICE:  drop cascades to table doinsert
DROP TABLE
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "dontinsert_pkey" for table "dontinsert"
CREATE TABLE
ERROR:  table "doinsert" does not exist
CREATE TABLE
CREATE RULE
INSERT 0 0
INSERT 0 1
 id_from_dont 
--------------
           42
(1 row)

 id_from_do 
------------
         42
(1 row)

更新:由于OP希望INSERTS失败并带来很多噪音,我不得不添加一个带有不可能约束的金丝雀表:

DROP TABLE alwaysempty CASCADE;
CREATE TABLE alwaysempty
        ( id INTEGER NOT NULL
        );

ALTER TABLE alwaysempty
        ADD CONSTRAINT dont_insert_you_sucker CHECK (id > 0 AND id < 0)
        ;

CREATE RULE dont_do_it AS
        ON INSERT TO dontinsert
        DO INSTEAD -- NOTHING
        INSERT INTO alwaysempty (id)
        VALUES (NEW.id)
        ;

新输出:

SET
NOTICE:  drop cascades to table doinsert
DROP TABLE
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "dontinsert_pkey" for table "dontinsert"
CREATE TABLE
ERROR:  table "doinsert" does not exist
CREATE TABLE
DROP TABLE
CREATE TABLE
ALTER TABLE
CREATE RULE
ERROR:  new row for relation "alwaysempty" violates check constraint "dont_insert_you_sucker"
INSERT 0 1
 id_from_dont 
--------------
           42
(1 row)

 id_from_do 
------------
         42
(1 row)

下一次尝试:将基本表格(仅限)移动到不可缓存的模式中(因为我真的讨厌触发器)......

SET search_path='tmp';

DROP SCHEMA hidden CASCADE;
CREATE SCHEMA hidden;
REVOKE ALL ON SCHEMA hidden FROM PUBLIC;

DROP TABLE dontinsert CASCADE;
CREATE TABLE dontinsert
        ( id INTEGER NOT NULL PRIMARY KEY
        );

DROP TABLE doinsert CASCADE;
CREATE TABLE doinsert ()
        INHERITS (dontinsert)
        ;

ALTER TABLE ONLY dontinsert SET SCHEMA hidden;

INSERT INTO alwaysempty (id) VALUES (NEW.id) ;

INSERT INTO dontinsert(id) VALUES( 13) ;

INSERT INTO doinsert(id) VALUES( 42) ;

SELECT id AS id_from_dont FROM hidden.dontinsert;

SELECT id AS id_from_do FROM doinsert;

答案 3 :(得分:0)

更新: revoke update等实际上很糟糕,因为它会使更新失败并且不会将它们自动导向分区 :-( ...所以最佳方式对我来说似乎是@JoeVanDyk s simple always-false check condition

作为 @derobert 在评论中已经提到过,对我来说似乎很好,考虑开销,速度和实用性,这样做通过权利

revoke insert, update, references on  my_master_table  from  my_upd_usr

references因为如果使用它就不会按预期工作:inheritance caveats

(当然,让一些用户有权在意外中插入到主人身上似乎是可能的,例如通过给予所有表格的权利,但我想它通常不太可能给某人&#34 ;意外地&#34; DML操作的权利。 即使这个人拥有权利,为什么他应该开始插入一个空表,看到它没有以这种方式使用,数据驻留在分区中。)