如何处理其中一个可为空的相关复合外键?

How to deal with related composite foreign keys with one of them being nullable?

以下是相关架构部分:

CREATE TABLE "Alarm" (
  "id" SERIAL PRIMARY KEY NOT NULL
);

CREATE TABLE "AlarmSensor" (
  "alarmId" INTEGER NOT NULL REFERENCES "Alarm"("id"),
  "sensorId" INTEGER NOT NULL REFERENCES "Sensor"("id"),
  PRIMARY KEY ("alarmId", "sensorId")
);

CREATE TABLE "AlarmUser" (
  "alarmId" INTEGER NOT NULL REFERENCES "Alarm"("id"),
  "userId" INTEGER NOT NULL REFERENCES "User"("id"),
  PRIMARY KEY ("alarmId", "userId")
);

CREATE TABLE "AlarmOccurrence" (
  "id" SERIAL PRIMARY KEY NOT NULL,
  "time" TIMESTAMP NOT NULL DEFAULT now(),
  "alarmId" INTEGER NOT NULL,
  "sensorId" INTEGER NOT NULL,
  "acknowledgedAt" TIMESTAMP NULL,
  "acknowledgedByUserId" INTEGER NULL,
  -- Both FKs share the same alarmId, but I want acknowledgedByUserId to be initially null
  FOREIGN KEY ("alarmId", "sensorId") REFERENCES "AlarmSensor"("alarmId", "sensorId"),
  FOREIGN KEY ("alarmId", "acknowledgedByUserId") REFERENCES "AlarmUser"("alarmId", "userId")
);

你看,我不是数据库设计专家。所以如果你认为我可以以某种方式改进它,我很乐意学习如何去做!无论如何,我可以创建一个新的 AlarmOccurrence 并将 acknowledgeByUserId 初始设置为 null 吗?目前,要创建一个新的 AlarmOccurrence,我必须 connect/create 一个用户来填写 alarmId/userId 外键。我猜那是因为 alarmId 不可为空。

编辑:我发现这是一个 ORM 问题。我可以使用原始 SQL 根据需要插入到 table。但是在应用程序代码中,两个 FK 都被标记为必需。我将打开一个单独的问题。非常感谢您的帮助。

您有两个选择:

  • 使用默认选项定义外键MATCH SIMPLE

    然后,如果定义外键的任何列为 NULL,则不强制执行外键。

  • MATCH FULL

    定义外键

    然后所有外键列必须为 NOT NULL 或全部为 NULL(在这种情况下不强制执行外键)。

阅读 CREATE TABLE 的文档了解更多信息。

有抱负的数据库设计者请注意:永远不要使用双引号标识符。否则,您将不得不在所有 SQL 语句中使用双引号,这会让您的生活比必要的更艰难。

SQL 标准定义了部分可为空的外键的“匹配类型”。它具有三个可能的值。

  • 匹配完整:整个外键必须为空或不为空。如果不为空,则针对引用的 table.

    强制执行
  • 匹配 部分:如果 FK 的某些部分为空,则在引用的 table 中验证 non-null 列;在引用的 table 中必须至少有一行具有这些值。尽管在文档中提到了此模式,但 PostgreSQL 并未实现此模式。

  • 匹配简单:如果 FK 中有空值,它会被接受但不会根据引用的 table.

    进行验证

在您的情况下,“FULL”似乎对您来说是正确的选择。您可以在 PostgreSQL ALTER TABLE.

查看语法