MySQL delete row if there is no foreign constraint - mysql

I have two tables. One called peoples another called addresses. The peoples table has a foreign constraint on the address table (peoples.address_id = addresses.address_id) Multiple people may have the same address. There is a unique constraint on the address column on the addresses table
I came across a situation where I have to delete a person from the peoples table and the associated address on the on the addresses table IF AND ONLY IF there is no other record in the peoples table who have the same address.
How can I go about doing this in MySQL?

This should be more performant than doing a subquery:
DELETE
addresses
FROM
addresses
LEFT JOIN peoples
ON addresses.address_id = peoples.address_id
WHERE
peoples.address_id IS NULL
It should delete all addresses that aren't referenced in the peoples table. If you want to only delete the specific address in question, add something to the WHERE clause like AND addresses.address_id = '<address_id>'.
Example

You can use a NOT EXISTS clause like below
DELETE FROM addresses
WHERE NOT EXISTS
(SELECT * FROM peoples WHERE peoples.address_id=addresses.address_id)
The cons against this is it will be pretty slow as it will re-run the query for every address row that exists. Otherwise it should solve the problem.
Delete the person first. Then this query deletes if the address has no people

Related

Using Foreign Keys, but showing another column (not id) during a query in MySQL

I have a question regarding foreign keys. I have searched for the answer and was unable to location one.
I have a table 'projects' that has the column 'owner_id' which references 'managers.owner_id' as a foreign key.
Would it be possible to reference 'managers.owner_id' as a foreign key, but show the column 'managers.full_name'? When I run a SELECT query against the 'projects' table, I want to see the manager's name to come up and not the manager's id.
If it is possible, is this normally done with the SELECT command or can I configure it when I CREATE/ALTER the 'projects' table?
I am fairly new with MySQL, thank you for your time and patience!
If what I'm asking seems insane, I wouldn't mind hearing what your thoughts are or if you have any other suggestions.
You just need to join the tables and select the fields that you want e.g.
SELECT projects.project_name, managers.full_name
FROM projects
INNER JOIN managers on projects.owner_id = managers.owner_id
If there are projects where the owner_id is NULL but you still want to list it then use a LEFT JOIN instead.

Can a foreign key act as a primary key?

