具有三列的Oracle Constraint,但只有一行可以等于某个值

Oracle Constraint with three columns, but only one row can equal a certain value

这听起来很容易,而且可能确实如此。我有 3 列,一列有一个常量字符串(名称),一列(状态)有两个选项(外键),另一列(room_id)是另一个 table 的父键,怎么能我确保只有一行被设置为相同名称的“ON”值。不能有多个具有相同名称的“ON”值的行,但可以有多个具有相同名称的“OFF”值的行。例子.

room_id       name       status
1             daniel       ON   --- OK for only one ON to be set for daniel
2             daniel       OFF
3             daniel       OFF
4             daniel       OFF
5             daniel       OFF
6             daniel       ON  --- THIS IS NOT ALLOWED...  but everything is UNIQUE
1             jeff         OFF
2             jeff         OFF
3             jeff         ON  --- OK for only ONE to be set to "ON" for jeff.
4             jeff         ON  --- THIS IS NOT ALLOWED... the room_id keeps things UNIQUE
5             jeff         ON  --- THIS IS NOT ALLOWED...

下面的 UNIQUE 约束允许具有相同名称的多个“ON”值。

ADD CONSTRAINT constratin_name_uq UNIQUE (room_id, name, status)

我相信我也需要一个 CHECK 约束,但是在值 = 'ON' 的状态列上的 COUNT 不大于 1...以确保只有一个“NAME”和“STATUS”具有 ON 值....

下面的约束不允许多个“OFF”值,这在我的情况下是必需的。

ADD CONSTRAINT constraint_name_uq UNIQUE (name, status)

此外...所有内容都可以“关闭”,但每个给定名称只能有一个“打开”。

感谢任何帮助,

谢谢, 丹尼尔

您可以创建唯一索引:

CREATE UNIQUE INDEX table_name__status_name__u
  ON table_name( CASE status WHEN 'ON' THEN name END );

其中,对于table:

CREATE TABLE table_name (
  room_id NUMBER
          NOT NULL,
  name    VARCHAR2(10)
          NOT NULL,
  status  VARCHAR2(3)
          NOT NULL
          CHECK ( STATUS IN ( 'ON', 'OFF' ) )
);

然后这个有效:

INSERT INTO table_name ( room_id, name, status )
SELECT 1, 'alice', 'ON' FROM DUAL UNION ALL
SELECT 2, 'alice', 'OFF' FROM DUAL UNION ALL
SELECT 3, 'alice', 'OFF' FROM DUAL UNION ALL
SELECT 4, 'alice', 'OFF' FROM DUAL UNION ALL
SELECT 5, 'alice', 'OFF' FROM DUAL UNION ALL
SELECT 6, 'alice', 'OFF' FROM DUAL UNION ALL
SELECT 1, 'beatrice', 'OFF' FROM DUAL UNION ALL
SELECT 2, 'beatrice', 'OFF' FROM DUAL UNION ALL
SELECT 3, 'beatrice', 'OFF' FROM DUAL;

然而,这:

INSERT INTO table_name ( room_id, name, status )
SELECT 7, 'alice', 'ON' FROM DUAL;

失败:

ORA-00001: unique constraint (FIDDLE_XFKAWDIVOXGJZVQESSZQ.TABLE_NAME__STATUS_NAME__U) violated

这也失败了,但出现了同样的异常:

INSERT INTO table_name ( room_id, name, status )
SELECT 4, 'beatrice', 'ON' FROM DUAL UNION ALL
SELECT 5, 'beatrice', 'ON' FROM DUAL;

db<>fiddle here

您的数据库中有冗余数据。一旦某个房间为给定名称打开,所有其他房间都已知(根据您的业务规则)对该名称关闭,不应将其存储在数据库中。只要有一个“person_location”table,包含两列,名称和当前 (ON) room_id 号码。对于那些人不在任何地方的时候,他的 room_id 应该设置为 NULL。假设此人一次不能在多个地方(同样,这似乎是您的业务规则),您可以通过将他们的当前位置作为现有主人的附加列 table,让您不必首先创建这个额外的 table。