【问题标题】:Creating a Foreign Key based on an index name rather than table(columns)基于索引名称而不是表(列)创建外键
【发布时间】:2012-08-22 20:03:42
【问题描述】:

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

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')

【问题讨论】:

  • Table A添加一个年份列怎么样?

标签: sql postgresql indexing foreign-keys


【解决方案1】:

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

从阅读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();

【讨论】:

  • 如果 c_year 总是应该是 created_at 中的年份,我也会为此添加一个 CHECK 约束。
  • @Catcall:关于 CHECK 约束的好建议! (重命名为 a_year 以与问题保持一致。)
  • 感谢您的深思熟虑的建议。我并没有真正使用created_at,而是published_at,这会导致DEFAULT 失败(因为您不能根据另一列的值进行默认设置)。发布日期是任意日期。
  • @DavidChan:(重新发布以修复错别字)在这种情况下,我将采用触发路线(我的替代解决方案在底部),仅基于published_at 和带有ON UPDATE CASCADE 的fk。
猜你喜欢
  • 1970-01-01
  • 2019-01-04
  • 1970-01-01
  • 1970-01-01
  • 2013-04-17
  • 2018-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多