This is a 2 part question.
Question 1: I am trying to create a foreign key on a table where I need to turn off the "Check Existing Data on Creation or Re-Enabling". I know theres an option visually but I'm looking for a way to do it programmatically. Is there anyway to do this?
Question 2: I have a code table and two tables A and B that need to reference that code table. I want to have these both referenced from a relationship table but I want to able to use the same column. Can I have 2 foreign keys pointing to the same column?
Yes you can have the same column inthe parent table refer to differnt columns in multiple tables.
I do not recommend turning off checking FK on creation. If you have bad data now, you need to fix it now. Otherwise the first time someone edits one of those records it will fail the FK check then.
From Books online as to why it is a bad idea to use nocheck:
If you do not want to verify new CHECK
or FOREIGN KEY constraints against
existing data, use WITH NOCHECK. We do
not recommend doing this, except in
rare cases. The new constraint will be
evaluated in all later data updates.
Any constraint violations that are
suppressed by WITH NOCHECK when the
constraint is added may cause future
updates to fail if they update rows
with data that does not comply with
the constraint.
Related
I have a fairly simple design, as follows:
What I want to achieve in my grouping_individual_history is marked in red:
when a session is deleted, I want to cascade delete the grouping_history....
when a grouping is deleted, I just want the child field to be nullified
It seems that MSSQL will not allow me to have more than one FK that does something else than no action ... It'll complain with:
Introducing FOREIGN KEY constraint 'FK_grouping_individual_history_grouping' on table 'grouping_individual_history' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I've already read this post (https://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with-a-trigger/), although it's not quite the same scenario it seems to me.
I've tried doing a INSTEAD OF DELETE trigger on my grouping table, but it wont accept it, because in turn, my grouping table has another FK (fkSessionID) that does a cascade delete... So, the fix would be to change it all, in all affected tables with FKs. The chain is long though, and we cannot consider it.
For one thing, can someone explain to me why SQL Server is giving me the issue for this very simple scenario in the first place? I just don't understand it.
Is there another workaround I could use (besides just removing the foreign key link from my grouping_individual_history table)?
This might sound complicated, so I'll give an example.
Say, I have two tables Instructor and Class.
Instructor has a required field called PreferredClassID which has a foreign key against Class.
Class has a required field called CurrentInstructorID which is a foreign key against Instructor
Is it possible to insert a row to either of these tables?
Cause if I insert a row to Instructor, I won't be able to as I'll need to supply a PreferredClassID, but I can't create a Class row either because it needs a CurrentInstructorID.
If I can't do this, how would I solve this problem? Would I just need to make one of those fields non-required (even if business requirements specifies it really should be required?)
If you find yourself here, reevaluate your data relation model.
In this case, you could simply have a lookup table called PreferredCourse with courseId and instructorId.
This will enforce that both the course and instructor exist before adding the row to the PreferredCourse lookup. Maintaining business model requirements without bending the rules of database model requirements.
While it may seem excessive to have another table, it will prevent a whole lot of maintenance overhead in both your database procedures and jobs, and your application code. Circular references create nothing but headaches and are easily solved with small lookup tables and JOINs.
The Impaler gave an example of how to accomplish this with your current data structure. Please note, that you have to 1: make a key nullable in at least one of the tables, and then 2: Perform INSERTs in a specified order. Or, 3: disable the constraints, 4: perform INSERTS, 5: reenable constraints, 6: roll back transaction if constraints are now broken.
There is a whole lot that can go wrong, simply fix the relation model now before things get out of hand.
As long as one of those foreign keys allows a null value, you're good. So you:
Insert the row that accepts the null value first (say Instructor), with a null value on the FK. Get the ID of the inserted row.
Insert in the other table (say Class). In the FK you use the ID you got from step #1. Once inserted, you get the ID of this new row.
Update the FK on the first row (Instructor) with the ID you got from step #2.
Commit.
Alternatively, if both FKs are NOT NULL then you have a bit of a problem. The options I see for this last case are:
Use deferrable FK integrity check. Some databases do allow you to insert without checking integrity until the COMMIT happens. This is really tricky, and enabling this is looking for trouble.
Disable the FK for a short period of time. Some databases allow you to enable/disable constraints. You are not deleting them, just temporarily disabling them. If you do this, don't forget to enable them back.
Drop the constraint temporarily, while you do the insert, and the add it again. This is really a work around of last resort. Adding/Dropping constraint are DML statements and usually cannot participate in a transaction. Do this at your own peril.
Something to consider (as per user7396598's answer) is looking at how normal forms apply to your data as it fits within your relational model.
In this case, it might be worth looking at the following:
With your Instructor table, is the PreferredClassID a necessary component? Does an instructor -need- to have a preferred class, or is it okay to say "Hey, I'm creating an entry for a new instructor, I don't know their preferred class."
(if they're new, they might not have a preferred class that your school offers)
This is a case where you definitely want to have a foreign key, but it should be okay to say 'I don't necessarily know the value I want to put there.'
In a similar vein, does a Class need to have an instructor when it's created? Is it possible to create a Class that an instructor has not been assigned to yet?
Again, both of these points are really a case of 'I don't know what I want to put here, but when I do, it should be a specific instance that exists in another table.'
So I have a SQL relationship problem. Lets say I have a database where I want to keep records of information about individuals. Now I have setup a table to take on that information. Okay so far so good.
Often times duplicate information can be discovered in the table and it would be removed. A record is considered a duplicate if a particular field has the same value as another field in another row. Example: Duplicate emails.
Now I want to create another table in the database to keep track of every duplicate that is ever discovered and deleted. My first thought into this was to create a Foreign Key relationship. So I created and then connected a dupes table to my persons table. The relationship was a simple Foreign to Primary key relationship with an on delete constraint.
Now while that may have worked at first the problem arose that the dupes table was receiving records that were deleted even if they were not deleted because they were dupes. This was a problem because even if I decided to delete a person from the persons table just because I did not like them, they would stored in the dupes table anyway.
Then I thought, why not create a disposition field in the persons table and connect that as a unique or primary key to my dupes table's index foreign key. Well the problem is a unique key must have a unique value so multiple dispositions of dupe or I don't like you would not work. The other option was to make the disposition field a primary key. That has the same problem though.
What would be the right relationship for this problem?
I can think of this implementation: An on delete trigger, with a 'before delete' check. The before delete check would confirm if the record being deleted is a duplicate or not. Not sure what all RDBMS systems support such checks though.
IMO, the theoritical relationship is complicated because the record is supposed to be preserved even after the dupe is deleted.
Foreign Keys are not going to solve this problem. I discovered Triggers and their exactly what I need.
I have a table with one column source_id whose value should be the primary key of another table, though which table it is will vary from record to record. Every record must have a value for source_table that specifies the table for the source record, and a value for source_id that specifies the row in the source table.
Is there any way to accomplish this to take advantage of the DB's foreign key constraints and validation? Or will I have to move my validation logic into the application layer? Alternately, is there another design that will just let me avoid this problem?
Foreign key constraints can only reference one target table. "Conditional" foreign keys which reference a different target table based on some other field are not available in SQL. As #OMG Ponies noted in a comment below, you can have more than one foreign key on the same column, referencing more than one table, but that would mean the value of that column will have to exist in all the referenced tables. I guess this is not what you are after.
For a few possible solutions, I suggest checking out #Bill Karwin's answer to this question:
Possible to do a MySQL foreign key to one of two possible tables?
I like the "supertable" approach in general. You may also want to check out this post for another example:
MySQL - Conditional Foreign Key Constraints
I think previous answers do answer the first part of the question well. However link recommended by Daniel provides a solution only for the case when the number of referenced "source" tables is reasonably small. And the solution will not scale easily if you decide to increase the number of "source" tables.
To recommend a better strategy it would be nice to have a little more details on what the task is and if the "source" tables have anything in common that would allow to combine them.
In current structure (as far as I can infer from the question) I would reverse the relationship:
I would create a table (let's call it AllSources) that would work as a repository of all available sources with columns source_id and source_table. Both included in the primary key.
I would create foreign keys from each "source" table referencing AllSources table so that they could have only sources already registered in it.
Then I would create the table you mentioned in your question with foreign key referencing the AllSources table (not separate "source" tables).
Drawback: you will have to manage AllSources and "source" tables together ensuring that if you create a record in AllSources, you also create a corresponding record in proper "source" table, which in reality is not that hard.
Our application uses an Oracle 10g database where several primary keys are exposed to the end user. Productcodes and such. Unfortunately it's to late to do anything with this, as there are tons of reports and custom scripts out there that we do not have control over. We can't redefine the primary keys or mess up the database structure.
Now some customer want to change some of the primary key values. What they initially wanted to call P23A1 should now be called CAT23MOD1 (not a real example, but you get my meaning.)
Is there an easy way to do this? I would prefer a script of some sort, that could be parametrized to fit other tables and keys, but external tools would be acceptable if no other way exists.
The problem is presumably with the foreign keys that reference the PK. You must define the foreign keys as "deferrable initially immediate", as described in this Tom Kyte article: http://www.oracle.com/technology/oramag/oracle/03-nov/o63asktom.html
That lets you ...
Defer the constraints
Modify the parent value
Modify the child values
Commit the change
Simple.
Oops. A little googling makes it appear that, inexplicably, Oracle does not implement ON UPDATE CASCADE, only ON DELETE CASCADE. To find workarounds google ORACLE ON UPDATE CASCADE. Here's a link on Creating A Cascade Update Set of Tables in Oracle.
Original answer:
If I understand correctly, you want to change the values of data in primary key columns, not the actual constraint names of the keys themselves.
If this is true it can most easily be accomplished redefining ALL the foreign keys that reference the affected primary key constraint as ON UPDATE CASCADE. This means that when you make a change to the primary key value, the engine will automatically update all related values in foreign key tables.
Be aware that if this results in a lot of changes it could be prohibitively expensive in a production system.
If you have to do this on a live system with no DDL changes to the tables involved, then I think your only option is to (for each value of the PK that needs to be changed):
Insert into the parent table a copy of the row with the PK value replaced
For each child table, update the FK value to the new PK value
Delete the parent table row with the old PK value
If you have a list of parent tables and the PK values to be renamed, it shouldn't be too hard to write a procedure that does this - the information in USER_CONSTRAINTS can be used to get the FK-related tables for a given parent table.