I have two tables, Results and ComparedResults.
ComparedResults has two columns which reference the primary key of the Results table.
My problem is that if a record in Results is deleted, I wish to delete all records in ComparedResults which reference the deleted record, regardless of whether it's one column or the other (and the columns may reference the same Results row).
A row in Results may deleted directly or through cascade delete caused by deleting in a third table.
Googling this could indicate that I need to disable cascade delete and rewrite all cascade deletes to use triggers instead. Is that REALLY nessesary? I'd be prepared to do much restructuring of the database to avoid this, as my main area is OO programming, and databases should 'just work'. It is hard to see, however, how a restructuring could help as I would just move the problem around... Or am I missing something?
I am also a bit at a loss as to why my initial construct should even be a problem for the Sql Server?!
Any comments welcome and much appreciated!
Anders, Denmark
Would it be possible to split the ComparedResults into two tables?
Edit:
You could then use a View to gather the results for showing? Or a join between the two tables?
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)?
Have a difficulties with query. I am new in SQL so trying to make better query for deleting a trip.
Does anyone know how to make it easier in one query?
Thank you
If you want to perform a delete across all your tables in one single statement and you happen to be using a SQL Engine that support CASCADE you might be in luck.
From your sample query it appears that all tables share a chained PK\FK relationship.
Venues through checkpoint_id
Checkpoints through day_id
Days through trip_id
Trips (at the top)
My inference is that TRIP is the top of the chain. If you specifically define the PK\FK relationship 'CONSTRAINT' which you should for good DB design, then on each table, you can alter the constraint with the option CASCADE. With that done simply deleting a row from trip will automatically delete all other rows below via the defined relationship.
In other words, with CASCADE and a SQL ENGINE that supports it - you can simply issue the last delete statement and the rest is implied.
DELETE FROM trips WHERE id = 27
Check table constraint options here. Pay attention to CASCADE definition. Similar question with explanations here.
I have the following case, what I would like to do is, if I delete a row from DashboardKpi or DashboardGrid, the corresponding record on ComponentProperty should be deleted as well.
Is this possible? So far, I was only able to do the other way around, if I delete a ComponentProperty, the corresponding DashboardKpi or DashboardGrid gets deleted, but this is definitely not what I want.
ER Diagram
Any suggestion on how can I do?
Delete cascade wont help you. As you mentioned when you delete row cascade it will delete all other rows in other tables those referencing to original deleting row, after that your original row.
The reason of logic is that rows in ComponentProperty can be without DashboardKpi or DashboardGrid, but rows in DashboardKpi or DashboardGrid (if they have reference to ComponentProperty) cant not cause they depend on ComponentProperty.
You could solve your problem in different ways depending on your DBMS. Common to most of them is to use procedures or triggers. If you use Postgresql then you can use ON DELETE rule as well.
Let's say I have a Customers table with columns ID and CompanyId.
I need to delete some companies from the DB.
The problem is, the table has a lot of child tables and those tables also has a lot of child tables and so on...
Just to clarify, all the relationships are with constraints.
How can I accomplish that ?
Thanks.
EDIT: Notice that what i'm trying to do is a one time operation.
Whether i will change the constraints or add triggers or anything like that, I'm planning on removing it in the end.
The inbuilt solution to this problem is to set up your FK constraints with ON DELETE CASCADE.
However many people (myself included) are somewhat uneasy about doing this as a mistaken delete will silently propagate through the database.
Here are three ways:
Use a stored procedure to delete child first then up to the parent row in a transation.
I personally wouldn't make it dynamic and would have a specific "DeleteCompany" proc. Your may need a rule that such as "no delete if sales > 100 million" that needs checked
CASCADE DELETEs on your foreign keys
This can be tricky if you have multiple cascade paths, but simple otherwise
INSTEAD OF trigger
An INSTEAD OF trigger is like a stored procedure in operation. Note: You'll get an FK violation before an AFTER trigger fires
Personally, I'd use a stored proc so I have explicit deletes. The effect is the same as cascading FKs but more obvious.
For SQL Server 2008, this is the solution:
Generate Delete Statement From Foreign Key Relationships in SQL 2008?
With this solution, you can easily find the correct sequence of DELETE respecting, in the meantime, the foreign keys' relationships.
If you are interested in this theme, you can read also the ORACLE PL/SQL solution:
How to generate DELETE statements in PL/SQL, based on the tables FK relations?
I have a fairly huge database with a master table with a single column GUID (custom GUID like algorithm) as primary key and 8 child tables that have foreign key relationships with this GUID column. All the tables have approximately 3-8 million records. None of these tables have any BLOB/CLOB/TEXT or any other fancy data types just normal numbers, varchars, dates, and timestamps (about 15-45 columns in each table). No partitions or other indexes other than the primary and foreign keys.
Now, the custom GUID algorithm has changed and though there are no collisions I would like to migrate all the old data to use GUIDs generated using the new algorithm. No other columns need to be changed. Number one priority is data integrity and performance is secondary.
Some of the possible solutions that I could think of were (as you will probably notice they all revolve around one idea only)
add new column ngu_id and populate with new gu_id; disable constraints; update child tables with ngu_id as gu_id; renaname ngu_id->gu_id; re-enable constraints
read one master record and its dependent child records from child tables; insert into the same table with new gu_id; remove all records with old gu_ids
drop constraints; add a trigger to the master table such that all the child tables are updated; start updating old gu_id's with new new gu_ids; re-enable constraints
add a trigger to the master table such that all the child tables are updated; start updating old gu_id's with new new gu_ids
create new column ngu_ids on all master and child tables; create foreign key constraints on ngu_id columns; add update trigger to the master table to cascade values to child tables; insert new gu_id values into ngu_id column; remove old foreign key constraints based on gu_id; remove gu_id column and rename ngu_id to gu_id; recreate constraints if necessary;
use on update cascade if available?
My questions are:
Is there a better way? (Can't burrow my head in the sand, gotta do this)
What is the most suitable way to do this? (I've to do this in Oracle, SQL server and mysql4 so, vendor-specific hacks are welcome)
What are the typical points of failure for such an exercise and how to minimize them?
If you are with me so far, thank you and hope you can help :)
Your ideas should work. the first is probably the way I would use. Some cautions and things to think about when doing this:
Do not do this unless you have a current backup.
I would leave both values in the main table. That way if you ever have to figure out from some old paperwork which record you need to access, you can do it.
Take the database down for maintenance while you do this and put it in single user mode. The very last thing you need while doing something like this is a user attempting to make changes while you are in midstream. Of course, the first action once in single user mode is the above-mentioned backup. You probably should schedule the downtime for some time when the usage is lightest.
Test on dev first! This should also give you an idea as to how long you will need to close production for. Also, you can try several methods to see which is the fastest.
Be sure to communicate in advance to users that the database will be going down at the scheduled time for maintenance and when they can expect to have it be available again. Make sure the timing is ok. It really makes people mad when they plan to stay late to run the quarterly reports and the database is not available and they didn't know it.
There are a fairly large number of records, you might want to run the updates of the child tables in batches (one reason not to use cascading updates). This can be faster than trying to update 5 million records with one update. However, don't try to update one record at a time or you will still be here next year doing this task.
Drop indexes on the GUID field in all the tables and recreate after you are done. This should improve the performance of the change.
Create a new table with the old and the new pk values in it. Place unique constraints on both columns to ensure you haven't broken anything so far.
Disable constraints.
Run an updates against all the tables to modify the old value to the new value.
Enable the PK, then enable the FK's.
It's difficult to say what the "best" or "most suitable" approach is as you have not described what you are looking for in a solution. For example, do the tables need to be available for query while you are migrating to new IDs? Do they need to be available for concurrent modification? Is it important to complete the migration as fast as possible? Is it important to minimize the space used for migration?
Having said that, I would prefer #1 over your other ideas, assuming they all met your requirements.
Anything that involves a trigger to update the child tables seems error-prone and over complicated and likely will not perform as well as #1.
Is it safe to assume that new IDs will never collide with old IDs? If not, solutions based on updating the IDs one at a time will have to worry about collisions -- this will get messy in a hurry.
Have you considered using CREATE TABLE AS SELECT (CTAS) to populate new tables with the new IDs? You'll be making a copy of your existing tables and this will require additional space, however it is likely to be faster than updating the existing tables in place. The idea is: (i) use CTAS to create new tables with new IDs in place of the old, (ii) create indexes and constraints as appropriate on the new tables, (iii) drop the old tables, (iv) rename the new tables to the old names.
In fact, it depend on your RDBMS.
Using Oracle, the simpliest choice is to make all of the foreign key constraints "deferred" (check on commit), perform updates in a single transaction, then commit.