如何防止从表中已有的值派生的表中的某些值组合?

Posted

技术标签:

【中文标题】如何防止从表中已有的值派生的表中的某些值组合?【英文标题】:How can I prevent certain combinations of values in a table, derived from the values already in the table? 【发布时间】:2016-04-29 19:47:20 【问题描述】:

我正在开发一个用于存储和管理任务的网络应用程序,使用 Flask-SQLAlchemy 与后端对话。我想使用一个表来存储任务对之间的优先级比较,以便构造一个partial ordering。该表将有两列,一列用于较小的元素,一列用于较大的元素,两列一起构成主键。

到目前为止,我的代码如下所示:

class PriorityPair(db.Model):
    lesser_id = db.Column(db.Integer, db.ForeignKey('task.id'),
                          primary_key=True)
    lesser = db.relationship('Task', remote_side=[id])
    greater_id = db.Column(db.Integer, db.ForeignKey('task.id'),
                           primary_key=True)
    greater = db.relationship('Task', remote_side=[id])

    def __init__(self, lesser, greater):
        self.lesser = lesser
        self.greater = greater

总而言之,这对于我想要对表执行的操作应该足够了,但存在一个问题,即可能会插入不一致的行。假设我有两个任务,A 和 B。如果任务 A 的优先级高于任务 B,我可以执行以下操作:

pair = PriorityPair(task_b, task_a)
db.session.add(pair)
db.session.commit

并且两者之间的关系将根据需要存储。但是,如果在将来某个时间点,将相反的关系 PriorityPair(task_a, task_b) 插入到表中,那将是不一致的。这两个任务不能同时比其他任务更重要。

现在我可以在 python 代码中防止这种情况发生,但我想确保 DB 表本身保证保持一致。是否可以(通过 Flask-SqlAlchemy)对表施加某种约束,以便如果 (A,B) 已经存在,那么 (B,A) 将被自动拒绝?这样的约束是否会跨数据库后端起作用?

【问题讨论】:

您可以创建一个CHECK constraint 来强制执行排序的传递属性,但跨数据库的可移植性是有限的。 @LukasGraf 你能以下面答案的形式回答吗? 我自己还没有为任何重要的事情使用 CHECK 约束,而且我目前缺乏时间和必要的设置来写一个半体面的答案。但是,如果您能弄清楚如何使其适用于您的用例,如果您将其发布为自我回答,我将很乐意投票。 @LukasGraf 当然,我可以这样做,但我希望接受您的回答,以便您获得积分。 【参考方案1】:

没有和没有。

这是不可能的。 SqlAlchemy 支持CHECK constraints,但检查的表达式以字符串形式给出。它需要一个子查询,例如(greater_id, lesser_id) not in (select sub.lesser_id, sub.greater_id from priority_pair as sub)。底层数据库后端会阻止它:

mysql ignores all CHECK constraints. SQLitedoes not allow sub-queries in CHECK constraints. PostgreSQL does not allow sub-queries in CHECK constraints. 甲骨文does not allow sub-queries in CHECK constraints.

相反,您必须找到其他解决方案,无论是触发器还是只是更改整个模型,这是我决定做的。

【讨论】:

以上是关于如何防止从表中已有的值派生的表中的某些值组合?的主要内容,如果未能解决你的问题,请参考以下文章

SQL 从表中获取值

从表中的多个值中选择不在数组中的位置

从 DB2 中的表中获取值

如何从表中选择除外表中的值之外的所有值?

使用从 VBA 代码派生的值填充表中的字段

仅从表中的多个字段返回异常