Have one entry automatically delete when its key is deleted mysql - mysql

I've got a user table with an id column
a profile table with an id column and a user_id col which refers to id in the user table
I want to automatically delete the profile when I delete the user.
Is this possible in mysql?

Assume that there is 1 to N relationship in between PARENT and CHILD entities so when you delete a PARENT then the associated CHILD(s) will be deleted as you wanted. For that you use ON DELETE CASCADE like below.
What you need to be aware of is, depending on your design if the CHILD entity is associated with one or more entities then you might cause unexpected issue such as breaking relationships, removing records in associated tables etc. Also read up on the storage engines in link above as well, you cannot use MuISAM.
PARENT
CREATE TABLE `parent` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CHILD
CREATE TABLE `child` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
`parent_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
KEY `FK_1_idx` (`parent_id`),
CONSTRAINT `FK_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
EXAMPLE
Populate Parent
mysql> INSERT INTO parent (name) VALUES ('parent_1'), ('parent_2');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM parent;
+----+----------+
| id | name |
+----+----------+
| 1 | parent_1 |
| 2 | parent_2 |
+----+----------+
2 rows in set (0.00 sec)
Populate Child
mysql> INSERT INTO child (name, parent_id) VALUES ('child_1', 1), ('child_2', 1), ('child_3', 2);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM child;
+----+---------+-----------+
| id | name | parent_id |
+----+---------+-----------+
| 1 | child_1 | 1 |
| 2 | child_2 | 1 |
| 3 | child_3 | 2 |
+----+---------+-----------+
3 rows in set (0.00 sec)
Delete
mysql> DELETE FROM parent WHERE name = 'parent_1';
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM child;
+----+---------+-----------+
| id | name | parent_id |
+----+---------+-----------+
| 3 | child_3 | 2 |
+----+---------+-----------+
1 row in set (0.00 sec)

You can do this ON DELETE CASCADE command in the table definition.
See: MySQL foreign key constraints, cascade delete
Or you can use a trigger to do the same:
CREATE
TRIGGER `delete_from_profile`
AFTER DELETE ON `user`
FOR EACH ROW
BEGIN
DELETE
FROM profile
WHERE user_id = old.id;
END

Related

Deletion using indexed field is slow in mysql

