Pergunta

We have a Claim entity which might be proved by either a EmailVerification , put MetaCode in the relevant website or uploading CertificateFile. Now the problem is the claimer may choose to prove their claim by using any combination of the methods menetioned but in each category they got only one shot. Meaning :

 Claim -- 0..1 --> EmialVerification
 Claim -- 0..1 --> MetaCode
 Claim -- 0..1 --> CertificateFile

so this is valid:

 Claim #20 --> EmialVerification #12
 Claim #20 --> MetaCode (none)
 Claim #20 --> CertificateFile #124

also this:

 Claim #34 --> EmialVerification (none)
 Claim #34 --> MetaCode (none)
 Claim #34 --> CertificateFile (none)

but not this:

 Claim #34 --> EmialVerification #1034 & #450
 Claim #34 --> MetaCode (none)
 Claim #34 --> CertificateFile (none)

because #43 is related to two EmailVerifications.

Now I'm stuck at the table schema because I don't know how best to model a 0..1 relationship:

  • four separate tables with artificial PKs (integer ids) and go as is usual with 1..* relations
  • four separate tables but only one has an id and the other 3 use this id as both their own PK and FK to the Claim table ?
  • simply aggregate the 3 other tables into the Claim table and use Null to indication lack of relation ?
  • something else ?

EDIT As it appears the question is not very clear : a row in Claim can be connected to either 0 (not connected at all) or 1 row in any of the other 3 but any single row in Claim can not be connected to more than one row in any the other 3 tables (like the 3rd case in the example).

EDIT A terrible typo was lurking in the shadows (Claim #34 was entered at the first row in each example mistakenly as Claim #43) !!! . really sorry. I think the correct answer still holds by the way.

Foi útil?

Solução

After clarifying comments, you seem to need only to store the primary key in the various verification tables. (You don't have to use an artificial or surrogate key. If "claim_num" is a natural key, and it's varchar(15), use "claim_num".)

create table Claims (
  claim_id integer primary key,
  other_columns_go_here char(1) not null default 'x'
);

create table EmailVerifications (
  claim_id integer primary key references Claims (claim_id),
  email_verification_num integer not null unique,
  other_columns_go_here char(1) not null default 'x'
);

create table MetaCodes (
  claim_id integer primary key references Claims (claim_id),
  metacode_num integer not null unique,
  other_columns_go_here char(1) not null default 'x'
);

create table CertificateFiles (
  claim_id integer primary key references Claims (claim_id),
  certificate_file_num integer not null unique,
  other_columns_go_here char(1) not null default 'x'
);

These inserts will succeed.

insert into Claims values (20);
insert into EmailVerifications values (20, 12);
insert into CertificateFiles values (20, 124);

The first two inserts will succeed. The PRIMARY KEY constraint on "EmailVerifications" will make the third one will fail.

insert into Claims values (43);
insert into EmailVerifications values (43, 1034);
insert into EmailVerifications values (43, 450);

The following doesn't match the clarified requirements. Consider its continued presence a bonus.

If I understand correctly, you want each row in Claims to be referenced by either zero rows, or by one and only one row.

create table Claims (
  claim_id integer primary key,
  verification_code char(1) not null
    check (verification_code in ('c', 'e', 'm')),
  unique (claim_id, verification_code),
  other_columns_go_here char(1) not null default 'x'
);

create table EmailVerifications (
  claim_id integer not null,
  verification_code char(1) not null default 'e'
    check (verification_code = 'e'),
  primary key (claim_id, verification_code),
  foreign key (claim_id, verification_code)
    references Claims (claim_id, verification_code),
  other_columns_go_here char(1) not null default 'x'
);

create table MetaCodes (
  claim_id integer not null,
  verification_code char(1) not null default 'm'
    check (verification_code = 'm'),
  primary key (claim_id, verification_code),
  foreign key (claim_id, verification_code)
    references Claims (claim_id, verification_code),
  other_columns_go_here char(1) not null default 'x'
);

create table CertificateFiles (
  claim_id integer not null,
  verification_code char(1) not null default 'c'
    check (verification_code = 'c'),
  primary key (claim_id, verification_code),
  foreign key (claim_id, verification_code)
    references Claims (claim_id, verification_code),
  other_columns_go_here char(1) not null default 'x'
);

begin;
  insert into Claims values (1, 'c', 'x');
  insert into CertificateFiles values (1, 'c', 'x');
commit;

begin;
  insert into Claims values (2, 'e', 'x');
  insert into EmailVerifications values (2, 'e', 'x');
commit;

begin;
  insert into Claims values (3, 'm', 'x');
  insert into MetaCodes values (3, 'm', 'x');
commit;

The overlapping PRIMARY KEY and UNIQUE constraints in "Claims" are required. Other tables reference the pair of columns "claim_id" and "verification_code", and they can't do that unless there's a UNIQUE constraint on the pair of columns.

The verification_code and its CHECK constraints guarantee that the foreign key reference will come from the right table.

-- This insert will fail. 
-- Inserting into EmailVerifications requires 'e', not 'c'.
begin;
  insert into Claims values (4, 'c', 'x');
  insert into EmailVerifications values (4, 'c', 'x');
commit;

-- This insert will fail. (Duplicate row.)
insert into EmailVerifications values (2, 'e', 'x');

-- This insert will fail. (Trying to make two rows reference one row in Claims.)
insert into CertificateFiles values (2, 'e', 'x');
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top