基于索引名称而不是表(列)创建外键

时间:2012-08-22 20:03:42

标签: sql postgresql indexing foreign-keys

我有一个带有时间戳字段的表,我在其上创建了一个复合索引。

CREATE INDEX "IDX_NAME_A" ON TABLE "A" (a_id, extract(year FROM created_at))

我有另一个存储一年的表和a_id,我想要一个外键关系。

我似乎无法找到我想要的语法。

ALTER TABLE "B" 
  ADD FOREIGN KEY(a_id, a_year) 
  REFERENCES A(a_id, extract(YEAR FROM created_at));

产生

ERROR:  syntax error at or near "("

我也试过......

ALTER TABLE "B"
  ADD FOREIGN KEY(a_id, a_year) 
  USING INDEX "IDX_NAME_A";

任何想法?

Table A
--------
a_id serial,
created_at timestamp default now()

Table B
-------
b_id serial
a_id integer not null,
a_year date_part('year')

1 个答案:

答案 0 :(得分:5)

外键约束无法引用索引。它必须是一张桌子 外键约束不能引用表达式。它必须指向引用表的列名 并且必须在引用列的集合上存在唯一索引(主键也是隐式的)。

首先阅读the manual about foreign keys here


优越的设计是删除列b.a_year。它是100%冗余的,可以随时从a.created_at派生。

如果您确实需要该列(例如,对于表b中的某些条件,每年强制执行一行),您可以实现这样的目标:

CREATE TABLE a (
  a_id serial
 ,created_at timestamp NOT NULL DEFAULT now()
 ,a_year NOT NULL DEFAULT extract(year FROM now())::int -- redundant, for fk
 ,CHECK (a_year = extract(year FROM created_at)::int)
);

CREATE UNIQUE INDEX a_id_a_year_idx ON TABLE a (a_id, a_year); -- needed for fk

CREATE TABLE b (
  b_id serial
 ,a_id integer NOT NULL
 ,a_year int -- doesn't have to be NOT NULL, but might
 ,CONSTRAINT id_year FOREIGN KEY (a_id, a_year) REFERENCES a(a_id, a_year)
);

@ Catcall评论后更新: CHECK约束与列DEFAULTNOT NULL条款相结合可以强制执行您的制度。

或者(不太简单,但允许NULL值)您可以使用trigger维护a.a_year中的值:

CREATE OR REPLACE FUNCTION trg_a_insupbef()
  RETURNS trigger AS
$BODY$
BEGIN

NEW.a_year := extract(year FROM NEW.created_at)::int;
RETURN NEW;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

CREATE TRIGGER insupbef
  BEFORE INSERT OR UPDATE ON a
  FOR EACH ROW EXECUTE PROCEDURE trg_a_insupbef();