Trigger to prevent inserting 2 same values in one table - oracle

I have little problem with programming trigger for my dtb. I need to control 2 values in one 1 table. I have table called Concert and it has 2 foreign keys: 1 is the id of table Place. Second is not important for this I think.
Concert: id_concert, id_place<fk>, id_organizer<fk>, date, name, sponsor
Place: id_place, name, capacity, adress, town
What I want to eliminate is, that 2 concerts organized at same day cannot be on one place. So, I need to somehow control that user cannot insert the same date and same place for concert if there already concert with this values exists.
Thank you very much for your suggestions and sorry for bad english.

You need to add a unique constraint on your Concert table that consists of the (id_place, date) pair. This would instruct the database engine to not allow more than one Concert in the same place at the same time.
For Oracle, information can be found here: http://www.techonthenet.com/oracle/unique.php
CREATE TABLE Concert
(
... (filled in with your existing table definition)
CONSTRAINT concert_place_unique UNIQUE (id_place, date)
);
or to alter an existing table:
ALTER TABLE Concert
add CONSTRAINT concert_place_unique UNIQUE (id_place, date);
Constraints are the proper way to handle this condition, not triggers. Constraints are database intrinsic and have no race conditions and prevent the data from being added in the first place.

Related

TRIGGER in oracle to prevent insert duplicate data

I'm learning Oracle and I had a problem. I have my "Chat" table:
CREATE TABLE chat (
id_chat NUMBER,
id_user NUMBER,
start_chat DATE,
end_chat DATE
);
Now, I created a trigger so that it does not allow me to enter a "Chat" if an old one was already registered with the same id_user. This is my trigger:
create or replace trigger distChat
before insert on Chat
for each row
begin
if :new.id_user = :old.id_user then
Raise_Application_Error(-20099,'YOU CAN'T INSERT DUPLICATED DATA.');
end if;
end distChat;
But still it still allows me to enter Chat with the same user code. Any help or recommendation to improve my trigger that does not work.
Thank you.
Don't use a TRIGGER.
Two reasons:
Oracle provides a mechanism for preventing duplicates
Triggers are expensive, and another database object to maintain
Do this
ALTER table CHAT ADD CONSTRAINT xpk_chat PRIMARY KEY ( ID_CHAT );
I don't know your data model, but I think you want ID_CHAT to distinguish a chat. If you do this for ID_USER, then a user couldn't ever have more than one chat...and who would want to use that system? If I'm wrong, just change the column referenced in the ALTER command above.
Now your table will have a constraint that prevents duplicate values on ID_CHAT column. This is called a PRIMARY KEY (Docs)
Additionally, you will have an INDEX, so querying your CHAT's by their ID value could be quicker.
P.S. Your Trigger isn't doing what you want it to do. If you were to do it with a trigger you would need to raise the exception if :new.id_user in (select distinct id_user from chat)...so basically if the ID resulting in the INSERT was already found in the table, there would be an exception. The beauty of the PK constraint is, that the database does this FOR YOU.

How to limit a table to 5 records with a certain field combination

I'm using MS Access 2016.
Suppose I have a Student table, a Subject table (ie Geography, History etc) and a StudentSubject table that records which subjects each student has chosen.
StudentSubject contains StudentId and SubjectId as foreign keys.
No student is to choose more that 5 subjects.
Is there any way to define a validation rule on the table such that a given StudentId may not appear in the StudentSubject table more than 5 times?
I could enforce the contraint by introducing an additional table, but if possible, I like to avoid that.
I also want to define a constraint at table level, rather than use vba code that gets invoked when a record is inserted via a form. Access, as far as I know, has no such thing as triggers, as one would have in an sql system.
You can use a CHECK constraint to limit the possibilities:
ALTER TABLE StudentSubject
ADD CONSTRAINT Max5Subjects
CHECK(
NOT EXISTS(
SELECT 1
FROM StudentSubject
GROUP BY StudentID
HAVING Count(StudentID) > 5
)
)
Note that this might slow down data entry a bit, especially if StudentID is not indexed.
Check constraints need to be executed either in ANSI 92 compatible mode, or using ADO (e.g. using CurrentProject.Connection.Execute). More details here
To execute it using ADO, you can just use this single line in the immediate window:
CurrentProject.Connection.Execute "ALTER TABLE StudentSubject ADD CONSTRAINT Max5Subjects CHECK(NOT EXISTS( SELECT 1 FROM StudentSubject GROUP BY StudentID HAVING Count(StudentID) > 5))"
Also, keep in mind that if somehow there are records that violate the constraint (e.g. because they were present before the constraint got added), your table will get locked down fully.

Fast data migration on the same database

I'm trying to find a way to perform a migration from two tables on the same database. This migration should be as fast as possible in order to minimize the downtime.
To put it on an example lets say I have a person table like so:
person_table -> (id, name, address)
So a person as an Id, a name and an address. My system will contain millions of person registries and it was decided that the person table should be partitioned. To do so, I've created a new table:
partitioned_person_table->(id,name,address,partition_time)
Now this table will contain an extra column called partition_time. This is the partition key for this table since this is a range partition (one partition every hour).
Finally, I need to find a way to move all the information from the person_table to the partitioned_person_table with the best performance.
The first thing I could try is to simply create a statement like:
INSERT INTO partitioned_person_table (id, name, address, partition_time)
SELECT id, name, address, CURRENT_TIMESTAMP FROM person_table;
The problem is that when it comes to millions of registries this might become very slow (also the temporary tablespace might not be able to handle all this information)
My second approach was to use the EXCHANGE PARTITION method. Unfortunetly, I cannot do this because the tables contain diffrent column numbers.
Is there any other way that I can perfom this with the best performance (less downtime) ?
Thank you.
If you can live with the state, that all the current records would be located in one partition (and your INSERT approach suggest that), you may only
1) add a new column partition_time either as NULL or possible with metadata default only - required 12c
2) switch the table to a partitioned table either with online redefinition (if you have no maintainace window, where the table is offline) or with exchange partition otherwise.

