I've read some of Bill Karwin's answers about single table inheritance and think this approach would be good for the setup I am considering:
Playlist
--------
id AUTO_INCREMENT
title
TeamPlaylist
------------
id REFERENCES Playlist.id
teamId REFERENCES Team.id
UserPlaylist
------------
id REFERENCES Playlist.id
userId REFERENCES User.id
PlaylistVideo
-------------
id
playlistId REFERENCES Playlist.id
videoId REFERENCES Video.id
All the CASCADE options are set to DELETE which will work correctly for when a Playlist is deleted, however, what happens if a User or Team is deleted?
ie. If a User is deleted, the rows in UserPlaylist will be deleted but the referenced rows in Playlist and PlaylistVideo will remain. I thought about enforcing this as a TRIGGER AFTER DELETE but there is no way of knowing if the delete request came about because the Playlist was deleted or if the User was deleted.
What is the best way to enforce integrity in this situation?
Edit (Provided ERD)
What you can do is implement triggers on your Users and Team tables that execute whenever rows get deleted from either:
User table:
DELIMITER $$
CREATE TRIGGER user_playlist_delete
BEFORE DELETE ON User FOR EACH ROW
BEGIN
DELETE a FROM Playlist a
INNER JOIN UserPlaylist b ON a.id = b.id AND b.userId = OLD.id;
END$$
DELIMITER ;
Team table:
DELIMITER $$
CREATE TRIGGER team_playlist_delete
BEFORE DELETE ON Team FOR EACH ROW
BEGIN
DELETE a FROM Playlist a
INNER JOIN TeamPlaylist b ON a.id = b.id AND b.teamId = OLD.id;
END$$
DELIMITER ;
What these triggers will do is each time a record is deleted from one of these tables, a DELETE operation will automatically execute on the Playlists table using the id that's about to be deleted (via an inner join).
I have tested this and it works great.
OK I see what you want here... what you want to do is run a query like
DELETE FROM playlist
WHERE id
NOT IN (
SELECT id
FROM UserPlayList
UNION
SELECT id
FROM TeamPlayList
)
after either a row is deleted from either users or teams
In my view, the problem is that your User and Team tables are the ones that should have a supertype table (such as Party), not the Playlist tables.
As you've pointed out, doing your "table inheritance" on playlists comes with penalties when trying to figure out what to delete. All those problems go away when you move the inheritance up to the user/team level.
You can see this answer for more detail about supertyping/subtyping.
I'm sorry to not supply code as I don't know the MySQL syntax by heart.
The basic concept is that the supertype table allows you to implement a database kind of polymorphism. When the table you're working with needs to link to any one of a group of subtypes, you just make the FK point to the supertype instead, and this automatically gets you the desired "only a one of these at a time" business constraint. The super type has a "one-to-zero-or-one" relationship with each of the subtype tables, and each subtype table uses the same value in its PK as the PK from the supertype table.
In your database, by having just one Playlist table with an FK to Party (PartyID), you have easily enforced your business rule at the database level without triggers.
The answer by Zane Bien is quite obvious & superb.But I have an idea for doing this without use of trigger because trigger has many problems.
Are you using any programming language ? If yes then,
Use a single transaction and make your database auto commit false
write a delete query for the referenced rows in Playlist and PlaylistVideo . Manually you have to write this query first by using that reference id(with where condition) and run it.
Now prepare another query for your main task i.e. delete the User, and the rows in UserPlaylist will be deleted automatically ( due to CASCADE DELETE option).Now run your second query and commit.
Finally make your transaction auto commit true.
It is working successfully, hope it will helpful.
Related
I have a products table, a category table and a join-table to link the two, named prod_cat_join. I am attempting to periodically delete all categories not associated with a product with a specific team ID. Let it be known that category has a trigger that deletes all joins in prod_cat_join associated with a category after a category is deleted. Is there a single SQL query that can be invoked to delete all categories not associated with a product and is associated with a specific team ID without running into a function/trigger error?
The trigger goes as follows:
CREATE DEFINER=`dbe1`#`%` TRIGGER `db`.`category_AFTER_DELETE` AFTER DELETE ON `category` FOR EACH ROW
BEGIN
delete from prod_cat_join where prod_cat_join.categoryID = ID;
END
And my query that is periodically called to delete all categories not associated with a product is:
DELETE db.category FROM db.category
LEFT JOIN db.prod_cat_join AS join_cat
ON join_cat.categoryID = db.category.ID
WHERE join_cat.productID IS NULL AND db.category.teamID = '1234'
I know it's possible to query all categories not associated with a product then delete the returned set of unassociated categories. But that seems inefficient as it requires two queries. Given our current setup is it possible to bypass the trigger to run the aforementioned query? Or is there some other single query method to delete the unassociated categories? And would putting an after delete trigger in prod_cat_join to delete all categories who no longer have an association to a join be effective? Would that not end up invoking the above-mentioned deletion trigger?
Guidance would be appreciated, thank you.
I have two tables one called users and another called profiles. Each of these tables has a column named user_id. What I want to do is when I insert a new user into the users table, I want to automatically copy over their new user_id in the users table to the profiles table. I don't want to use the UPDATE clause because then I would have to call that every time I add a new user. I thought that relations would achieve what I am trying to do so I made the user_id from profiles reference the user_id from users and it still doesn't update automatically. What should I do? And what is the point of relations in the first place if they don't update your columns automatically?
This is probably a design error. If rows in these two tables always exist with the same IDs, they should probably be a single table.
The foreign key you've created only guarantees that every row that exists in profiles must have the same ID as a row in users. It does not cause those rows to be created -- it just means that if you try to create a row with an ID that doesn't match, the database will throw an error.
That all being said, it's possible to create a trigger to do what you're describing:
CREATE TRIGGER user_insert_creates_profile
AFTER INSERT ON users
FOR EACH ROW
INSERT INTO profile (user_id) VALUES (NEW.user_id);
But it's probably better to reconsider your design, or to do the insert in your application. Triggers are best avoided.
(My website is built using PHP and MySQL.)
My DB structure for users is mainly composed of 2 tables: "doctors" and "clients".
However, in order to integrate a chat system, I need to create a third table, namely 'chat_users', which combines both doctors and clients: fields of 'chat_users' table are
userid (unique integer),
username,
type (0:client, 1:doctor),
refid (id of the user in the associated clients or doctors table)
But I do not want to insert/delete/update this table manually each time a client or doctor is inserted/updated/deleted. I heard about cascading table some time ago...
What would be the best way performance-wise to do so? How can I achieve it?
I'm not sure you'll consider this an "answer", but may I comment on your database architecture?
You will be much happier in the long run having the following tables:
user_account: (ua_id, ua_email, ua_username, ua_password, etc.)
doctor: (d_id, ua_id, etc.)
customer: (c_id, ua_id, etc.)
In other words, have your relation going the other way. Then if you would like to be able to delete a doctor or customer by simply deleting the user_account, you can add the following relational constraint:
ALTER TABLE `doctor`
ADD CONSTRAINT `doctor_fk_user_account` FOREIGN KEY (`ua_id`) REFERENCES `user_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `customer`
ADD CONSTRAINT `customer_fk_user_account` FOREIGN KEY (`ua_id`) REFERENCES `user_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
What you need is an AFTER INSERT Trigger. This would allow you to create new users. In case if you want it to be updated on update and deleted on delete of the original record then you need those triggers as well.
CREATE TRIGGER `chat_users_insert` AFTER INSERT ON `doctors`
FOR EACH ROW BEGIN
INSERT INTO `chat_users` SET user_id= NEW.id;
END;
The above would insert a record and set the value of id. http://dev.mysql.com/doc/refman/5.0/en/trigger-syntax.html can give you exact syntax. Let me know if you need any specific clarifications.
I know, this is not exactly an answer to your question but what about using an old fashioned view instead? This would save you from storing redundant data altogether.
CREATE VIEW chat_users AS
SELECT drid uid, drid userid, drname username, 0 FROM doctors
UNION ALL
SELECT clid+100000, clid, clname, 1 FROM clients
This view will have unique uids only if you don't have more than 100000 doctors in your table (otherwise choose a higher offset). The advantage of this approach would be that there is no dependent table data to maintain.
"I do not want to insert/delete/update this table manually each time a client or doctor is inserted/updated/deleted."
Why are you fretting about this? Just do it. You have application requirements that mandate it, so unless you can figure out how to unify your client and doctor tables, you will need a third that relates to your chat function.
The difficulty of adding this in an application framework is almost zero, it's just the case of creating an additional record when a client or doctor is created, and removing it when their respective record is deleted.
The other answers here propose using views or triggers to obscure what's really happening. This is generally a bad idea, it means your application isn't in charge of its own data, basically handing over control of certain application logic functions to the database itself.
The best solution is often the most obvious, as that leads to fewer surprises in the future.
I could have the wording 'wrong' here (new to mysql) but i hope i've explained what I'm trying to do well.
i have a table called submissions with 4 fields submitId, studentName, submitDate, status
status refers to whether they got admitted or not.
submitId is auto incremented
Now i wanted to create another table based on that, but only if the status is true, this new table would have the submitId, studentName, submitDate, plus additional fields.
this table would have a new auto increment studentId
how would i do that so it automatically updates any new entry to the first table on the second table, but not overwrite the additional content of table 2.
i thought of using a view, but u can't add new columns on the view, right?
do i have the logic wrong here or what are the options, could someone please point me in the right direction, thanks
You want to use a trigger. See:
http://dev.mysql.com/doc/refman/5.6/en/triggers.html
You can create the trigger so that when a row is inserted into submissions with status=true, it inserts a row into your new student table. It would look something like this:
delimiter //
CREATE TRIGGER sub_ins_check AFTER INSERT ON submissions
FOR EACH ROW
BEGIN
IF NEW.status = 1 THEN
INSERT INTO your_new_table (student_name, submit_date, submit_id) VALUES (NEW.student_name, NEW.submit_date, NEW.submit_id);
END IF;
END;//
delimiter ;
Then create another trigger so that when a row is updated in submissions, it updates the row with the same submit_id in your new table, like this:
delimiter //
CREATE TRIGGER sub_ins_check AFTER UPDATE ON submissions
FOR EACH ROW
BEGIN
IF NEW.status = 1 THEN
UPDATE your_new_table SET student_name = NEW.student_name, submit_date = NEW.submit_date, (etc..)) WHERE submit_id = NEW.submit_id;
END IF;
END;//
delimiter ;
I think your data model is wrong. Remember that student my have several submissions and there may be number of students with the same name. You must distinguish them.
Is there any reason you want to duplicate student data in both tables?
If you're new to SQL, read about table normalization first.
In you Student table you should store students data and in Submission table - guess what :)
The first thing you need to do is step back and consider the problem from the perspective of logical entities.
You've identified two entities that I can see - student and submission. "Student" is an obvious entity which you may choose NOT to store in your database, but it may be better that you do. "Submission" is a more obvious one, but what is not so obvious is what a "submission" actually is. Let's assume it is some sort of transaction.
You've mentioned a "second table" without a clear indication of its role in the solution. The best I could infer is that it is meant to be some sort of historical trail on activity against a submission. If true, then I could envision a physical schema sketched out as follows:
Student table. One row per student; contains information about a student (name, id, etc.). Primary key would probably be an auto-incremented number.
Submission table. One row per submission; includes a foreign key to the student table (referencing the primary key); has its own primary key, also an auto-incremented integer. Also has triggers defined for INSERT and UPDATE. INSERT trigger causes INSERT into submission_log table; UPDATE trigger also causes INSERT into submission_log table.
Submission_log table. One row per event against the submission table. Includes all the fields of submission plus its own primary key (submission's primary key is a foreign key here), and includes an indicator field for whether it represents an insert or update on submission.
The purpose of the above is not to supply a solution, or even the framework of a solution, but rather to get you to think in terms of the logical entities you want to model in your solution, and their relationships to each other. When you have a clear picture of the logical model, it will be much easier to determine what tables are required, what their roles are, and how they will be used and how they will relate to each other.
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.