Performing SQL Query to Remove Unused Users from a Database - sql

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

Related

How to insert into a table if it violates a foreign key constraint

I am working on a script that moves data between two databases.
I am moving a table of Phone Numbers. Each Phone Number is for a user.
The problem is that each Phone Number entry references a User with a User ID. Some of these users do not exist anymore, so when I try to insert, it returns a foreign key constraint violation.
insert or update on table "phone_numbers" violates foreign key constraint "fk3843uenfej83jf32wde"
user_id = 10 is not present in table users
However, I can't go and delete each single user reference as there are thousands of references.
So what would be the best way to approach it?
Should I simply remove the foreign key constraint?
Phone numbers that belong to non existent users are termed “orphaned” data.
Either clean up orphaned data in the source data (orphaned data shouldn’t exist):
delete from phone_number
where not exists (select * from user where id = user_id)
Or don’t select them when exporting:
select p.*
from phone_number p
join user u on u.id = p.user_id
I would not remove the constraint, as it can have impacts on other things (application ? report ? Whatever).
So the question is wHhat do you need ?
Insert all ph. numbers including the ones without users
Insert only ph. numbers with users associated
In any case load your data to a 'temp' table call, temp_phones, without any constraint.
In case 1 migrate data to phone_numbers making userid = null if the user is not present anymore. You can do it with an "easy" query
In case 2 migrate data to phone_numbers only when the userid of the record is found in your user table, also this can be done with a query
You can perform both processes also after having migrate the data. In this case you should disable\remove the constraint, update the userid according to the proposed rules, then recreate the constraint

How to migrate IDs from JOIN table into foreign key column in PostgreSQL

I have the following tables in my PostgreSQL database:
CREATE TABLE "User" (
id VARCHAR(25) PRIMARY KEY NOT NULL
);
CREATE TABLE "Post" (
id VARCHAR(25) PRIMARY KEY NOT NULL
);
CREATE TABLE "_PostToUser" (
"A" VARCHAR(25) NOT NULL REFERENCES "Post"(id) ON DELETE CASCADE,
"B" VARCHAR(25) NOT NULL REFERENCES "User"(id) ON DELETE CASCADE
);
The relationship between User and Post right now is managed via the _PostToUser JOIN table.
However, I want to get rid of this extra JOIN table and simply have a foreign key reference from Post to User, so I ran this query to create the foreign key:
ALTER TABLE "Post" ADD COLUMN "authorId" VARCHAR(25);
ALTER TABLE "Post"
ADD CONSTRAINT fk_author
FOREIGN KEY ("authorId")
REFERENCES "User"("id");
Now, I'm wondering what SQL query I need to run in order to migrate the data from the JOIN table to the new authorId column? If I understand correctly, I need a query that reads all the rows from the _PostToUser relation table and for each row:
Finds the respective Post record by looking up the value from column A
Inserts the value from column B as the value for authorId into that Post record
Edit: As mentioned by #Nick in the comments, I should have clarified that I indeed want to change the relationship from m-n and restrict it to 1-n: One post can at most have one author. One author/user can write many posts.
Your current design is already correct, and uses a proper junction table to store the relationships between users and their posts. In this design, a given relationship only requires storing two ID values, which is lean. Going in the direction you suggest is denormalizing your data, and will result in data duplication. To see why this is the case, your suggested table will now store metadata from the author table. This metadata will, in principle, be repetitive, since a given author's metadata would be the same for every record in the new posts table.
Instead, I suggest indexing the junction table:
CREATE INDEX idx ON "_PostToUser" (B, A);
As an example, the above index should help the following query:
SELECT u.*, p.*
FROM "User" u
INNER JOIN "_PostToUser" pu ON pu.B = u.id -- index helps here
INNER JOIN "Post" p ON p.id = pu.A; -- Post.id is already a primary key
The join to the lookup table should now be faster, because Postgres can use the index take a given user id value and try to find the corresponding A value on the other side of the junction.
As long as you are happy to restrict the relationship between Posts and Users to N:1, and you only store a foreign key to User in Post, then I think what you are doing is fine. The query to update the Post table would be:
UPDATE "Post" p
SET "authorId" = pu."B"
FROM "_PostToUser" pu
WHERE pu."A" = p."id"
Demo on dbfiddle

How to delete records from two different tables that are linked with FK? 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.

Delete based on primary key from associative table - Error

I'm trying to delete 1 record based on primary key from (3) tables.
Here is the statement that I'm using
DELETE FROM CUSTOMER
WHERE EXISTS
( SELECT MERCHANTNAME
FROM CREDITCARD
WHERE MERCHANTNAME = 'VISA');
Deleting record of a customer with a VISA from customer table.
Here is the error that I'm getting
ORA-02292: integrity constraint
(PLATINUMAUTOGROUP.CDRIVERLICENSENUM_FK) violated - child record found
I'm guessing CDRIVERLICENSENUM is the foreign key in the 3rd table that I have. How do I go about this? Is it possible to delete 1 record from 3 tables in 1 statement?
the three tables are
customer / customer_creditcard / creditcard
You need to delete the records in the foreing tables before deleting the record in the primary table.
But perhaps your delete command in the CUSTOMER table might be wrong, because if the EXISTS command return true, all records will be deleted from the CUSTOMER table. Check if this is the result you expect.

foreign key: conflicted with foreign key constraint

so i have 2 tables related to each other with an fk
appointment
{id, dept.id, datetime, sometable.id, sometableagain.id}
task
{id, appointment.id, deptlead.id, taskname}
deptlead
{id, name}
so i had to alter the appointment table to another foreignkey from another table. so i dropped the keys (task_appointment_fk, appointment_sometable_fk, appointment_sometableagain_fk) altered the table to add the new field and added again everything. the last two got added with no problems. while the other one (task_appointment_fk) kept giving me a this message :
"ALTER TABLE statement conflicted with the Forien Key Constraint "dept_appointment". The Cconflict occurred in the database "MyDb" , table "appointment", column "id"
so i found some solutions that states that there might be some rows on the task that has a appointmentid value that does not exist on the appointment table. so i tried inserting rows that would have the same value right. still gives me the same thing. the thing is , i want to delete the rows from the task to make it easier but doing that i have to drop all the fks again and do the same thing all over on the other tables, and i have a lot of other tables..
need some advice.
thanks!!
You can write a query to see which values in foreign key table does not have a matching key record in primary key table .If values are there then try to delete them .
select * from [task] a
left join [appointment] b
on a.appointment_id = b.id