Can a check constraint relate to another table? Oracle

I have searched for solution to my problem and this question describes it perfectly.
Let´s say I have one table called ProjectTimeSpan (which I haven´t, just as example!) containing the columns StartDate and EndDate.
And that I have another table called SubProjectTimeSpan, also containing columns called StartDate and EndDate, where I would like to set a Check constraint that makes it impossible to set StartDate and EndDate to values "outside" the ProjectTimeSpan.StartDate to ProjectTimeSpan.EndDate
Kind of a Check constraint that knows about another tables values...
Is this possible?
But I have a hard time to implement the solution to oracle. I've got even more puzzled when other articles stated that check constraint can not relate to other tables.
No it can't.
A FOREIGN KEY constraint can (and must) relate to another table, but it can only perform equiality checks.
I.e. you can test that a column (or a set of columns) are equal to those in the other table but not more complex conditions (like inside a span or whatever).
You'll have to implement a trigger for that.

Make column unique in two tables in our database

I have come into a bump at my current company where they have an account and a member. For some reason or another both are stored in separate tables.
Right now a member and an account can be registered. That's fine, except the users of both member and an account can have the same username. This is of course as you all know just wrong. Especially since they use the username to login to the same system except with different functionality levels.
Right now we are doing a check at the application level, and we're just wondering if it's possible to get the database to enforce two columns to be unique, say like a union of the two tables.
Can't set them up as primary or foreign key at the moment but that's for future anyway. Right now looking for a quick fix. In the future I will probably merge databases and get all members added on as new rows in the account table and add a boolean for IsMember.
In general, I agree with the consensus opinion that it's better to fix the design than to kluge a fix using triggers. However, a properly implemented trigger-based solution is still probably better than your current situation.
If you're going to use triggers, the right way to do it is to:
Create a new table that will contain nothing but usernames, with a primary key enforcing uniqueness (this may, in fact, be a good candidate for an indes-organized table).
Create before-insert triggers on both existing tables that add the new username to the new table. If the new username already exists, an error will be thrown, preventing the insert of both rows. Of course, the application will need to be able to handle this error gracefully (presumably it already can, for scenarios in which the new username already exists in the table it's being added to).
The wrong way to do this would be to make the trigger select from the other table, in order to verify uniqueness.
You can add a trigger that enforces your requirement.
The recommended triggers tend to be really brittle with concurrent transaction.
What you can do (AFAIK) is to create a materialized view containing the union of the column in question and put a unique constraint on that column.
Make sure you do some performance tests though.
As you use a soft delete pattern.
A trigger could be used (on each table) as a temporary measure.
By inserting a disabled record in the the other table, you will get a failure if the other record already exists
Remember this will not enforce the rule on existing data, only records that are inserted will be checked
Something like this:
-- Insert into the accounts table too
CREATE OR REPLACE TRIGGER tr_member_chk
BEFORE INSERT ON members
FOR EACH ROW
BEGIN
INSERT INTO account (name, id, etc, isenabled) VALUES(:new.name, :new.id, :new.etc, 0);
END;
-- Insert into the members table too
CREATE OR REPLACE TRIGGER tr_account_chk
BEFORE INSERT ON accounts
FOR EACH ROW
BEGIN
INSERT INTO members (name, id, etc,isenabled) VALUES(:new.name, :new.id, :new.etc,0);
END;

Resources