Search code examples
oracle-databaseforeign-keysrdbms

Composite FK with nullable fields


I have 3 tables:

  • NETWORK_OPERATORs;

  • NETWORK_CELLs: each of them belongs to one NETWORK_OPERATOR;

  • IRIs: each of them can have either:

    1. a Network Operator or
    2. a Network Cell

but one of 1) and 2) is mandatory.

In case of 1) the netOpId must exists in NETWORK_OPERATOR table;

In case of 2) the cellId+netOpId must exist in CELL table;

Here is a sample DDL code:

CREATE TABLE "NETWORK_OPERATOR" (
  "NETOPID" INTEGER NOT NULL, 
  "NAME" VARCHAR2(20),
  CONSTRAINT "NETWORK_OPERATOR_PK" PRIMARY KEY ("NETOPID")
)

CREATE TABLE "NETWORK_CELL" (
  "CELLID" INTEGER  NOT NULL, 
  "NETOPID" INTEGER  NOT NULL, 
  "NAME" VARCHAR2(20), 
  CONSTRAINT "NETWORK_CELL_PK" PRIMARY KEY ("CELLID"),
  CONSTRAINT "CELL_NETOPS_FK" FOREIGN KEY ("NETOPID") REFERENCES "NETWORK_OPERATOR" ("NETOPID")
)

CREATE TABLE "IRI" (
  "IRIID" INTEGER NOT NULL,
  "NETOPID" INTEGER,
  "CELLID" INTEGER,
  "NAME" VARCHAR2(20),
  CONSTRAINT "IRI_PK" PRIMARY KEY ("IRIID"),
  CONSTRAINT "IRI_NETOPS_FK" FOREIGN KEY ("NETOPID") REFERENCES "NETWORK_OPERATOR" ("NETOPID")
)

In other words,

a NETWORK_CELL is itself always bound to a NETWORK_OPERATOR, so that IF a IRI has a netOpId it should be enforced to be an existing netOpId, ELSE IF a IRI has a cellId+netOpId it should be enforced to be an existing cellId+netOpId

I see 2 options:

Option 1:

Make only IRI.NETOPID NOT NULLable and add a composite FK

    CREATE TABLE "IRI" (
      ...
      "NETOPID" INTEGER NOT NULL,
      "CELLID" INTEGER,
      ...
      CONSTRAINT "IRI_CELL_FK" FOREIGN KEY ("CELLID", "NETOPID") REFERENCES "NETWORK_CELL" ("CELLID", "NETOPID")

)

(of course there will be a Unique key on "NETWORK_CELL" ("CELLID", "NETOPID"))

In other words, an IRI will have a mandatory FK relationship with a Network Operator, and an optional FK relationship with a Network Cell.

The "suspect" thing is that this "optional" FK is composed by a mandatory field and an optional one, on IRI side.

Oracle RDBMS accepts this (I just tried), but is it a good practice?

Option 2:

Same FK, like in option 1, but leave IRI.NETOPID nullable and add a custom constraint that enforce either netOpId or netOpId+cellId

I feel this solution more portable, but maybe I'm wrong.

The question

Are there better options?

What's the best practice to deal with this situation and why? I'm thinking about portability to other RDBMS, too...

Thank you


Solution

  • Your option 1 is OK. The way default FK (foreign key) declaration mode MATCH SIMPLE (usually the only one implemented) works, a FK subrow value with any NULLs satisfies its constraint. So you can have IRI FKs (netid) & (netid, cellid)--plus netid NOT NULL. (You seem to have forgotten the NOT NULL in your first IRI though not the second.)

    Then the only cases for the column pair are (non-null, null) & (non-null, non-null). A netid must exist; a non-null cellid must exist with that netid & a NULL cellid is OK.