How to delete records from two different tables that are linked with FK? SQL - sql

I have two tables City and Buildings in my database. They are linked with city_number that is Primary Key in City table and Foreign Key in Buildings table. If user wants to delete record from City table I want to remove any records from the Buildings table that is tied to that City. I use unique auto incremented id passed through the argument to remove these records. My SQL Query looks like this:
DELETE C.*, B.*
FROM City AS C
INNER JOIN Buildings AS B
ON C.c_number = B.b_district
WHERE D.c_id = 'some id example: 107';
Query above won't work sicne SQL allow only records from one table to be removed with INNER JOIN so i will have to use two separate DELETE statements like this:
DELETE
FROM City
WHERE c_id = '107'
DELETE
FROM Buildings
WHERE b_city = 'city that is tied to unique id 107'
My question is, what is the best practice to remove records that are tied in two tables? Since I have to use two separate SQL statements, should I pass City and then delete record(s) from Buildings table? or Should I create another query that will pull City from City table based on unique id and then remove record(s) from Buildings? If anyone knows better way to do this please let me know.

I believe the easiest way to accomplish your goal would be to set up your foreign key with ON DELETE CASCADE. That way, whenever a row in the parent table is deleted, any related rows in the child table will be deleted automatically.
Here is an example of a way to alter a table in order to create a foreign key with ON DELETE CASCADE:
ALTER TABLE child_table
ADD CONSTRAINT fk_name
FOREIGN KEY (child_col1, child_col2, ... child_col_n)
REFERENCES parent_table (parent_col1, parent_col2, ... parent_col_n)
ON DELETE CASCADE;
In your case, the child table would be Buildings and the parent table would be City. It sounds like you would have just city_number for the column. You'll have to fill in the name of your foreign key.

Like Shannon mentioned, you can use ON DELETE CASCADE to delete data from parent and child tables.
Here is a working example:
http://sqlfiddle.com/#!18/f5860/10

Without writing out the code, here's what I would do:
Select all the ids to be deleted for buildings belonging to a city
Delete all the buildings
Delete the city
Put it in a stored procedure
Re-usable, self-contained, and clear.
This is a violation of SRP however, let me know if you care about that and I'll post a SRP based SQL solution.

Related

Performing SQL Query to Remove Unused Users from a Database

I'm currently working with a database that consists of a users table, a permissions table, a set of documents-related tables, and several miscellaneous tables that have foreign key dependencies on rows in the user table.
I'm trying to remove all user entries from the 'Users' table that meet the following criteria:
Not referenced by an entry in one of the documents tables.
Not referenced by an entry in the permissions table.
Contains a null value in the 'Customer ID' column of the User row.
I'm able to create a query that gets all users, which looks like this:
SELECT id
INTO MyTableVar
FROM Users
WHERE
(NOT EXISTS (SELECT Author_Id FROM ItemInstances_DocumentInstance
WHERE Users.Id = ItemInstances_DocumentInstance.Author_Id)
AND NOT EXISTS (SELECT CompletedBy_Id FROM TaskInstanceUser
WHERE Users.Id = TaskInstanceUser.CompletedBy_Id)
AND Cust_Id IS NULL
AND Id > 4)
SELECT *
FROM MyTableVar
This query gets all of Id's of users that I want to remove, but I get an error when I try to delete these entries
The DELETE statement conflicted with the REFERENCE constraint "FK_MessageUser_User.
I'm stumped as to how I should use the ID's I've queried to remove entries in the MessageUser_User table that correspond to users I want to delete. I feel like this should be easy, but I can't figure out a way to do it with SQL syntax.
PS: I'd also appreciate some feedback on how I wrote what I have so far for my query. I'd love to know what I could do to make it cleaner. I'm new to SQL and need all the help I can get.
I'm guessing that the table with the Foreign Key does not have ON DELETE CASCADE which you can read about here.
If you have the ability to alter constraints on your table, you can do this, which will permit the referencing table to automatically delete records that reference a deleted row from the main table.
ALTER TABLE MessageUser_User DROP
CONSTRAINT FK_MessageUser_User;
ALTER TABLE MessageUser_User ADD
CONSTRAINT FK_MessageUser_User
FOREIGN KEY (<<IdColumnName>>)
REFERENCES Users (Id)
ON DELETE CASCADE;
Otherwise, you can use a separate query to delete from MessageUser_User where it contains the IDs you want to delete in it's foreign key column:
DELETE FROM MessageUser_User WHERE ID IN (SELECT ID FROM MyTableVar );
Regarding the style of your delete query - I usually prefer to do left joins then delete the records where there is a null in the right table(s):
SELECT id
INTO MyTableVar
FROM Users
LEFT JOIN ItemInstances_DocumentInstance ON Author_Id = Users.Id
LEFT JOIN TastInstanceUser ON CompletedBy_Id = Users.Id
WHERE
Author_Id IS NULL
AND CompletedBy_Id IS NULL
AND Cust_Id IS NULL
AND Id > 4

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

Create SQL one-to-many relationship with itself

I'm trying to create a one-to-many relationship on a table A with it's own records.
I thought I could just create a second table B, with two foreign keys linking to table A. It appears that this is not sufficient, as LINQ-to-SQL generated a one-to-one relationship. What am I missing?
Some context: table A contains tags, and tags can be sub tags of tags.
Edit: Maybe I need a many-to-many relationship?
Assuming you have a table with the following columns itemid and MasterID
in the column masterid you have the itemid of the item it links to
select a.Itemid,b.itemid from table1 as a inner join table1 as b on a.Itemid=b.MasterID
Above query will give you a result like this :
a_itemid b_itemid
3020079 3020334
3020079 3020335
3045396 3045397
3045396 3045398
3045396 3045401
3020079 3577149
SQL server won't let you create a foreign key for the table referencing itself with ON DELETE CASCADE or ON UPDATE CASCADE since it may cause cycles or multiple cascade paths. But you can create foreign key with ON DELETE NO ACTION and ON UPDATE NO ACTION.
ALTER TABLE tagtablename ADD FOREIGN KEY ([parent_id]) REFERENCES [tagtablename] ([id]) ON DELETE NO ACTION ON UPDATE NO ACTION
You also need to make the two keys in your link table B, the primary key of this table.
Also, Linq2SQL seems to generate properties based on the names of your keys. This resulted in some odd names for me.

SQL Server Relation table parent->child and cycle cascade

I have a sql table (let's call it Person) like, for example :
TABLE Person
Id Firstname Lastname
I want to make relations beetwen somme of this person (parent/child relation) so I built another table (let's call it Person_Relation) like that :
TABLE Person_Relation
Id_person_parent Id_person_child
I made a constraint to avoid the case where parent = child (it would be awkward !) and now i try to make foreign key beetwen table Person and table Person_Relation.I am currently able to make one foreign key, but when i try to set the second I get a : may cause cycles or multiple cascade paths error.
Knowing that I would keep the 'Delete cascade' to automatically delete links in Person_Relation table when an entry in Person is deleted, is there any clean solution to do that ?
Thank you in advance.
SQL Server won't let you create multiple cascade paths that could theoretically conflict. For more on that, see this answer.
One way to still achieve your goal is to use a trigger in place of a foreign key with a cascade action.
CREATE TRIGGER dbo.Person_Delete
ON dbo.Person
FOR DELETE
AS
DELETE dbo.Person_Relation
WHERE Id_person_parent IN (SELECT Id FROM deleted)
OR Id_person_child IN (SELECT Id FROM deleted);

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