tags:

views:

156

answers:

3

If I have a joining table with two columns, TeamA and TeamB, how can I ensure that each pair is unique?

Obviously I can put a unique composite index on these columns but that will only ensure uniqueness in the order A,B but not B,A correct?

TeamA | TeamB
-------------
 red  | blue
 pink | blue
 blue | red

As you can see, Red vs. Blue has already been specified as the first record and then it is specified again as the last. This should be illegal since they will already be facing each other.

Edit: Also, is there a way to handle the SELECT case as well? Or the UPDATE? DELETE? Etc..

Also, the idea of Home or Away team has been brought up which may be important here. This initial concept came to me while thinking about how to build a bracketing system on the DB side.

+2  A: 

If your RDBMS (you didn't specify) supports Triggers, then create a trigger on that table to enforce your constraint.

Create a trigger that fires on INSERT, that checks if a pair already exists with order reversed. If it does ROLLBACK, else allow the insert.

Mitch Wheat
Could you be a little more specific? I know what triggers are but I've never used them.
Joe Philllips
+3  A: 

Define a constraint such that, for example, the value in the A column must be (alphabetically or numerically) smaller than the value in the B column: thus you'd be allowed to insert {blue,red} but not {red,blue} because blue is less than red.

ChrisW
Also captures the case that you can't have {red, red} or {blue, blue}.
Apocalisp
but then how do you allow {yellow, blue}? i.e. What if TeamA is in yellow?
Mitch Wheat
This would essentially be telling me to have the application handle this scenario. Is there a better way?
Joe Philllips
Define the contraint in the database schema: something like ALTER TABLE MyTable ADD CONSTRAINT MyConstraint CHECK (TeamA < TeamB)
ChrisW
@Mitch I'm assuming it's sufficient to allow {blue, yellow} instead of {yellow, blue}
ChrisW
@ChrisW: It hink that assumption restricts valid domain instances
Mitch Wheat
@Mitch I'm not sure what you mean. Often when I want to define a pair then the sequence of the things within the pair doesn't matter to the domain: i.e. it doesn't matter which of blue or yellow is called TeamA and/or TeamB, the only important point is that the pair consists of blue and yellow. If the sequence is important and if you still want to contrain there to be only one of the two possible (sequenced) pairs, then maybe use a trigger or use a third column (whose value is for example either A_to_B or B_to_A) to specify the pair's sequencing. I prefer using a constraint to using a trigger.
ChrisW
@ChrisW: so do I. BUT you are saying that TeamA cannot have a yellow strip! Maybe TeamA is in yellow and TeamB in blue (but obviously not the other way around). I'm assuming this is a home/away team strip?
Mitch Wheat
The original question hadn't specified home or away but now that you bring it up, that is probably a better way to think about it
Joe Philllips
+1  A: 

Here is some sample code for use with the trigger method that Mitch described.

I have not tested this code, and it's late at night here :-)

CREATE TRIGGER trig_addTeam
ON Teams
FOR INSERT, UPDATE
AS

DECLARE @TeamA VARCHAR(100) 
DECLARE @TeamB VARCHAR(100)
DECLARE @Count INT

SELECT @TeamA = (SELECT TeamA FROM Inserted)
SELECT @TeamB = (SELECT TeamB FROM Inserted)

SELECT @Count = (SELECT COUNT(*) FROM TEAMS WHERE (TeamA = @TeamA AND TeamB = @TeamB)
                 OR (TeamA = @TeamB AND TeamB = @TeamA))

IF @Count > 0 THEN

BEGIN
    ROLLBACK TRANSACTION
END

What this is doing is looking to see if either sequence of A|B or B|A exists in the current table. If it does then the count returned is greater than zero, and the transaction is rolled back and not committed to the database.

Richard West