support for check constraint for foreign key - sql

I have the following tables in database
Employee(emp_id,emp_name,salary,dept_id)
Department(dept_id,dept_name,location)
dept_id in Employee table is foreign key references on Department(dept_id)
I want to ask if I can make these constraint or not
(when inserting a row in employee table, the dept_id must found in department table, if it is not found >> it's automatically inserted in department table)
Can I make this using check constraint, if yes, how? if no, why?
Thanks

No, constraints cannot do inserts into other tables. To do what you want, you need to write a Trigger.

Related

update or delete on table "employee" violates foreign key constraint

I have an employee table with the following columns:
fname (varchar), lname (varchar), id (numeric)
id is the primary key.
There is a table name called works_on with columns
projectname (varchar), id (numeric)
Here, id is a foreign key that references the employee table.
When I was trying delete a row from the employee table like this:
delete from employee where id = 1
I get this error:
update or delete on table "employee" violates foreign key constraint "works_on_id_fkey" on table "works_on"`.
I am new to the database management system.
Any solution?
As employee is a foreign key in table works_on; the reason you are unable to delete employee ID 1 is because employee ID 1 exists on works_on (or perhaps other tables in which employee is a foreign key). The system is trying to maintain integrity of the database by preventing you from deleting an employee affiliated with works_on.
Say the system let you delete the employee record. Now when you look at the works_on table what would employee 1 relate to? You can no longer look up first/last names among other information. So the system is saying: If you want to delete the employee record, you must first remove/alter the foreign key associations to other system records; to ensure their continued integrity. If the system let you do this it would be called "Orphaning" a record. the parent record to which the child associates no longer exists.
To resolve a few options are:
Create a procedure that deletes employees but first checks any tables in which employee is a foreign key and ensures it's ok to delete those as well; and then deletes those records before deleting the employee record. (this can cause a massive daisy chain if those tables have PK's to which other tables are FK. But that's the nature of RDBMS.
Create a feature that lets you assign such records to employee 1's replacement or removes such records if no longer relevant.
enable ON DELETE CASCADE ON UPDATE CASCADE, which will automatically delete child records if parent record is deleted. (BE VERY CAREFUL AND CONSIDER how this impacts your system before enabling) Example: Docs
Don't delete the record, instead maintain a status field showing active/inactive and use it as a control mechanism to show or not show employees and their associated records.
There's several other options to consider as well; you must ask yourself, or the business for which this is being developed, what should happen to all those records in which employee 1 is a foreign key. Delete some/All, reassign some delete some? Prompt the user for how they want to handle each instance? Simply Inform the user they must first address the constraints found in (List all places this employee has a FK relationship?) and ensure they have a way to handle all those places... Lots of options.
You can not delete a row by that way. Because it has the constraint id in it (works_on_id_fkey). If you want to delete, you have to remove constraint from it.
alter table employee drop foreign key works_on_id_fkey

Unable to move primary key fields into foreign key?

I've created to two tables name STUDENT and ROOM which in my case is a one to one relation. The primary key from STUDENT table is a foreign key in ROOM but when i insert data into student its doesn't update values in foreign key of room.
There were many similar question about this in mysql and sqlserver but not in oracle sql.
I believe you are looking for cascading updates.
Check out this article:-
http://tkyte.blogspot.com/2009/10/httpasktomoraclecomtkyteupdatecascade.html?m=1
The script from article generates a package and three triggers which will do cascade updates.

column constraint that uses a user-defined function (udf)

I have 2 tables - EmpDetails & ChangeLog
EmpDetails stores details of employees - it has ID, Name etc.
ChangeLog is used to log changes to employee details - it has ID, DateOfChange, ChangeDescription, etc.
I wanted to make sure that ChangeLog.ID is a value contained in EmpDetails.ID column.
So, I put a CHECK constraint using a user-defined function for ChangeLog.ID column in (the UDF checks if ID exists in EmpDetails.ID or not)
My question is - if a particular ID's row is deleted from EmpDetails, will an error be raised if there are rows for that ID in ChangeLog ?
This does not seem to be the case... And I don't understand why.
So, how would I get such a functionality ? One way I can think of is to create a trigger for delete operations on EmpDetails..
Any other solution to the above problem ?
EDIT -
I tried to specify a Foreign Key relationship. But ID in ChangeLog is not a key as the ChangeLog table can contain multiple records for the same ID (I mean , employees can change their details more than once, hence there will be more than 1 record for the same ID in ChangeLog). Should I be able to specify a Foreign Key relationship even in that case ?
What you are describing is a foreign key relationship. To enforce it:
ALTER TABLE ChangeLog
ADD CONSTRAINT FK_EmpDetailsId FOREIGN KEY (ID)
REFERENCES EmpDetails (ID);
SQL Server will then maintain the relationship for you, without the need for your UDF. Inserts to ChangeLog will fail if the corresponding row in EmpDetails does not exist and deletes from EmpDetails will fail if there is a matching row in ChangeLog.
The check constraint in on the ChangeLog table, not on the EmpDetails, so when you modify EmpDetails, it simply does not get checked. SQL server is not smart enough to figure out that you would want the check to run when some other table is changed.
But, if you just want to ensure that there is a row in the EmpDetails table, why not use a simple referential integrity rule (a.k.a. a foreign key)?

Dealing with circular reference when entering data in SQL

What kind of sql tricks you use to enter data into two tables with a circular reference in between.
Employees
EmployeeID <PK>
DepartmentID <FK> NOT NULL
Departments
DepartmentID <PK>
EmployeeID <FK> NOT NULL
The employee belongs to a department, a department has to have a manager (department head).
Do I have to disable constraints for the insert to happen?
I assume your Departments.EmployeeID is a department head. What I'd do is make that column nullable; then you can create the department first, then the employee.
Q: Do I have to disable constraints for the insert to happen?
A: In Oracle, no, not if the foreign key constraints are DEFERRABLE (see example below)
For Oracle:
SET CONSTRAINTS ALL DEFERRED;
INSERT INTO Departments values ('foo','dummy');
INSERT INTO Employees values ('bar','foo');
UPDATE Departments SET EmployeeID = 'bar' WHERE DepartmentID = 'foo';
COMMIT;
Let's unpack that:
(autocommit must be off)
defer enforcement of the foreign key constraint
insert a row to Department table with a "dummy" value for the FK column
insert a row to Employee table with FK reference to Department
replace "dummy" value in Department FK with real reference
re-enable enforcement of the constraints
NOTES: disabling a foreign key constraint takes effect for ALL sessions, DEFERRING a constraint is at a transaction level (as in the example), or at the session level (ALTER SESSION SET CONSTRAINTS=DEFERRED;)
Oracle has allowed for foreign key constraints to be defined as DEFERRABLE for at least a decade. I define all foreign key constraints (as a matter of course) to be DEFERRABLE INITIALLY IMMEDIATE. That keeps the default behavior as everyone expects, but allows for manipulation without requiring foreign keys to be disabled.
see AskTom: http://www.oracle.com/technology/oramag/oracle/03-nov/o63asktom.html
see AskTom: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:10954765239682
see also: http://www.idevelopment.info/data/Oracle/DBA_tips/Database_Administration/DBA_12.shtml
[EDIT]
A: In Microsoft SQL Server, you can't defer foreign key constraints like you can in Oracle. Disabling and re-enabling the foreign key constraint is an approach, but I shudder at the prospect of 1) performance impact (the foreign key constraint being checked for the ENTIRE table when the constraint is re-enabled), 2) handling the exception if (when?) the re-enable of the constraint fails. Note that disabling the constraint will affect all sessions, so while the constraint is disabled, other sessions could potentially insert and update rows which will cause the reenable of the constraint to fail.
With SQL Server, a better approach is to remove the NOT NULL constraint, and allow for a NULL as temporary placeholder while rows are being inserted/updated.
For SQL Server:
-- (with NOT NULL constraint removed from Departments.EmployeeID)
insert into Departments values ('foo',NULL)
go
insert into Employees values ('bar','foo')
go
update Departments set EmployeeID = 'bar' where DepartmentID = 'foo'
go
[/EDIT]
This problem could be solved with deferable constraints. Such constraints are checked when the whole transaction is commited, thus allowing you to insert both employee and department in the same transaction, referring to each other. (Assuming the data model makes sense)
Refactor the schema by removing the circular reference.
Delete an ID column from either of the table schema.
Departments.EmployeeID doesn't seem to belong there in my opinion.
I can't think of a non hackish way to do this. I think you will need to remove the constraint or do some type of silly dummy values that get updated after all the inserts.
I'd recommend refactoring the DB schema. I can't think of any reasons why you would want it to work this way.
Maybe something like, Employee, EmployeeDepartment (EmployeeId, DepartmentId) and Department would be a better way to accomplish the same goal.
You could create a row in the Department table for 'Unassigned'
To create a new department with a new Employee you then would
Create the Employee (EmployeeA) in the 'Unassigned' Department
Create the new department (DepartmentA) with the employee EmployeeA
Update EmployeeA to be in DepartmentA
This wouldn't invalidate your current schema, and you could set up a task to be run regularly to check there are no members of the Unassigned department.
You would also need to create a default employee to be the Employee of Unassigned
EDIT:
The solution proposed by chaos is much simpler though
There are a few good designs I've used. All involve removing the "manager" EmployeeID from the Department table and removing the DepartmentID from the Employee table. I've seen a couple answers which mention it, but I'll clarify how we used it:
I typically end up with an EmployeeDepartment relationship link table - many to many, usually with flags like IsManager, IsPrimaryManager, IsAdmin, IsBackupManager etc., which clarify the relationship Some may be constrained so that there is only one Primary Manager allowed per department (although a person can be a PrimaryManager of multiple departments). If you don't like the single table, then you can have multiple tables: EmployeeDepartment, ManagerDepartment, etc. but then you could have situations where a person is a manager but not an employee, etc.
We also typically allowed people to be members of multiple departments.
For simplified access, you can provide views which perform the join appropriately.
Yes, in this instance you will have to disable a foreign key.
You need to get rid of one or the other reference permanently . This is not a viable design structure. Which has to be entered first? Department or Employee? Unless your departments are all one employee big, the structure doesn't make sense anyway as each employee would have to have a distinct departmentid.

Data Modeling: What is a good relational design when a table has several foreign key constrainst to a single table?

I have 2 tables:
1. Employees
2. Vouchers
Employees table has a single primary key.
Vouchers table has 3 foreign key constraints referencing the Employees table.
The following is a sample T-SQL script (not the actual table script) to create both tables and their relationship in SQL Server:
IF OBJECT_ID('dbo.Vouchers') IS NOT NULL
DROP TABLE dbo.Vouchers
IF OBJECT_ID('dbo.Employees') IS NOT NULL
DROP TABLE dbo.Employees
GO
CREATE TABLE Employees
(
ObjectID INT NOT NULL PRIMARY KEY IDENTITY
)
CREATE TABLE Vouchers
(
ObjectID INT NOT NULL PRIMARY KEY IDENTITY,
IssuedBy INT,
ReceivedBy INT,
ApprovedBy INT,
CONSTRAINT fk_Vouchers_Employees_IssuedBy FOREIGN KEY (IssuedBy)
REFERENCES Employees (ObjectID)
ON UPDATE CASCADE
ON DELETE NO ACTION,
CONSTRAINT fk_Vouchers_Employees_ReceivedBy FOREIGN KEY (ReceivedBy)
REFERENCES Employees (ObjectID)
ON UPDATE CASCADE
ON DELETE NO ACTION,
CONSTRAINT fk_Vouchers_Employees_ApprovedBy FOREIGN KEY (ApprovedBy)
REFERENCES Employees (ObjectID)
ON UPDATE CASCADE
ON DELETE NO ACTION
)
But an error is thrown:
Msg 1785, Level 16, State 0, Line 7
Introducing FOREIGN KEY constraint 'fk_Vouchers_Employees_ReceivedBy' on table 'Vouchers' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I don't have an idea of what efficient solution is available here. The requirements on the relationship is that: whenever an Employee is deleted, the Voucher that references some of its columns to the Employee does not get deleted (ON DELETE CASCADE is not an option). Instead, the values of the columns (IssuedBy, ReceivedBy and/or ApprovedBy) that are referenced to the deleted Employee should be set to NULL (since the columns are NULLABLE).
Many thanks!
Strictly from a relational design point of view, the Vouchers table as three Foreign Keys. Whether you choose to enforce them, through CASCADE assertions or otherwise, is an implementation issue, but the relational design still exists. Presumably you want to enforce that, if one of the three fields is not NULL, then a matching record needs to exist. Or not. It's an implementation issue as to whether or not you care to enforce the design.
However, the violations you describe are assumed at your peril. The fact that you're asking this question suggests you may not fully appreciate all the ways these choices can lead into the swamp.
I think the error may be a consequence of the fact that more than one of the three might refer to the same employee.
BTW, I've in very few cases ever found it necessary to delete records in such a fashion that CASCADES are useful. Usually that would be used to prevent the database from being too big; and database capacity is less and less an issue over time.
I would not actually delete Employees, but instead use a trigger to set a flag to mark them as deleted.
I generally don't turn on cascade of Updates or Deletes, but instead require an application to explicitly perform these actions.
From a design standpoint it seems good to have the 3 foreign keys you listed. It looks like the error message you are getting relates to the ON UPDATE CASCADE options on your foreign keys (although I was able to create the table as specified). Regardless, to get the behavior you mention wanting, I would recommend a trigger on the Employees table, that fires before you delete the record. This trigger would find instances of the Employees.OjbectID in the Vouchers table and set them to NULL.