I have two tables as follows:
department(alpha, college, etc.)
course(id, alpha, college, title, etc.)
College and alpha are present in both tables by design. I decided to de-normalize a little because the college and alpha are always desired when viewing a course.
I have one trigger that executes after the department table is updated so that it updates all rows in the course table with the new alpha and college values. I also have a trigger that executes before updating the course table to make sure that the alpha-college pair that the user submitted in his or her edits exists in the department table; if the pair isn't there it raises and application error.
These triggers conflict. The second one checks that the new values for the department table are in their, but they aren't yet so it fails like it should.
Is it possible to ignore the second trigger if the first trigger is executed first? I really don't want to execute the second trigger in this case, since I know the values are in the first table. If that's not possible, is there a better way to do this without changing my schema?
Your second trigger sounds like nothing more than a foreign key. Drop it and create a foreign key constraint on course instead. That works in my tests.
However, it seems like unnecessary work to support a denormalization that provides little benefit. If you just want to write simple queries, create a view that joins the two tables and use that in your queries. If you are concerned about the join performance, I doubt very much that it will be a problem, unless you are missing obvious indexes on the tables.
I would sincerely recommend removing your trigger approach all together since it's burdened by dirty reads. Whenever I faced a challenge such as this I would implement the DML using Stored Procedures only. You get all the advantages of triggers without the headaches if implemented properly.
If your fear is you want to make sure all updates to the department table follow your logic as do changes in course, remove update permissions to any user except the owner of the stored procedure. This ensures the only caller who can modify that table is the stored procedure you control and understand. And by coincidence, it becomes the only way to update the tables.
Just $0.02
Like most other cases implemented with triggers, you can see the burden here because the data-model itself has defects.
You can implement the same logic as below and maintain all rules using PK and FK constraints.
---Department references College...
Create table department(
department_id number primary key,
aplha varchar2(20) not null,
college varchar2(20) not null
);
***--Course belongs to a department.. so should be a child of department.
--If it's possible for different depts to give the same course (IT and CS),
--you'll have
--a dept_course_asc table***
Create table Course(
course_id number primary key
department_id number references department(department_id),
course_name varchar2(100) not null
);
if you have a student table, you'll associate it with the course table with another student_table association table.
It might appear these are a lot more tables than you intially showed, but if you want to avoid data redundancies and don't want to have the burden of updating columns in all tables whenever they change in the parent table, the above model is the only way to go.
There are two possible solutions to the problem.
Solution 1: Using DEFERRABLE FK constraint.
This solution is only possible if (alpha, college) combination is unique and can be defined as a PK for department table.
In this case, you do not need the trigger on the course table.
Instead, you define a DEFERRABLE FK (alpha, college) on course that referenced the department table.
And before the update on department you must execute SET CONSTRAINT ... DEFERRED statement see documentation.
Then the FK will be not verified until the commit.
Solution 2: Using system context
You switch off the second trigger using the local system_context.
The context must be created first. see User Created Contexts
The trigger on department set a variable in the context to a some value.
And in the second trigger on courses you check the value of the variable in the context
Related
I have encountered a problem in my project using PostgreSQL.
Say there are two tables A and B, both A and B have a (unique) field named ID. The ID column of table A is declared as a primary key, while the ID column of table B is declared as a foreign key pointing back to table A.
My problem is that every time we have new data inputted into database, the values in table B tend to be updated prior to the ones in table A (this problem can not be avoided as the project is designed this way). So I have to modify the relationship between A and B.
My goal is to achieve a situation where I can insert data into A and B separately while having the ON DELETE CASCADE clause enabled. What's more, INSERT and DELETE queries may happen at the same time.
Any suggestions?
It sounds like you have a badly designed project, if you can't use deferred constraints. Your basic problem is that you can't guarantee internal consistency of the data because transactions may occur which do not move the data from one consistent state to another.
Here is what I would do to be honest:
Catalog affected keys.
Drop affected key constraints.
Write a periodic job that looks for orphaned rows. Use LEFT JOIN because antijoins do not perform as well in PostgreSQL.
The problem with a third table is it doesn't solve your basic problem, which is that writes are not atomically consistent. And once you sacrifice that a lot of your transactional controls go out the window.
Long term, the project needs to be rewritten.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Foreign key constraint may cause cycles or multiple cascade paths?
I have a database of contact details that contains four primary tables:
Organisations
Departments
Sections
People
Here is a diagram (the arrows indicate foreign key constraints):
IMPORTANT: I accidentally marked the green arrow as pointing to per_PerID. It should point to per_SecID
I have created constraints that ensure each department record falls under an organisation, each section falls under a department, and so forth. These foreign key constraints have their action set to CASCADE, so that deleting an organisation will delete all corresponding departments etc.
The problem is that each organisation needs to have a person in charge. This translates to a foreign key constraint on the field that will contain the ID of the person in charge. Creating the constraint was easy, but when I tried to set the ON DELETE action to SET NULL, I got the following error:
Introducing FOREIGN KEY constraint ... on table
... may cause cycles or multiple cascade paths. Specify ON
DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
My reasoning was that cycles would only occur if I set the action to CASCADE, not SET NULL, but evidently this was incorrect.
Person record deleted
org_incharge_PerID set to null in corresponding organisation record
No further propagation occurs. If the organisation had been deleted instead there would be a problem, since departments also refer to organisations. As things stand there shouldn't be an issue.
Is there another way to ensure referential integrity, or should I rethink my table structure, in a way that can avoid the cycles problem entirely?
What I want is for org_incharge_PerID to be constrained to the set of per_PerIDs in the persons table. Additionally, I want for the value to be set to NULL if that person is deleted.
Once you have run into the cycles warning, the only other way to ensure referential integrity without changing your database structure is to use a trigger for one of the constraints. The problem triggering the warning is twofold:
The Sections table shares two opposite-direction foreign key constraints with the Persons table.
All the four tables have a full cycle that progresses through all of them in the same direction, Person->Section->Department->Organization.
Additionally, there is an issue with the table design: the way it is constructed, a person could be in charge of a section that he is not a member of.
You can get around two these three problems by removing InCharge column from Sections and introducing another table, SectionInCharge, containing columns PerID and SecID, with a clustered index/unique constraint on SecID, and a composite foreign key to the Persons table on both columns. You will need to add a UNIQUE constraint to the two columns in the Persons table so the FK can relate to them.
Now you can set the new FK to ON DELETE CASCADE and delete away.
It is often a superior database design pattern to use the presence or absence of rows to signify something rather than to use a nullable column.
Now that I think about it, the other tables with a "person in charge" column have a similar problem in regards to allowing the assignment of someone who is not properly a member. The only way I know to solve this using FKs (which in my mind is best practice over triggers) is by propagating columns through tables and including them in composite keys. This has the unfortunate side effect of making things a little awkward, but it is not too bad as you can put the most-specific column first and join only on it when possible. So you would put OrgID in the Section table, with a composite FK to the Dept table on (DeptID, OrgID), and then put DeptID and OrgID also in the Persons table. You will then need DeptInCharge and OrgInCharge tables.
One more note about this (that's not really about solving the core problem you presented) that you might be interested in is the possibility of combining your three bottom tables into a single table, OrgUnit. Add an OrgUnitType column and a ParentOrgID column. Either add columns that will be NULL when the thing does not possess that property (as in Sections not having a fax number), or make the existing Organizations, Departments, and Sections tables subtypes of the OrgUnit (example of DB supertype/subtype). The advantage of this is that then you can refer to any one of the three types anywhere you want to: if the FK is to OrgUnit, it can be any one of them. But if it is only allowed to be a Section, then you would FK to that table instead. In fact, that is then perfect for a single OrgUnitInCharge table, with an FK to OrgUnit (OrgUnitID, OrgUnitTypeID).
I know there are some loose ends here that I haven't tied up completely into a pretty package for you, but I hope that what I've described so far is helpful.
P.S. I hope you will forgive me for adding a stylistic consideration. To me it is a lot of extra needless decoration to put a table-signifying prefix on every column in each table. I type 90-110 wpm depending on how good I feel, but I still hate typing any more than I have to. Also, I believe very strongly in keeping a high signal-to-noise ratio in my code, and for me, the prefixes exploding all over the place drastically reduce that. In SQL queries, best practice is to use table aliases and then use those aliases on each column, as in SELECT P.Name FROM dbo.Person P. To resolve the issue of multiple tables having the same column names, I have started making the typical WidgetName column in my Widget table to also be Widget. As one example of why a high signal-to-noise is so important: vrbauxCan proYou verRead artThis nouSentence advQuickly conAnd advEasily? True, the additional prefixes contain useful information, but that useful information is only a little useful, and a lot distracting. If you're designing your database from scratch now, I'd like to request, as someone who could by a strange coincidence some day end up maintaining it (stranger things have happened), that you not use those prefixes. :)
P.P.S. I like PascalCase over under_scores because it's easier to type. The underscore is on the top row and hit by the weakest finger--so it's slower and error prone.
P.P.P.S. I also prefer singular table names, but that's neither here nor there is it? I apologize in advance for hitting you up with style critique.
Can you show sample coding to create a trigger or stored procedure that maintains rows a SQL Server junction table when changes are made to the Authors and BookTitles tables such as inserting and updating rows in those tables?
We have the following tables:
Authors:
ID
NAME
ZIP
AND SOME MORE COLUMNS
BookTitles:
ID
TITLE
ISBN
AND SOME MORE COLUMNS
This is the table we will use as our junction table:
AuthorTitles:
ID
AUTHOR_ID
BOOK_TITLE_ID
We would like to do this in a trigger instead of doing the coding in our VB.Net form.
All help will be appreciated.
The above table structures were simplified to show what we are trying to do.
We are implementing a junction table for teachers and programs.
Here is a photo of the actual system:
Unless you have Foreign Key constraints that require at least one Book per Author and/or vice-versa, then the only cases that you should need special handling are for the Deletes to BookTitles or Authors. They can be done like this:
CREATE PROC BookTitle_Delete(#Book_ID As INT) As
-- First remove any children in the junction table
DELETE FROM AuthorTitles WHERE BOOK_TITLE_ID = #Book_ID
-- Now, remove the parent record on BookTitles
DELETE FROM BookTitles WHERE ID = #Book_ID
go
In general, you want to resist the temptation to do Table Maintenance and other things like this in Triggers. As triggers are invisible, add additional overhead, can cause maintenance problems for the DBA's, and can lead to many subtle transactional/locking complexities and performance issues. Triggers should be reserved for simple things that really should be hidden from the client application (like auditing) and that cannot be practically implemented in some other way. This is not one of those cases.
If you really want an "invisible" way of doing this then just implement a Cascading Foreign-Key. I do not recommend this either, but it is still preferable to a trigger.
I have a problem that can be summarized as follow:
Assume that I am implementing an employee database. For each person depends on his position, different fields should be filled. So for example if the employee is a software engineer, I have the following columns:
Name
Family
Language
Technology
CanDevelopWeb
And if the employee is a business manager I have the following columns:
Name
Family
FieldOfExpertise
MaximumContractValue
BonusRate
And if the employee is a salesperson then some other columns and so on.
How can I implement this in database schema?
One way that I thought is to have some related tables:
CoreTable:
Name
Family
Type
And if type is one then the employee is a software developer and hence the remaining information should be in table SoftwareDeveloper:
Language
Technology
CanDevelopWeb
For business Managers I have another table with columns:
FieldOfExpertise
MaximumContractValue
BonusRate
The problem with this structure is that I am not sure how to make relationship between tables, as one table has relationship with several tables on one column.
How to enforce relational integrity?
There are a few schools of thought here.
(1) store nullable columns in a single table and only populate the relevant ones (check constraints can enforce integrity here). Some people don't like this because they are afraid of NULLs.
(2) your multi-table design where each type gets its own table. Tougher to enforce with DRI but probably trivial with application or trigger logic.
The only problem with either of those, is as soon as you add a new property (like CanReadUpsideDown), you have to make schema changes to accommodate for that - in (1) you need to add a new column and a new constraint, in (2) you need to add a new table if that represents a new "type" of employee.
(3) EAV, where you have a single table that stores property name and value pairs. You have less control over data integrity here, but you can certainly constraint the property names to certain strings. I wrote about this here:
What is so bad about EAV, anyway?
You are describing one ("class per table") of the 3 possible strategies for implementing the category (aka. inheritance, generalization, subclass) hierarchy.
The correct "propagation" of PK from the parent to child tables is naturally enforced by straightforward foreign keys between them, but ensuring both presence and the exclusivity of the child rows is another matter. It can be done (as noted in the link above), but the added complexity is probably not worth it and I'd generally recommend handling it at the application level.
I would add a field called EmployeeId in the EmployeeTable
I'd get rid of Type
For BusinessManager table and SoftwareDeveloper for example, I'll add EmployeeId
From here, you can then proceed to create Foreign Keys from BusinessManager, SoftwareDeveloper table to Employee
To further expand on your one way with the core table is to create a surrogate key based off an identity column. This will create a unique employee id for each employee (this will help you distinguish between employees with the same name as well).
The foreign keys preserve your referential integrity. You wouldn't necessarily need EmployeeTypeId as someone else mentioned as you could filter on existence in the SoftwareDeveloper or BusinessManagers tables. The column would instead act as a cached data point for easier querying.
You have to fill in the types in the below sample code and rename the foreign keys.
create table EmployeeType(
EmployeeTypeId
, EmployeeTypeName
, constraint PK_EmployeeType primary key (EmployeeTypeId)
)
create table Employees(
EmployeeId int identity(1,1)
, Name
, Family
, EmployeeTypeId
, constraint PK_Employees primary key (EmployeeId)
, constraint FK_blahblah foreign key (EmployeeTypeId) references EmployeeType(EmployeeTypeId)
)
create table SoftwareDeveloper(
EmployeeId
, Language
, Technology
, CanDevelopWeb
, constraint FK_blahblah foreign key (EmployeeId) references Employees(EmployeeId)
)
create table BusinessManagers(
EmployeeId
, FieldOfExpertise
, MaximumContractValue
, BonusRate
, constraint FK_blahblah foreign key (EmployeeId) references Employees(EmployeeId)
)
No existing SQL engine has solutions that make life easy on you in this situation.
Your problem is discussed at fairly large in "Practical Issues in Database Management", in the chapter on "entity subtyping". Commendable reading, not only for this particular chapter.
The proper solution, from a logical design perspective, would be similar to yours, but for the "type" column in the core table. You don't need that, since you can derive the 'type' from which non-core table the employee appears in.
What you need to look at is the business rules, aka data constraints, that will ensure the overall integrity (aka consistency) of the data (of course whether any of these actually apply is something your business users, not me, should tell you) :
Each named employee must have exactly one job, and thus some job detail somewhere. iow : (1) no named employees without any job detail whatsoever and (2) no named employees with >1 job detail.
(3) All job details must be for a named employee.
Of these, (3) is the only one you can implement declaratively if you are using an SQL engine. It's just a regular FK from the non-core tables to the core table.
(1) and (2) could be defined declaratively in standard SQL, using either CREATE ASSERTION or a CHECK CONSTRAINT involving references to other tables than the one the CHECK CONSTRAINT is defined on, but neither of those constructs are supported by any SQL engine I know.
One more thing about why [including] the 'type' column is a rather poor choice to make : it changes how constraint (3) must be formulated. For example, you can no longer say "all business managers must be named employees", but instead you'd have to say "all business managers are named employees whose type is <type here>". Iow, the "regular FK" to your core table has now become a reference to a VIEW on your core table, something you might want to declare as, say,
CREATE TABLE BUSMANS ... REFERENCES (SELECT ... FROM CORE WHERE TYPE='BM');
or
CREATE VIEW BM AS (SELECT ... FROM CORE WHERE TYPE='BM');
CREATE TABLE BUSMANS ... REFERENCES BM;
Once again something SQL doesn't allow you to do.
You can use all fields in the same table, but you'll need an extra table named Employee_Type (for example) and here you have to put Developer, Business Manager, ... of course with an unique ID. So your relation will be employee_type_id in Employee table.
Using PHP or ASP you can control what field you want to show depending the employee_type_id (or text) in a drop-down menu.
You are on the right track. You can set up PK/FK relationships from the general person table to each of the specialized tables. You should add a personID to all the tables to use for the relationship as you do not want to set up a relationship on name because it cannot be a PK as it is not unique. Also names change, they are a very poor choice for an FK relationship as a name change could cause many records to need to change. It is important to use separate tables rather than one because some of those things are in a one to many relationship. A Developer for instnce may have many differnt technologies and that sort of thing should NEVER be stored in a comma delimted list.
You could also set up trigger to enforce that records can only be added to a specialty table if the main record has a particular personType. However, be wary of doing this as you wil have peopl who change roles over time. Do you want to lose the history of wha the person knew when he was a developer when he gets promoted to a manager. Then if he decides to step back down to development (A frequent occurance) you would have to recreate his old record.
I've got a table structure I'm not really certain of how to create the best way.
Basically I have two tables, tblSystemItems and tblClientItems. I have a third table that has a column that references an 'Item'. The problem is, this column needs to reference either a system item or a client item - it does not matter which. System items have keys in the 1..2^31 range while client items have keys in the range -1..-2^31, thus there will never be any collisions.
Whenever I query the items, I'm doing it through a view that does a UNION ALL between the contents of the two tables.
Thus, optimally, I'd like to make a foreign key reference the result of the view, since the view will always be the union of the two tables - while still keeping IDs unique. But I can't do this as I can't reference a view.
Now, I can just drop the foreign key, and all is well. However, I'd really like to have some referential checking and cascading delete/set null functionality. Is there any way to do this, besides triggers?
sorry for the late answer, I've been struck with a serious case of weekenditis.
As for utilizing a third table to include PKs from both client and system tables - I don't like that as that just overly complicates synchronization and still requires my app to know of the third table.
Another issue that has arisen is that I have a third table that needs to reference an item - either system or client, it doesn't matter. Having the tables separated basically means I need to have two columns, a ClientItemID and a SystemItemID, each having a constraint for each of their tables with nullability - rather ugly.
I ended up choosing a different solution. The whole issue was with easily synchronizing new system items into the tables without messing with client items, avoiding collisions and so forth.
I ended up creating just a single table, Items. Items has a bit column named "SystemItem" that defines, well, the obvious. In my development / system database, I've got the PK as an int identity(1,1). After the table has been created in the client database, the identity key is changed to (-1,-1). That means client items go in the negative while system items go in the positive.
For synchronizations I basically ignore anything with (SystemItem = 1) while synchronizing the rest using IDENTITY INSERT ON. Thus I'm able to synchronize while completely ignoring client items and avoiding collisions. I'm also able to reference just one "Items" table which covers both client and system items. The only thing to keep in mind is to fix the standard clustered key so it's descending to avoid all kinds of page restructuring when the client inserts new items (client updates vs system updates is like 99%/1%).
You can create a unique id (db generated - sequence, autoinc, etc) for the table that references items, and create two additional columns (tblSystemItemsFK and tblClientItemsFk) where you reference the system items and client items respectively - some databases allows you to have a foreign key that is nullable.
If you're using an ORM you can even easily distinguish client items and system items (this way you don't need to negative identifiers to prevent ID overlap) based on column information only.
With a little more bakcground/context it is probably easier to determine an optimal solution.
You probably need a table say tblItems that simply store all the primary keys of the two tables. Inserting items would require two steps to ensure that when an item is entered into the tblSystemItems table that the PK is entered into the tblItems table.
The third table then has a FK to tblItems. In a way tblItems is a parent of the other two items tables. To query for an Item it would be necessary to create a JOIN between tblItems, tblSystemItems and tblClientItems.
[EDIT-for comment below] If the tblSystemItems and tblClientItems control their own PK then you can still let them. You would probably insert into tblSystemItems first then insert into tblItems. When you implement an inheritance structure using a tool like Hibernate you end up with something like this.
Add a table called Items with a PK ItemiD, And a single column called ItemType = "System" or "Client" then have ClientItems table PK (named ClientItemId) and SystemItems PK (named SystemItemId) both also be FKs to Items.ItemId, (These relationships are zero to one relationships (0-1)
Then in your third table that references an item, just have it's FK constraint reference the itemId in this extra (Items) table...
If you are using stored procedures to implement inserts, just have the stored proc that inserts items insert a new record into the Items table first, and then, using the auto-generated PK value in that table insert the actual data record into either SystemItems or ClientItems (depending on which it is) as part of the same stored proc call, using the auto-generated (identity) value that the system inserted into the Items table ItemId column.
This is called "SubClassing"
I've been puzzling over your table design. I'm not certain that it is right. I realise that the third table may just be providing detail information, but I can't help thinking that the primary key is actually the one in your ITEM table and the FOREIGN keys are the ones in your system and client item tables. You'd then just need to do right outer joins from Item to the system and client item tables, and all constraints would work fine.
I have a similar situation in a database I'm using. I have a "candidate key" on each table that I call EntityID. Then, if there's a table that needs to refer to items in more than one of the other tables, I use EntityID to refer to that row. I do have an Entity table to cross reference everything (so that EntityID is the primary key of the Entity table, and all other EntityID's are FKs), but I don't find myself using the Entity table very often.