I'm currently designing a database structure for our team's project. I have this very question in mind currently: Is it possible to have a foreign key act as a primary key on another table?
Here are some of the tables of our system's database design:
user_accounts
students
guidance_counselors
What I wanted to happen is that the user_accounts table should contain the IDs (supposedly the login credential to the system) and passwords of both the student users and guidance counselor users. In short, the primary keys of both the students and guidance_counselors table are also the foreign key from the user_accounts table. But I am not sure if it is allowed.
Another question is: a student_rec table also exists, which requires a student_number (which is the user_id in the user_accounts table) and a guidance_counsellor_id (which is also the user_id in the user_accounts) for each of its record. If both the IDs of a student and guidance counselor come from the user_accounts table, how would I design the student_rec table? And for future reference, how do I manually write it as an SQL code?
This has been bugging me and I can't find any specific or sure answer to my questions.
Of course. This is a common technique known as supertyping tables. As in your example, the idea is that one table contains a superset of entities and has common attributes describing a general entity, and other tables contain subsets of those entities with specific attributes. It's not unlike a simple class hierarchy in object-oriented design.
For your second question, one table can have two columns which are separately foreign keys to the same other table. When the database builds the query, it joins that other table twice. To illustrate in a SQL query (not sure about MySQL syntax, I haven't used it in a long time, so this is MS SQL syntax specifically), you would give that table two distinct aliases when selecting data. Something like this:
SELECT
student_accounts.name AS student_name,
counselor_accounts.name AS counselor_name
FROM
student_rec
INNER JOIN user_accounts AS student_accounts
ON student_rec.student_number = student_accounts.user_id
INNER JOIN user_accounts AS counselor_accounts
ON student_rec.guidance_counselor_id = counselor_accounts.user_id
This essentially takes the student_rec table and combines it with the user_accounts table twice, once on each column, and assigns two different aliases when combining them so as to tell them apart.
Yes, there should be no problem. Foreign keys and primary keys are orthogonal to each other, it's fine for a column or a set of columns to be both the primary key for that table (which requires them to be unique) and also to be associated with a primary key / unique constraint in another table.

MySQL Double way Foreign Keys?

I was wondering if this kind of thing will work:
Let's say I have two tables: COMPANIES and ADDRESSES. Each company can have only one address. Simplified schema would look like this.
COMPANIES
id
address_id
name
(...)
ADDRESSES
id
first_name
street
(...)
Now, I want to add foreign key on COMPANIES.address_id -> ADDRESSES.id ON DELETE SET NULL ON UPDATE CASCADE.
But I also want the address to be deleted when the company is deleted. So the other way around it would be ADDRESSES.id -> COMPANIES.address_id ON DELETE CASCADE. Is this safe and possible?
Maybe, instead of foreign keys, using triggers could be a solution to your problem.
create trigger addr_delete
after delete on companies for each row
begin
delete from addresses where id=old.address_id
end

Is there an alternative approach to this (unallowed) UPDATE with a nested SELECT?

I have an existing table of contacts that has about 140k records in it. I am introducing a parent table (let's call them "parent_contacts") such that one parent_contact can have many contacts; but initially, parent_contacts will be seeded to have one record for every contact that currently exists in the database.
I thought I was being clever in trying something like the following, which I now understand is not allowed (assume all the necessary parent_contact records have been created ahead of time):
UPDATE contacts
SET contacts.parent_id =
(SELECT parent_contacts.id FROM parent_contacts
WHERE NOT EXISTS
(SELECT * FROM contacts AS c WHERE c.parent_id = parent_contacts.id) LIMIT 1)
(If not readily apparent, the idea here being to set the parent_id of each contact to the id of the first parent_contact that another contact isn't already linked to)
Since this particular approach is not possible, is there another way of doing this that doesn't involve executing 140k individual update statements?
FOLLOW-UP: I resolved this by introducing a temporary child_id on the parent table, which I then removed after the seeding was finished. But in the context of the original question, I think Tony's answer below sounds apt.
You seem to have done this backwards
Add Parent_id to contacts (no constraint yet!)
Update Contacts filling Parent_id with a unique number.
Create ParentContracts, Don't put Identity in or Primary key.
The backfill ParentContacts with a Insert into ParentContacts Select Parent_id, .... From Contacts
Add the Identity (don't forget seed to next value) and Primary key to ParentContacts
Add the foreign key constraint to Contacts.
Nice easy steps and easy to check each one instead of this whole cloth manouvre you are trying now.

Hard task with foreign keys

I have two related tables, quite common case: Clients(CID, name, surname) and Visits(VID, VCID, dateOfVisit) - VCID is the Client ID. How to manipulate foreign keys, when I want database to delete Visits records relative to some Client, who I DELETE, and to delete Client, when I DELETE the last Visit of that client left?
I would suggest you do a soft delete, so you can keep your Visits record.
Soft delete means you just add an extra field is_active default it to true, and when you delete the record flip it to false.
you can automatically delete visits for deleted clients by using "on delete cascade". something like:
create table clients (id integer primary key);
create table visits (id integer primary key,
client integer,
foreign key (client) references clients(id)
on delete cascade);
but going the other way (automatically deleting clients with no visits) is more difficult.
you can manually delete clients with no visits by executing:
delete from clients where id in
(select cid from
(select clients.id as cid, visits.id as vid
from clients left join visits on (clients.id = visits.client))
where vid is null);
(and maybe there's something simpler?). so either run that every now and then, or create a trigger that runs it when something is deleted from visits (although if you're going to add a trigger it could use the deletion info to do something smarter).
or maybe someone with more time/energy than me can write an answer with the trigger...?
(as others have said, automatically deleting clients is pretty drastic behaviour - it's not something you'd normally want to do in a production system - apart from anything else, if clients need more visits they are going to be pretty annoyed if they have to enter their details again...)