Is there any option (or even DB client) to require a confirmation when deleting a row for CASCADE deletes. For example:
DELETE FROM catalogs WHERE id=1
Warning: this will also DELETE CASCADE 92,358 products and 142 catalog_histories. Are you sure? (If so, temporarily turn on ALLOW_CASCADE_DELETES or add WITH CASCADES to query)
I will prefer a solution that doesn't require following particular rules in db schema and works with MySQL. (Alternatively if there is a way to force FK deletes while leaving CASCADE off schema in general then this will also be a good option.)
You can't do that in mysql
i tried it a bit https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b6575f084b486025a66f6d52a4df9893
but the foreign key restaont has to be enable before you start to delete the row like in the example
So you must run two codes one with SET FOREIGN_KEY_CHECKS
CREATE TABLE catalogs(id int PRIMARY KEY);
INSERT INTO catalogs VALUES (1),(2);
CREATE TABLE catalogs_sub(id int, cat_id int,
FOREIGN KEY (cat_id)
REFERENCES catalogs( id)
ON DELETE CASCADE
)
INSERT INTO catalogs_sub VALUES (1,1),(2,1),(3,2),(4,2)
SELECT * FROM catalogs_sub
id | cat_id
-: | -----:
1 | 1
2 | 1
3 | 2
4 | 2
SET FOREIGN_KEY_CHECKS=0;
DELETE FROM catalogs WHERE id=1;
SET FOREIGN_KEY_CHECKS=1;
SELECT * FROM catalogs
| id |
| -: |
| 2 |
SELECT * FROM catalogs_sub
id | cat_id
-: | -----:
1 | 1
2 | 1
3 | 2
4 | 2
DELETE FROM catalogs WHERE id=2;
SELECT * FROM catalogs
| id |
| -: |
SELECT * FROM catalogs_sub
id | cat_id
-: | -----:
1 | 1
2 | 1
db<>fiddle here
Related
This question already has answers here:
Prevent InnoDB auto increment ON DUPLICATE KEY
(5 answers)
Closed 4 years ago.
I have 2 tables with one-to-one relationship:
post_views table
___________________________________
| | | |
| id | post_id | views |
|________|_____________|___________|
posts table
__________________________________________
| | | | |
| id | title | text | .. |
|________|___________|__________|_________|
post_id from post_views table is joined with id from posts table.
The id in both tables is primary and auto incremented, And the post_id is unique.
Here is a screenshot of the indexes for post_views:
https://prnt.sc/k6no10
Each post should has only one row in post_views table.
I run this query to insert a new row or increase the views, If that post_id exists:
INSERT INTO post_views (`post_id`, `views`) VALUES (1, 1) ON DUPLICATE KEY UPDATE `views` = `views`+1
It's executed successfully and a new row is inserted:
____________________________________
| | | |
| id | post_id | views |
|__________|_____________|___________|
| | | |
| 1 | 1 | 1 |
| | | |
|__________|_____________|___________|
Then when I run the same query again to increase the views, I get a success message saying that 2 rows inserted and the row is now:
____________________________________
| | | |
| id | post_id | views |
|__________|_____________|___________|
| | | |
| 1 | 1 | 2 |
| | | |
|__________|_____________|___________|
And that's what I want, but if I run the query with a new post_id:
INSERT INTO post_views (`post_id`, `views`) VALUES (2, 1) ON DUPLICATE KEY UPDATE `views` = `views`+1
I get that:
____________________________________
| | | |
| id | post_id | views |
|__________|_____________|___________|
| | | |
| 1 | 1 | 2 |
|__________|_____________|___________|
| | | |
| 3 | 2 | 1 |
|__________|_____________|___________|
The id is 3 instead of 2, So each time I run the query with the same post_id is like I'm inserting a new row with an id.
So if I run the query with post_id = 3 three times, The news id will be 7.
Is that's normal?
This is a non-issue. The ids in a table are not intended to be sequential with no gaps. Ensuring such logic is very expensive. It requires locking the whole table for the inserts. Database engines wisely do not do this.
In a single threaded environment -- no concurrent transactions -- you can get around this by doing separate update and insert commands:
update post_views
set views = views + 1
where post_id = 1;
insert into post_views (post_id, views)
select post_id, 1
from (select 1 as post_id) x
where not exists (select 1 from post_views pv where pv.post_id = x.post_id);
The where prevents the insert from even attempting an update, so no new id is generated. However, I strongly advise you not to take this approach. It is not thread-safe. In a concurrent processing world, it will not guarantee what you want.
Your case is even stranger. You have no need for the id column in post_views. The post_id can be both a primary key and a foreign key:
create table post_views (
post_id int primary key,
views int default 0,
constraint fk_post_views_post_id foreign key (post_id) references posts(id)
);
If you set the data up this way, you won't have the id, and you won't have the problem at all. Or, you could just add views into the posts table and deal with one table.
Suppose a table contains data like
MariaDB [c]> select * from t2;
+-----+
| abc |
+-----+
| 1 |
| 3 |
| 5 |
+-----+
Suppose my update command is
MariaDB [c]> update t2 set abc = abc+2;
It give following error
ERROR 1062 (23000): Duplicate entry '3' for key 'PRIMARY'
While the above command works fine in oracle 10g, Is it some kind of bug or what?
The following is just an illustration and trivial.
create table t2
( id int auto_increment primary key,
abc int not null,
unique key(abc)
);
insert t2(abc) values (1),(3),(5); -- 3 rows added
update t2 set abc = abc+2; -- Error Code 1062: Duplicate entry '3' for key 'abc'
The above error occurred because the update marched in the order of the primary key, also the physical ordering, and changing the 1 to a 3 violated the 3 that was already in place via the unique key. The fact that the end state would make every thing OK, ideally, doesn't keep it from failing at that moment.
To illustrate this working in this highly rigged example knowing there is no other data:
truncate table t2; -- the data wasn't modified but for the paranoid, clear and re-insert
insert t2(abc) values (1),(3),(5); -- 3 rows added
Try it bottom up (so that the Unique constraint is not violated):
update t2 set abc = abc+2 order by abc desc;
select * from t2;
+----+-----+
| id | abc |
+----+-----+
| 1 | 3 |
| 2 | 5 |
| 3 | 7 |
+----+-----+
It leverages the ability to have an order by in an update statement.
So it comes down to knowing your data and what you can get away with. Saying it worked on Oracle as you did in comments is on another db platform and with some other schema. So that is mute.
I have two table in MySql Database. I am pretty sure if those have a relation 1 : n
This is those table :
First table named tb_karyawan (karyawan in english is employee)
MariaDB [db_egi]> select * from tb_karyawan where NIK_KARYAWAN = 2;
+--------------+------------------+---------------+-------+-------------+-------+
| NIK_KARYAWAN | NAMA | TGL_BERGABUNG | LEVEL | DEPARTEMENT | KELAS |
+--------------+------------------+---------------+-------+-------------+-------+
| 2 | Nurrahmi Massere | 2016-02-02 | 5 | 6 | 3 |
+--------------+------------------+---------------+-------+-------------+-------+
1 row in set (0.00 sec)
The second table named : tb_master_perhitungan :
MariaDB [db_egi]> select * from tb_master_perhitungan;
+----+---------------------+--------------+-------+-------+-------+-------+-------+------------+
| ID | PERIODE | NIK_KARYAWAN | ID_C1 | ID_C2 | ID_C3 | ID_C4 | ID_C5 | STATUS |
+----+---------------------+--------------+-------+-------+-------+-------+-------+------------+
| 13 | 2016-02-07 12:07:00 | 2 | 1 | 3 | 3 | 2 | 3 | UNAPPROVED |
| 13 | 2016-01-07 09:00:28 | 2 | 2 | 3 | 3 | 2 | 3 | APPROVED |
| 13 | 2015-12-07 15:47:18 | 2 | 3 | 3 | 3 | 2 | 3 | APPROVED |
+----+---------------------+--------------+-------+-------+-------+-------+-------+------------+
3 rows in set (0.00 sec)
Please see of the field : NIK_KARYAWAN.
The relation is : there are one record in tb_karyawan that related to a lot of record in tb_master_perhitungan.
I set the relation in HeidiSQL like this :
| KEY NAME | COLUMN | REFERENCE | FOREIGN COLUMN | ON UPDATE | ON DELETE |
+--------------------------------------------------------------------------------------------------------------------+
| FK_tb_master_perhitungan_tb_karyawan | NIK_KARYAWAN | tb_karyawan | NIK_KARYAWAN | CASCADE | NO ACTION |
+--------------------------------------------------------------------------------------------------------------------+
On UPDATE is good but in delete I got a problem.
The problem is, when I decide to delete one row in tb_karyawan, which is NIK_KARYAWAN = 2, I want to still hold the record in tb_master_perhitungan that have a relation with this NIK_KARYAWAN = 2 even NIK_KARYAWAN = 2 has been deleted in tb_karyawan.
This is the error :
Error Number: 1451
Cannot delete or update a parent row: a foreign key constraint fails (`db_egi`.`tb_master_perhitungan`, CONSTRAINT `FK_tb_master_perhitungan_tb_karyawan` FOREIGN KEY (`NIK_KARYAWAN`) REFERENCES `tb_karyawan` (`NIK_KARYAWAN`) ON DELETE NO ACTION ON UPDATE CASCADE)
DELETE FROM `tb_karyawan` WHERE `NIK_KARYAWAN` = '2'
Should I change the design or there is another solution ?
Any solution is so appreciated.
In the second table you have to make foreign key constraint, ON DELETE CASCADE ON UPDATE CASCADE.
as like suppose i have two table jobseeker and job_status and one foreign key between them so foreign key setup will be like :-
CONSTRAINT `job_status_ibfk_1` FOREIGN KEY (`jobseeker_id`)
REFERENCES `jobseeker` (`jobseeker_id`) ON DELETE CASCADE ON UPDATE CASCADE
No action relation in mysql restrict the action the same as restrict, so when you want to delete record with the primary key from your master table you must delete all related records within the foreign keys table before.
http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html
I am trying to use ON DELETE CASCADE for a database I'm working on. Didn't seem to work so I tested it out on a simple example with no success.
CREATE TABLE foo (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
data VARCHAR(10),
PRIMARY KEY (id)
)ENGINE=InnoDB;
CREATE TABLE foo2 (
id INT UNSIGNED NOT NULL,
data2 VARCHAR(10),
PRIMARY KEY (id),
CONSTRAINT fk_foo2_id FOREIGN KEY (id) REFERENCES foo(id) ON DELETE CASCADE
)ENGINE=InnoDB;
INSERT INTO foo (data) VALUE ('hello'),('world'),('mysql');
INSERT INTO foo2 (data2) VALUE ('hello2'),('world2'),('mysql2');
SELECT * FROM foo;
+----+-------+
| id | data |
+----+-------+
| 1 | hello |
| 2 | world |
| 3 | mysql |
+----+-------+
3 rows in set (0.00 sec)
SELECT * FROM foo2;
+----+--------+
| id | data2 |
+----+--------+
| 1 | hello2 |
| 2 | world2 |
| 3 | mysql2 |
+----+--------+
3 rows in set (0.00 sec)
DELETE FROM foo WHERE id=2;
SELECT * FROM foo;
+----+-------+
| id | data |
+----+-------+
| 1 | hello |
| 3 | mysql |
+----+-------+
2 rows in set (0.00 sec)
SELECT * FROM foo2;
+----+--------+
| id | data2 |
+----+--------+
| 1 | hello2 |
| 2 | world2 |
| 3 | mysql2 |
+----+--------+
3 rows in set (0.00 sec)
I can't for the life of me figure out why this isn't working. I looked at similar questions and answers on here and I did exactly what they said and it still didn't work. Most of them just said to change to ENGINE=InnoDb, but I tried it and no success.
There must be something I'm missing here, and it's probably very obvious.. Monday mornings.
If anyone can shed some light on this little noob problem of mine, I would greatly appreciate it!
Edit: removed the auto_increment from id in foo2 as it did not belong there
The first thing that pops to mind is to check the setting of the foreign_key_checks variable. If that's set to 0 (FALSE), then foreign key constraints are NOT enforced.
SHOW VARIABLES LIKE 'foreign_key_checks'
To enable foeign key constraints, set to the variable to 1
SET foreign_key_checks = 1;
NOTE: this affects only the current session. New sessions inherit the GLOBAL setting.
Also, verify that your tables are actually using the InnoDB engine, and that the foreign keys are defined. Easiest way is to get the output from:
SHOW CREATE TABLE foo;
SHOW CREATE TABLE foo2;
FOLLOWUP
This is something that we expect NOT to be broken in MySQL 5.1.61.
As a workaround, try defining the foreign key constraint as a separate ALTER TABLE statement.
ALTER TABLE foo2
ADD CONSTRAINT fk_foo2_id FOREIGN KEY (id) REFERENCES foo(id) ON DELETE CASCADE ;
I don't see much use in a foreign key constraint between two columns that are both defined with "auto_increment". In your example, you could easily create several rows in table "foo" (without a counterpart in "foo2"), and from then onwards you could not control whether "id" values in both tables match.
I admit I didn't check the documentation, but it would not surprise me if MySQL silently ignored a foreign key constraint for an auto-generated column.
IMNSHO, your table "foo2" should use "id" values which are set explicitly and reference specific rows in "foo", because then it would make sense that deleting such "foo" rows should cascade onto "foo2".
I am trying to delete a project from the projects table and all the images associated with that project in the images table.
Lets say p_id = 10
DELETE FROM projects, images WHERE projects.p_id = ? AND images.p_id = ?
What is wrong with this query?
DELETE projects, images
FROM projects, images
WHERE projects.p_id = ?
AND projects.p_id = images.p_id;
As Chacha102 noted, the problem of your query was the AND in the WHERE clause.
However, you may want to use the JOIN syntax for multi-table DELETEs, which I find easier to read:
DELETE projects, images
FROM projects
LEFT JOIN images ON images.p_id = projects.p_id
WHERE projects.p_id = 10;
DELETE FROM projects, images WHERE projects.p_id = ? or images.p_id = ?
When being deleted, an item will never meet both of these requirements, therefore it must be OR not AND
The answer
DELETE FROM p, i
USING projects p, images i
WHERE p.p_id = ?
AND p.p_id = i.p_id
The test
projects
create table projects (
p_id int unsigned not null auto_increment primary key
);
insert into projects (p_id) values (1),(2),(3);
select * from projects;
-- +------+
-- | p_id |
-- +------+
-- | 1 |
-- | 2 |
-- | 3 |
-- +------+
images
create table images (
i_id int unsigned not null auto_increment primary key,
p_id int unsigned default null
);
insert into images (p_id) values (1),(1),(1),(2),(2),(3),(3);
select * from images;
-- +------+------+
-- | i_id | p_id |
-- +------+------+
-- | 1 | 1 |
-- | 2 | 1 |
-- | 3 | 1 |
-- | 4 | 2 |
-- | 5 | 2 |
-- | 6 | 3 |
-- | 7 | 3 |
-- +------+------+
the delete
delete from p, i
using projects p, images i
where p.p_id = i.p_id
and p.p_id = 1;
the result
select * from projects;
-- +------+
-- | p_id |
-- +------+
-- | 2 |
-- | 3 |
-- +------+
select * from images;
-- +------+------+
-- | i_id | p_id |
-- +------+------+
-- | 4 | 2 |
-- | 5 | 2 |
-- | 6 | 3 |
-- | 7 | 3 |
-- +------+------+
works a treat!
You should use two separate queries to do that :
delete from images where p_id = 123;
delete from projects where p_id = 123;
i.e. :
First, delete the images, that depend on the project (foreign key ? )
And, when nothing depends on the project anymore, delete the project itself.
And, as a security precaution, you should wrap all this in a transaction, to get a all or nothing behavior -- well, if you are using a storage engine that suppors transactions, like InnoDb.
See 12.3.1. START TRANSACTION, COMMIT, and ROLLBACK Syntax, about that, in the MySQL Manual.
Change the AND into an OR.
You might want to use a foreign key constraint with a cascading delete, much easier, but you have to use innoDB and create this FK-constraint. Delete the project and all related images will be deleted as well.
(Wrong answer, MySQL allows this)
You can't delete from two tables in one query.
The closest you can get is wrap the two deletes in a transaction:
begin transaction
delete from projects where p_id = ?
delete from images where p_id = ?
commit transaction