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

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)?

Related

SQL delete query with foreign key constraint

I know that this question belongs to the very early stages of the database theory, but I have not encountered such a problem since several months. If someone has a database with some tables associated together as "chain" with foreign keys and they want to delete a record from a table which has some "dependent" tables, what obstacles arise? In particular, in a database with tables: Person, Profile, Preference, Filter exist the associations as Person.id is foreign key in Profile and Profile.id is foreign key in Preference and Filter.id is foreign key in Preference, so as that all the associationsenter code here are OneToMany. Is it possible to delete a Person with a simple query:
Delete from Person p where p.id= 34;
If no, how should look like the query in order to perform the delete successfully?
If the database in the application is managed by hibernate, what constraints (annotations) should I apply to the associated fields of each entity, so as to be able with the above simple query to perform the delete?
FOR SQL VERSION
Look at the Screenshot. you can use the Insert Update Specificaiton Rules. as it has Delete and Update Rules. you can set either of these values.
Foreign key constraints may be created by referencing a primary or unique key. Foreign key constraints ensure the relational integrity of data in associated tables. A foreign key value may be NULL and indicates a particular record has no parent record. But if a value exists, then it is bound to have an associated value in a parent table. When applying update or delete operations on parent tables there may be different requirements about the effect on associated values in child tables. There are four available options in SQL Server 2005 and 2008 as follows:
No Action
Cascade
SET NULL
SET Default
Use this article for Refrence.
http://www.mssqltips.com/sqlservertip/2365/sql-server-foreign-key-update-and-delete-rules/
ORACLE VERSION
you can use one of below.
alter table sample1
add foreign key (col1)
references
sample (col2)
on delete no action;
alter table sample1
add foreign key (col1)
references
sample (col2)
on delete restrict;
alter table sample1
add foreign key (col1)
references sample (col2)
on delete cascade;
for refrance.
http://docs.oracle.com/cd/B19306_01/server.102/b14200/clauses002.htm
answer is no if there is foreign key constraint then you have to delete leaf node table data first
that is first delete from Preference table
then from Profile and Filter Table
then delete record from Person table
This is the generic concept that you apply anywhere

Two tables reference each other: How to insert row in an Oracle database?

I have two tables
Department
Professor
in which Department has an attribute called HeadID referencing Professor
and Professor has an attribute called DeptID referencing Department
They form a circular relationship.
But the problem is that, how to insert a row to any of these tables?
Oracle complained "parent key not found" after I tried insert a row.
You can define one of the foreign key constraints as DEFERRABLE and defer constraint checking until the end of your transaction (instead of checking at the end of statement which ends with "parent key not found"). Read here
The other solutions described here are simpler.
But if you really want the DB to describe your buisiness (which is not necessarily the best approach) then you can have another table, lets say DEPT_HEAD_POSITIONS. the Department table will have the FK (HeadID) refer to this table, and the Professor table will have another nullable field as a FK to this new table.
Now, what you have is:
departments head positions
departments (that must have a head position)
professors (which must belong to a department and may be head of the department)
It is possible for a foreign key consisting of multiple columns to allow one of the columns to contain a value for which there is no matching value in the referenced columns, per the SQL-92 standard. To avoid this situation, create NOT NULL constraints on all of the foreign key's columns
for reference
so I think you can insert data in one of the row without giving value in foreign key column and then insert row into second row referring value of primary key in the first table and then you can proceed ...
If you have the authority to redesign the schema you should. If not I think the simplest and best approach is described in deathApril's comment.
In the use case where you want to add a new department and a new professor who heads it, you're best of:
Adding the Professor under a different department
Adding the Department with the Professor from Step 1 as head
Updating the Professor record from Step 1 to refer to his new Department created in Step 2
Oracle and SQL Server do not allow circular referencing because there is always a problem when deleting a row from a table having dependencies to another row from another table (foreign key) which refers to the row being deleted.....
For more Info: Click here

ON UPDATE CASCADE with two columns in a single table in SQL Server [duplicate]

