问题
I have two tables:
CREATE TABLE Event_details( event_no INTEGER AUTOINCREMENT NOT NULL,
no_players INTEGER NOT NULL,
game_type VARCHAR(20) NOT NULL,
payout_positions INTEGER NOT NULL,
PRIMARY KEY(event_no)
CONSTRAINT check_game_type CHECK(game_type IN ('NLH','NLO','PLO','PLH','STUD','HORSE')
CONSTRAINT check_no_players CHECK (no_players > 1)
CONSTRAINT check_payouts CHECK (payout_positions > 0 AND payout_positions < no_players));
CREATE TABLE Venue( venue_no INTEGER AUTOINCREMENT NOT NULL,
name VARCHAR(20) NOT NULL,
location VARCHAR(20) NOT NULL,
capacity INTEGER NOT NULL,
PRIMARY KEY (venue_no)
CONSTRAINT check_capacity CHECK (capacity > 0));
And a foreign key between them:
ALTER TABLE Event_details
ADD FOREIGN KEY (venue_no)
REFERENCES Venue(venue_no)
ON DELETE SET NULL;
I want to set up a CONSTRAINT (or TRIGGER???) that will not allow (or flag) an entry where Event_details(no_players) < Venue(capacity) where the Venue(capacity) is the value found in the foreign key row.
Is this possible?
回答1:
A couple of comments on the DDL you posted.
- There is no
AUTOINCREMENTkeyword in Oracle. You'd need to create a sequence (generally one sequence per table) and use theNEXTVALfrom the sequence either in theINSERTstatement itself or in a trigger to populate the synthetic primary key. - There is nothing that is creating a
VENUE_NOcolumn inEVENT_DETAILS. I assume your actual DDL is defining that column.
You cannot enforce this through a simple CHECK constraint. You can create a trigger
CREATE OR REPLACE TRIGGER validate_capacity
BEFORE INSERT OR UPDATE ON event_details
FOR EACH ROW
DECLARE
l_venue_capacity venue.capacity%type;
BEGIN
SELECT capacity
INTO l_venue_capacity
FROM venue
WHERE venue_no = :new.venue_no;
IF( l_venue_capacity < :new.no_players )
THEN
RAISE_APPLICATION_ERROR( -20001, 'Sorry, the venue has insufficient capacity' );
END IF;
END;
Be aware, however, that
- You would also need to have a trigger on the
VENUEtable that checks to see whether changes to the venue's capacity causes certain events to become invalid. Generally, that would require that there is some sort of date in the event details table since, presumably, the capacity of a venue can change over time and you really only want the validation to check for future events in that venue. - Trigger based solutions will not always work in multi-user environments. Imagine venue 1 has a capacity of 30. Now, session A updates that capacity to 15. But before session A commits, session B inserts an event with a
NO_PLAYERSof 20. Neither session's trigger will see a problem so both changes will be allowed. But once both sessions commit, there will be an event booked with 20 players in a venue that only supports 15 players. The trigger onEVENT_DETAILScould potentially lock the row in theVENUEtable to avoid this race condition but they you're serializing inserts and updates on theEVENT_DETAILStable which could be a performance problem particularly if your application ever waits for human input before committing a transaction.
As an alternative to triggers, you can create an ON COMMIT materialized view that joins the two tables together and put a CHECK constraint on that materialized view that enforces the requirement that the number of players cannot exceed the venue capacity. That will work in a multi-user environment but it requires materialized view logs on both base tables and it moves the check to the point where the sessions commit which can be a bit tricky. Most applications don't consider the possibility that a COMMIT statement could fail so handling those exceptions can be tricky. And from a user-interface standpoint, it can be somewhat tricky to explain to the user what the problem is since the exception may relate to changes made much earlier in the transaction.
来源:https://stackoverflow.com/questions/8418456/is-this-possible-in-oracle-sql