当前位置: 首页 > 面试题库 >

SQLAlchemy中的复杂外键约束

郎弘壮
2023-03-14
问题内容

我有两个表,SystemVariablesVariableOptionsSystemVariables应该是不言自明的,并且VariableOptions包含所有变量的所有可能选择。

VariableOptions有一个外键,variable_id指出了它是哪个变量的选项。SystemVariables有一个外键,choice_id指出了当前选择的选项。

我用得到周围的循环关系use_alterchoice_id,并post_updateSystemVariableschoice关系。但是,我想添加一个额外的数据库约束,以确保该约束choice_id是有效的(即,它所指的是一个引用它的选项)。

我需要的逻辑(假设它sysVar代表SystemVariables表中的一行)基本上是:

VariableOptions[sysVar.choice_id].variable_id == sysVar.id

但是我不知道如何使用SQL,声明式或任何其他方法来构造这种约束。如有必要,我可以在应用程序级别进行验证,但如果可能的话,我希望在数据库级别进行验证。我正在使用Postgres
9.1。

这可能吗?


问题答案:

您可以实现该目标 而无需肮脏的把戏 。只是 延长外键 引用所选择的选项包括variable_idchoice_id

这是一个工作示例。临时表,因此您可以轻松使用它:

CREATE TEMP TABLE systemvariables (
  variable_id integer PRIMARY KEY
, variable    text
, choice_id   integer
);

INSERT INTO systemvariables(variable_id, variable)
VALUES
  (1, 'var1')
, (2, 'var2')
, (3, 'var3');

CREATE TEMP TABLE variableoptions (
  option_id integer PRIMARY KEY
, option text
, variable_id integer REFERENCES systemvariables(variable_id)
                      ON UPDATE CASCADE ON DELETE CASCADE
, UNIQUE (option_id, variable_id) -- needed for the foreign key
);

ALTER TABLE systemvariables
ADD CONSTRAINT systemvariables_choice_id_fk
   FOREIGN KEY (choice_id, variable_id)
   REFERENCES variableoptions(option_id, variable_id);

INSERT INTO variableoptions
VALUES
  (1, 'var1_op1', 1)
, (2, 'var1_op2', 1)
, (3, 'var1_op3', 1)
, (4, 'var2_op1', 2)
, (5, 'var2_op2', 2)
, (6, 'var3_op1', 3);

允许选择关联的选项:

UPDATE systemvariables SET choice_id = 2 WHERE variable_id = 1;
UPDATE systemvariables SET choice_id = 5 WHERE variable_id = 2;
UPDATE systemvariables SET choice_id = 6 WHERE variable_id = 3;

但是并没有脱节:

UPDATE systemvariables SET choice_id = 7 WHERE variable_id = 3;
UPDATE systemvariables SET choice_id = 4 WHERE variable_id = 1;
ERROR:  insert or update on table "systemvariables" violates foreign key

constraint “systemvariables_choice_id_fk”
DETAIL: Key (choice_id,variable_id)=(4,1) is not present in table
“variableoptions”.


Voilá 。正是您想要的。

所有键列NOT NULL

我想我在后面的答案中找到了更好的解决方案:

  • 如何处理相互递归的插入

在注释中解决@ypercube的问题,以避免关联不明的条目创建所有键列NOT NULL,包括外键。

循环依赖关系通常使之不可能。这是经典的 鸡肉 问题:两者必须首先出现才能产生另一个。但是大自然找到了解决之道,Postgres也找到了办法:
可延缓的外键约束

CREATE TEMP TABLE systemvariables (
  variable_id integer PRIMARY KEY
, variable    text
, choice_id   integer NOT NULL
);

CREATE TEMP TABLE variableoptions (
  option_id   integer PRIMARY KEY
, option      text
, variable_id integer NOT NULL
     REFERENCES systemvariables(variable_id)
     ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
, UNIQUE (option_id, variable_id) -- needed for the foreign key
);