I have a database table called Lesson:
columns: [LessonID, LessonNumber, Description] ...plus some other columns
I have another table called Lesson_ScoreBasedSelection:
columns: [LessonID,NextLessonID_1,NextLessonID_2,NextLessonID_3]
When a lesson is completed, its LessonID is looked up in the Lesson_ScoreBasedSelection table to get the three possible next lessons, each of which are associated with a particular range of scores. If the score was 0-33, the LessonID stored in NextLessonID_1 would be used. If the score was 34-66, the LessonID stored in NextLessonID_2 would be used, and so on.
I want to constrain all the columns in the Lesson_ScoreBasedSelection table with foreign keys referencing the LessonID column in the lesson table, since every value in the Lesson_ScoreBasedSelection table must have an entry in the LessonID column of the Lesson table. I also want cascade updates turned on, so that if a LessonID changes in the Lesson table, all references to it in the Lesson_ScoreBasedSelection table get updated.
This particular cascade update seems like a very straightforward, one-way update, but when I try to apply a foreign key constraint to each field in the Lesson_ScoreBasedSelection table referencing the LessonID field in the Lesson table, I get the error:
Introducing FOREIGN KEY constraint 'c_name' on table 'Lesson_ScoreBasedSelection' may cause cycles or multiple cascade paths.
Can anyone explain why I'm getting this error or how I can achieve the constraints and cascading updating I described?
You can't have more than one cascading RI link to a single table in any given linked table. Microsoft explains this:
You receive this error message because
in SQL Server, a table cannot appear
more than one time in a list of all
the cascading referential actions that
are started by either a DELETE or an
UPDATE statement. For example, the
tree of cascading referential actions
must only have one path to a
particular table on the cascading
referential actions tree.
Given the SQL Server constraint on this, why don't you solve this problem by creating a table with SelectionID (PK), LessonID, Next_LessonID, QualifyingScore as the columns. Use a constraint to ensure LessonID and QualifyingScore are unique.
In the QualifyingScore column, I'd use a tinyint, and make it 0, 1, or 2. That, or you could do a QualifyingMinScore and QualifyingMaxScore column so you could say,
SELECT * FROM NextLesson
WHERE LessonID = #MyLesson
AND QualifyingMinScore <= #MyScore
AND #MyScore <= QualifyingMaxScore
Cheers,
Eric

Database table id-key Null value and referential integrity

I'm learning databases, using SQLce. Got some problems, with this error:
A foreign key value cannot be inserted because a corresponding primary key value does not exist.
How does the integrity and acceptance of data work when attempting to save a data row that does not have specified one foreign key. Isn't it possible to set it to NULL in some way, meaning it will not reference the other table? In case, how would I do that? (For an integer key field)
Also, what if you save a row with a valid foreign key that corresponds to an existing primary key in other table. But then decide to delete that entry in this other table. So the foreign key will no longer be valid. Will I be allowed to delete? How does it work? I would think it should then be simply reset to a null value.. But maybe it's not that simple?
What you need to do is insert your data starting from the parent down.
So if you have an orders table and an items table that refers to orders, you have to create the new order first before adding all the children to the list.
Many of the data access libraries that you can get (in C# there is Linq to SQL) which will try and abstract this problem.
If you need to delete data you actually have to go the other way, delete the items before you delete the parent order record.
Of course, this assumes you are enforcing the foreign key, it is possible to not enforce the key, which might be useful during a bulk delete.
This is because of "bad data" you have in the tables. Check if you have all corresponding values in the primary table.
DBMS checks the referential integrity for ensuring the "correctness" of data within database.
For example, if you have a column called some_id in TableA with values 1 through 10 and a column called some_id in TableB with values 1 through 11 then TableA has no corresponding value (11) for that which you have already in TableB.
You can make a foreign key nullable but I don't recommend it. There are too many problems and inconsistencies that can arise. Redesign your tables so that you don't need to populate the foreign key for values that don't exist. Usually you can do that by moving the column to a new table for example.

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.