【问题标题】:Foreign key filtering in PostgreSQLPostgreSQL 中的外键过滤
【发布时间】:2021-08-28 11:34:48
【问题描述】:

我目前正在 PostgreSQL 中寻找一种方法,我可以针对给定的表限制有效外键的数量 通过定义条件可用。

所以场景是这样的

https://dbfiddle.uk/?rdbms=postgres_11&fiddle=4dbe279906dc881598b7e72093534ce7

A、B、C都代表不同的实体,每个具体的条目都列在x_entry表中。

表 A、B、C 都代表给定条目的版本,以及它们在什么时间跨度内有效。

我想确保 表 A 总是有 B 和 C 的外键 其中时间跨度 A 的条件 A.B.C == A.C

在这里看到

违反了Ainit 范围为 2000-3000 的条件,因为binit 在时间范围2100-3000 中具有指向cinot 的外键 其中 ainit 将其外键从 cinit 更改为 cinat 违反了 A.B.C == A.C 的条件

但是我如何设置这样一个条件限制,以防止外键不违反这样的特定条件。 ? 甚至有可能吗?

【问题讨论】:

  • id BIGSERIAL REFERENCES C_entry(id),
  • 为什么没有意义,我在引用另一个表 id。
  • 外键另一个表中的PK(或其他一些唯一键)。那可能是一个(大)连续剧。外键本身不应该是序列号。 (这将使它与所引用的键相当独立)
  • @wildplasser 要么我理解错了,但是 postgresql 语法说明外键本身也需要指定类型。不能不指定。 postgresql.org/docs/8.3/tutorial-fk.html
  • 我认为,@wildplasser 的意思是,该类型应该是 BIGINT 而不是 BIGSERIAL

标签: postgresql foreign-keys constraints


【解决方案1】:

我目前正在 PostgreSQL 中寻找一种可行的方法 给定表,限制可用的有效外键数量 定义条件。

你不能这样做。

外键约束基于唯一性强制引用完整性,例如“table_a.id == table_b.a_id”。您不能过滤重叠的日期范围或类似的东西。

我可以从根本上想到一种方法来满足您的要求:触发器*

这不是太复杂,您在每个表上添加“BEFORE INSERT/DELETE/UPDATE”触发器,拒绝所有违反您规则的插入/更新/删除。

如果你可以重组你的表,它会变得更容易,你只需要两个表:

entry
  id
  type ("a", "b" ,"c")
  name ("cinit", etc.)

PK entry(id, type)

entry_version
  id
  entry_id
  type ("a", "b" ,"c")
  valid_period (tsrange)

PK entry_version(id)
FK entry_version(entry_id, type) -> entry(id, type)

现在您只需要一个在 entry_version 上执行规则的插入/更新/删除触发器。

您可能想要研究的内容:

排除约束例如,您可以确保不能将具有重叠日期范围的两行放在同一个表中。

表分区您可以为每个有效期或其他内容设置不同的分区。 (不过对你来说可能是一个死胡同)。

想办法将日期范围转换为表格中的行并使用外键引用它们。

*是的,也有规则:但那是专家级别

【讨论】:

    【解决方案2】:

    我结束了对表 A 和表 B 的检查约束 两者都检查此功能的地方

    CREATE OR REPLACE FUNCTION lookup_filtering_check(
        intermediate_table regclass, 
        intermediate_table_column_name text, 
        timespan tsrange,
        lookup_value uuid,
        entity_id uuid,
        OUT result boolean
    )
        LANGUAGE plpgsql AS
    $$
    BEGIN
       EXECUTE 
            FORMAT(
                'SELECT ('
                    'NOT EXISTS('
                        'SELECT FROM %1$I '
                        'WHERE %1$I.valid && %3$L '
                        'AND %1$I.id = %5$L '
                        'AND %1$I.%2$I<> %4$L '
                    ')'
                ')', 
                intermediate_table, 
                intermediate_table_column_name, 
                timespan, 
                lookup_value,
                entity_id)
       USING intermediate_table, timespan, lookup_value, id
       INTO result;
    
    END
    $$;
    

    在表 A 和表 B 中。

    我确实看到了一些性能问题,因为它必须检查每一行。

    【讨论】:

    • "PostgreSQL 不支持引用表数据的 CHECK 约束,而不是被检查的新行或更新行。虽然违反此规则的 CHECK 约束可能在简单测试中起作用,但它不能保证数据库将不会达到约束条件为假的状态(由于涉及的其他行的后续更改)。这将导致数据库转储和重新加载失败。即使完整的数据库状态为重新加载也可能失败与约束一致,因为行未按满足约束的顺序加载。
    • 此外,删除行时不会评估检查约束,因此这是另一种可能导致数据无效的方式。
    猜你喜欢
    • 2020-09-07
    • 2012-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-23
    • 1970-01-01
    • 2012-03-07
    相关资源
    最近更新 更多