ALTER TABLE systemvariables
ADD CONSTRAINT systemvariables_choice_id_fk FOREIGN KEY (choice_id, variable_id)
   REFERENCES variableoptions(option_id, variable_id)
   DEFERRABLE INITIALLY DEFERRED; -- no CASCADING here!

变量和关联的选项必须插入同一事务中:

BEGIN;

INSERT INTO systemvariables (variable_id, variable, choice_id)
VALUES
  (1, 'var1', 2)
, (2, 'var2', 5)
, (3, 'var3', 6);

INSERT INTO variableoptions (option_id, option, variable_id)
VALUES
  (1, 'var1_op1', 1)
, (2, 'var1_op2', 1)
, (3, 'var1_op3', 1)
, (4, 'var2_op1', 2)
, (5, 'var2_op2', 2)
, (6, 'var3_op1', 3);

END;

NOT NULL约束不能被推迟,则立即执行。但是外键约束 可以 ,因为我们是这样定义的。在交易结束时检查它,避免了鸡蛋问题。

在此已 编辑 方案中, 两个外键都被推迟 。您可以按任意顺序输入变量和选项。

您可能已经注意到,第一个外键约束没有CASCADE修饰符。(允许更改variableoptions.variable_id级联回去是没有意义的。

另一方面,第二个外键具有CASCADE修饰符,并且仍被定义为可延迟的。这带有一些限制。手册:

NO ACTION即使约束被声明为可延迟的,也不能延迟检查以外的引用动作。

NO ACTION 是默认值。

因此,参照完整性检查就INSERT被推迟,但声明的级联的行动DELETEUPDATE没有。在PostgreSQL 9.0或9.1中
,以下内容是不允许的,因为在每个语句之后都会强制执行约束:

UPDATE option SET var_id = 4 WHERE var_id = 5;
DELETE FROM var WHERE var_id = 5;

奇怪的是,同样的事情在PostgreSQL 8.4中也有效 ,而文档中声明了同样的行为。看起来像是旧版本中的错误-
即使乍一看似乎是有益的,而不是有害的。对于新版本,必须已修复。



 类似资料:
  • mysql 5.7 外键约束 主表:部门表 从表:员工表 添加外键:从表 dep_id 关联 主表 id >[danger] CASCADE > 级联更新 主表数据更新从表会更新外键 级联删除 主表数据删除,从表会一起删除

  • 问题内容: 由于已经暗示了该关系,因此我不想在db中创建约束。我该怎么办? 目前,我在进行Alembic迁移后手动删除这些约束。 问题答案: 无需定义“模式”级别约束,而是创建自定义的外部条件;将您要用作“外键”的列以及传递给。您必须手动定义原因,因为: 默认情况下,此值是基于父表和子表(或关联表)的外键关系计算的。 外键也可以在using中内联注释: 您可以验证没有为表 c 发出约束: 警告:

  • 主要内容:在创建表时设置外键约束,在修改表时添加外键约束,删除外键约束MySQL外键约束(FOREIGN KEY)是表的一个特殊字段,经常与主键约束一起使用。对于两个具有关联关系的表而言,相关联字段中主键所在的表就是主表(父表),外键所在的表就是从表(子表)。 外键用来建立主表与从表的关联关系,为两个表的数据建立连接,约束两个表中数据的一致性和完整性。比如,一个水果摊,只有苹果、桃子、李子、西瓜等 4 种水果,那么,你来到水果摊要买水果就只能选择苹果、桃子、李子和西

  • 注意 当前章节中涉及的配置一般适用于关系数据库。这里展示的扩展方法在你安装了关系数据库提供程序之后就能获得(由Microsoft.EntityFrmeworkCore.Relational 程序包共享)。 外键约束是为模型中的关系引入的。 惯例 按照惯例,外键约束命名为 FK_<依赖实体类型名称>_<主实体类型名称>_<外键属性名称>。对于组合键,<外键属性名> 则为用下划线分隔的外键属性名。 数

  • 我的代码: 错误: 在课程和教师表的 ID 中插入数据后,我尝试运行第三个创建表代码,但没有变化。