views:

295

answers:

7

I've been racking my brain trying to come up with a solution to this.

For a database class, I need to implement the following:

Table HUSBANDS: (Name Varchar2(10)) (Wife Varchar2(10))
Table WIVES: (Name Varchar2(10)) (Husband Varchar2(10))

and using Oracle constraints, enfore the following rules:

  1. No two husbands can have the same name
  2. No two wives can have the same name
  3. Every wife must have one and only one husband
  4. Every husband must have one and only one wife

So far, I have implemented the table in Oracle SQL:

create table husbands(
  name varchar2(10) not null
  , wife varchar2(10) not null
);
create table wives(
  name varchar2(10) not null
  , husband varchar2(10) not null
);

I'm pretty sure I have solved points 1 and 2 using correct primary keys:

alter table husbands
  add constraint husbands_pk
  primary key(name);
alter table wives
  add constraint wives_pk
  primary key(name);

And here is where I'm running into issues. I figured to use foreign keys to implement steps 3 and 4:

alter table husbands
  add constraint husbands_fk_wife
  foreign key(wife)
  references wives(name);
alter table wives
  add constraint wives_fk_husband
  foreign key(husband)
  references husbands(name);

Now the test case my professor is using is to be able to add a married couple to the database. The problem I am having is how to do this using only constraints. If I wanted to add Jack and Jill as a married couple, one cannot add the husband until the wife is added. the wife cannot be added until a husband is added.
I think my problem is using foreign keys. A check constraint might work in this situation, but I cannot conceptualize how it would work.

A: 

1)setAutoCommit() as false 2)Inserts record into both table in one Unit Of Work. 3)commit

sacsha
Missing the essential part of making the constraints deferrable.See http://www.oracle-base.com/articles/8i/ConstraintCheckingUpdates.php
Gary
+3  A: 

An alternative to deferrable constraints is a third table of (husband, wife) with two unique constraints (one on husband, one on wife), and have referential integrity constraints between that and the husbands table and wifes table. The wife/husband columns on the husbands/wifes tables would be redundant and should be dropped.

PS. Should it be WIVES rather than WIFES ?

Gary
I agree on the redundancy, but that was the assignment. I'm glad I'm not the only one who sees a flaw in this problem.
sdellysse
+1  A: 

Study deferrable constraints (not a new type, just a param to the existing ones), so far you did good.

Michal Pravda
A: 

Hi sdellysse,

Deferrable constraints are the right way to do it. Interestingly, there is an alternative way however -- with your setup and Oracle 10gR2:

SQL> CREATE OR REPLACE TRIGGER husband_wife_trg AFTER INSERT ON husbands
  2  FOR EACH ROW
  3  BEGIN
  4     INSERT INTO wives VALUES (:new.wife, :new.name);
  5  END;
  6  /

Trigger created

SQL> INSERT INTO husbands VALUES ('Husband A', 'Wife B');

1 row inserted

SQL> SELECT * FROM wives;

NAME       HUSBAND
---------- ----------
Wife B     Husband A

I don't like putting transactional logic into triggers, but if you follow this path you don't need deferrable constraints.

Vincent Malgrat
I was thinking of that, but it was supposed to be without triggers.
sdellysse
+3  A: 

The need to use deferrable constraints is often a pointer to design problems. Certainly this data model is not a good one: it is not properly normalised. A normalised solution would look like this:

PERSON
------
ID number 
NAME varchar2(30)
PRIMARY KEY (ID)


MARRIED_COUPLE
--------------
PARTNER_1 number
PARTNER_2 number
PRIMARY KEY (PARTNER_1, PARTNER_2)
FOREIGN KEY (PARTNER_1) REFERENCES (PERSON.ID)
FOREIGN KEY (PARTNER_2) REFERENCES (PERSON.ID)

This has the added advantage of supporting civil partnerships :) If you want to discourage bigamy then you could put unique keys on PARTNER_1 or PARTNER_2.

It is trickier to model cultures where polygyny or polyandry is permitted.

edit

What David is objecting to (in the comments) is this:

SQL> create table married_couple (partner_1 number, partner_2 number)
  2  /

Table created.

SQL> alter table married_couple add primary key (partner_1, partner_2)
  2  /

Table altered.

SQL> insert into married_couple values (1, 2)
  2  /

1 row created.

SQL> insert into married_couple values (2,1)
  2  /

1 row created.

SQL>

It's a valid point but it is resolvable. For instance, with Oracle I can create a unique function-based to enforce uniqueness of permutations.

SQL> delete from married_couple
  2  /

2 rows deleted.

SQL> create unique index mc_uidx on married_couple 
  2     (greatest(partner_1, partner_2),least(partner_1, partner_2))
  3  /

Index created.

SQL> insert into married_couple values (1, 2)
  2  /

1 row created.

SQL> insert into married_couple values (2,1)
  2  /
insert into married_couple values (2,1)
*
ERROR at line 1:
ORA-00001: unique constraint (APC.MC_UIDX) violated


SQL>

To avoid polygamy we can use a similar trick. We don't want this:

SQL> insert into married_couple values (1,3)
  2  /

1 row created.

So, we need two indexes:

SQL> delete from married_couple where partner_2 = 3;

1 row deleted.

SQL> create unique index mc1_uidx
  2      on married_couple (greatest(partner_1, partner_2))
  3  /

Index created.

SQL> create unique index mc2_uidx
  2      on married_couple (least(partner_1, partner_2))
  3  /

Index created.

SQL> insert into married_couple values (3, 1)
  2  /
insert into married_couple values (3, 1)
*
ERROR at line 1:
ORA-00001: unique constraint (APC.MC2_UIDX) violated


SQL>

To those who think it's cheating to solve a data modelling issue with an implementation trick, I plead "Guilty as charged" but I have had a long and trying day of it.

APC
+1: Excellent suggestion, cyclic constraints cannot be properly modeled without leading to problems.
IronGoofy
polyandry is just a form of polygamy
ammoQ
@ammoQ - quite right, I should have said "polygyny".
APC
Sorry your solution does not work. For example you could have Bob as partner_1 and Mary as partner_2, Bob could also have Jane as partner_2. Additionally you could switch it up and have Bob as partner_2 and say Joanne as partner_1.
David
A: 

You need a third table, not only for fixing this, but also for properly handling polygamy/bigamy which is legal in over 40 countries of the world.

Marius Burz
+1  A: 

Silly idea - why not just have a single table "Couples" with columns "Husband_Name" and "Wife_Name" that each have a unique constraint? Seems to me like this satisfies all the requirements. :)

Vilx-