I have two tables PARENT and CHILD. PARENT has one to many relationship with CHILD. PARENT table has 300000 and CHILD has 1500000 rows respectively. Row with some ID is inserted and deleted frequently and deletion takes time approximately 20 sec. And Execution Plan of deletion in CHILD table shows entire table scan. Why deletion on indexed field takes time?
UPDATE - 1
Table schema of parent and child
show create table parent;
| Table | Create Table | parent | CREATE TABLE `parent` (
`id` bigint(20) NOT NULL,
`name` varchar(500) COLLATE latin1_general_cs DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_cs |
show create table child;
| Table | Create Table | child | CREATE TABLE `child` (
`id` bigint(20) NOT NULL,
`value` varchar(2000) COLLATE latin1_general_cs DEFAULT NULL,
KEY `child_cons` (`id`),
CONSTRAINT `child_cons` FOREIGN KEY (`id`) REFERENCES `parent` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_cs |
UPDATE - 2
The delete statement used is
delete from child where id=1;
Execution plan
explain delete from child where id=1;
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | SIMPLE | child | ALL | child_cons | NULL | NULL | NULL | 1350398 | Using where |
1 row in set (0.00 sec)
UPDATE - 3
Child table contains only 5 rows for id = 1

foreign key relation constraint - cascade on update also the other value of the foreign id

INITIAL STATE
parents
id parent_validity
1 1
2 1
children
id parent_id child_validity
1 1 1
2 1 1
3 2 1
REQUIRED STATE AFTER UPDATE
parents
id parent_validity
1 0 (updated as 0)
2 1
children
id parent_id child_validity
1 1 0 (This val is expected as 0)
2 1 0 (This val is expected as 0)
3 2 1 (This val is expected as no change)
WHAT I GOT
children
id parent_id child_validity
1 1 0
2 1 0
3 2 0 (all values under this column became 0)
What I want is if I update a parent_validity in table parents, then related child_validity in table children should be updated. What I mean from related is foreign key relationship of ids.
My result is: Any validity change in table parents changes all validities in table children.
I don't want to do what I required with PHP. let MySQL handles.
I couldn't get the correct search terms for my request in web so I was unable to find any solution. Can you please help me.
Thanks for your help.
note: I think my q title needs an update. (That was my best, sorry.)
my sql to create and give the constraints
CREATE TABLE `parents` (
`id` int(2) unsigned NOT NULL AUTO_INCREMENT,
`parent_validity` bit(1) NOT NULL COMMENT '1:valid or 0:invalid',
PRIMARY KEY (`id`),
KEY (`parent_validity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `children` (
`id` int(2) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(2) unsigned NOT NULL,
`child_validity` bit(1) NOT NULL COMMENT '1:valid or 0:invalid',
PRIMARY KEY (`id`),
KEY (`child_validity`),
CONSTRAINT fk_parent_id FOREIGN KEY (parent_id) REFERENCES parents(id) ON UPDATE CASCADE,
CONSTRAINT fk_parent_validity FOREIGN KEY (child_validity) REFERENCES parents(parent_validity) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
An option is a composite key, but you should evaluate the design impact this entails for your solution:
mysql> DROP TABLE IF EXISTS `children`, `parents`;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE `parents` (
-> `id` int(2) unsigned NOT NULL AUTO_INCREMENT,
-> `parent_validity` bit(1) NOT NULL COMMENT '1:valid or 0:invalid',
-> PRIMARY KEY (`id`, `parent_validity`)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE `children` (
-> `id` int(2) unsigned NOT NULL AUTO_INCREMENT,
-> `parent_id` int(2) unsigned NOT NULL,
-> `child_validity` bit(1) NOT NULL COMMENT '1:valid or 0:invalid',
-> PRIMARY KEY (`id`),
-> KEY (`parent_id`, `child_validity`),
-> CONSTRAINT `fk_parent_id_child_validity` FOREIGN KEY (`parent_id`, `child_validity`)
-> REFERENCES `parents` (`id`, `parent_validity`) ON UPDATE CASCADE
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO
-> `parents`
-> VALUES
-> (NULL, 1), (NULL, 1);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> INSERT INTO
-> `children`
-> VALUES
-> (NULL, 1, 1),
-> (NULL, 1, 1),
-> (NULL, 2, 1);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SELECT
-> `id`,
-> CONVERT(`parent_validity`, UNSIGNED) `parent_validity`
-> FROM
-> `parents`;
+----+-----------------+
| id | parent_validity |
+----+-----------------+
| 1 | 1 |
| 2 | 1 |
+----+-----------------+
2 rows in set (0.00 sec)
mysql> SELECT
-> `id`,
-> `parent_id`,
-> CONVERT(`child_validity`, UNSIGNED) `child_validity`
-> FROM
-> `children`;
+----+-----------+----------------+
| id | parent_id | child_validity |
+----+-----------+----------------+
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 2 | 1 |
+----+-----------+----------------+
3 rows in set (0.00 sec)
mysql> UPDATE `parents`
-> SET `parent_validity` = 0
-> WHERE `id` = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT
-> `id`,
-> CONVERT(`parent_validity`, UNSIGNED) `parent_validity`
-> FROM
-> `parents`;
+----+-----------------+
| id | parent_validity |
+----+-----------------+
| 1 | 0 |
| 2 | 1 |
+----+-----------------+
2 rows in set (0.00 sec)
mysql> SELECT
-> `id`,
-> `parent_id`,
-> CONVERT(`child_validity`, UNSIGNED) `child_validity`
-> FROM
-> `children`;
+----+-----------+----------------+
| id | parent_id | child_validity |
+----+-----------+----------------+
| 1 | 1 | 0 |
| 2 | 1 | 0 |
| 3 | 2 | 1 |
+----+-----------+----------------+
3 rows in set (0.00 sec)
Creating the constraint
CONSTRAINT fk_parent_validity FOREIGN KEY (child_validity) REFERENCES parents(parent_validity) ON UPDATE CASCADE creates an new foreign key between your tables.
As you update the parent_validity with a value of 1 and change it to 0, MySQL cascades this with the foreign key to set all references where the value was 1 and updates to 0 (in this case all child_validity where it originally was 1).
Rather than a foreign key, you need to use an UPDATE trigger. Triggers are additional procedures which automatically run when an event happens on the database. You need an "UPDATE" trigger here.
Think this might work:
CREATE TRIGGER update_child_validitiy AFTER UPDATE ON parents
FOR EACH ROW
BEGIN
UPDATE children SET child_validity = NEW.parent_validity WHERE id = NEW.id;
END;
I've not used triggers much but hopefully these links help:
http://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html
https://www.techonthenet.com/mysql/triggers/after_update.php

MySQL INSERT ... ON DUPLICATE KEY not updating table with no errors or warnings

So I have the following table:
mysql> show create table user_api_skills \G
*************************** 1. row ***************************
Table: user_api_skills
Create Table: CREATE TABLE `user_api_skills` (
`characterID` int(11) NOT NULL,
`typeID` int(11) NOT NULL,
`level` enum('0','1','2','3','4','5') NOT NULL DEFAULT '0',
`skillpoints` int(11) NOT NULL,
`currentTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`characterID`,`typeID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql>
And in that table a row which I am trying to insert/update:
mysql> SELECT * FROM `user_api_skills` WHERE `characterID` =93192782 AND `typeID` =3359;
+-------------+--------+-------+-------------+---------------------+
| characterID | typeID | level | skillpoints | currentTime |
+-------------+--------+-------+-------------+---------------------+
| 93192782 | 3359 | 3 | 135765 | 2013-09-30 16:58:35 |
+-------------+--------+-------+-------------+---------------------+
1 row in set (0.00 sec)
I believe my query is correctly formed and when executed it doesn't throw any errors or warnings:
mysql> INSERT INTO user_api_skills (characterID,typeID,level,skillpoints)
VALUES (93192782,3359,4,135765) ON DUPLICATE KEY UPDATE level=4,
skillpoints=135765,currentTime=NOW();
Query OK, 2 rows affected (0.22 sec)
I get 2 rows updated (as I would expect from an insert on dup update)
mysql> SELECT * FROM `user_api_skills` WHERE `characterID` =93192782 AND `typeID` =3359;
+-------------+--------+-------+-------------+---------------------+
| characterID | typeID | level | skillpoints | currentTime |
+-------------+--------+-------+-------------+---------------------+
| 93192782 | 3359 | 3 | 135765 | 2013-09-30 16:59:13 |
+-------------+--------+-------+-------------+---------------------+
1 row in set (0.00 sec)
mysql>
but the row itself only changes a single value (the currentTime). Can anybody explain why the other two fields are not updating?
Sorry, I have solved this myself. The level field is an ENUM and the query specified the new value as a number. Updating the query to the following resulted in the expected results.
mysql> INSERT INTO user_api_skills (characterID,typeID,level,skillpoints) VALUES
(93192782,3359,4,135765) ON DUPLICATE KEY UPDATE level='4', skillpoints=135765,
currentTime=NOW();
By providing a int to the update it updated to the one based number choice of the enum, so in this instance, the 4th choice is '3'.

Can't DROP "MUL" key/index in MySQL - column listed in SHOW

I'm using mysql-server-5.0.45-7.el5 on CentOs 5.
In my database, there is a table which, I don't know when, a MUL key was created (the database is shared, under control of a group), and now when I try to insert some values I get an error like shown above:
Duplicate entry '2-1-2004-09-11 13:13:41.526' for key 2:INSERT INTO ephemeris SET
EPH_TYPE_ID = 1, FILENAME = 'CBERS_2_CCD1_DRD_2004_09_11.13_13_23', ID = 0,
IS_NEW = 1, SATELLITE_ID = 2, TIME = '2004-09-11 13:13:41.526'
I got this error once, and I tried ALTER TABLE ephemeris DROP INDEX SATELLITE_ID;
It worked at first time, but now the same constraint appeared and it didn't worked at all.
The structure of table (resumed):
mysql> show columns from ephemeris;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | NULL | auto_increment |
| SATELLITE_ID | int(11) | NO | MUL | 0 | |
| EPH_TYPE_ID | int(11) | NO | | 0 | |
When I type the ALTER TABLE command, mysql returns as:
mysql> ALTER TABLE ephemeris DROP INDEX ephemeris.SATELLITE_ID ;
ERROR 1091 (42000): Can't DROP 'SATELLITE_ID'; check that column/key exists
Anybody have already gotten this error? Any help?
Rgds.
You need to first drop the foreign key constraint, then drop the index.
mysql> show create table a;
| CREATE TABLE `a` (
`id` varchar(20) DEFAULT NULL,
`sal` int(20) DEFAULT NULL,
`b_id` varchar(20) DEFAULT NULL,
KEY `b_id` (`b_id`),
CONSTRAINT `a_ibfk_1` FOREIGN KEY (`b_id`) REFERENCES `b` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
mysql> alter table a drop foreign key `a_ibfk_1`;
Query OK, 0 rows affected (0.10 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table a drop index `b_id`;
Query OK, 0 rows affected (0.23 sec)
Records: 0 Duplicates: 0 Warnings: 0

Mysql primary key when using INSERT INTO ... ON DUPLICATE KEY UPDATE

My table structure is:
CREATE TABLE IF NOT EXISTS `users_settings_temp` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`userid` int(10) unsigned DEFAULT NULL,
`type` enum('type1','type2')
`data` text,
`date_created` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
What I am trying to do is:
Let say I want to insert a new entry, but I dont want it to be duplicate, after google around, i found this format:
INSERT INTO users_settings_temp(...)
ON DUPLICATE KEY UPDATE data = '{$data}'
I guess the problem is in my table, the primary key => id. How do I alter the table, so that I could use the:
INSERT INTO ... ON DUPLICATE KEY UPDATE
Can I use user_id + type as primary key? If yes, could you please show me how to do it?
CREATE TABLE IF NOT EXISTS `users_settings_temp` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`userid` int(10) unsigned DEFAULT NULL,
`type` enum('type1','type2'),
`data` text,
`date_created` int(11) DEFAULT NULL,
PRIMARY KEY (`id`, `type`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
When you do it like this then
a) specifying id works
mysql> INSERT INTO users_settings_temp VALUES (1, 2, 'type1', 'keks', 5);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO users_settings_temp VALUES (1, 2, 'type2', 'keks', 5);
Query OK, 1 row affected (0.00 sec)
b) of course primary key is guaranteed to be unique
mysql> INSERT INTO users_settings_temp VALUES (1, 2, 'type2', 'keks', 5);
ERROR 1062 (23000): Duplicate entry '1-type2' for key 'PRIMARY'
c) letting database pull a new id works
mysql> INSERT INTO users_settings_temp VALUES (NULL, 2, 'type2', 'keks', 5);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO users_settings_temp VALUES (NULL, 2, 'type1', 'keks', 5);
Query OK, 1 row affected (0.00 sec)
but will increase them always
mysql> SELECT * FROM users_settings_temp;
+----+--------+-------+------+--------------+
| id | userid | type | data | date_created |
+----+--------+-------+------+--------------+
| 1 | 2 | type1 | keks | 5 |
| 1 | 2 | type2 | keks | 5 |
| 2 | 2 | type2 | keks | 5 |
| 3 | 2 | type1 | keks | 5 |
+----+--------+-------+------+--------------+
4 rows in set (0.00 sec)
NOTES:
You should think if your id should still be autoincrement or not.
Also, can not think of a reason why date_created should be int(11) instead of datetime