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

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

Related

MySql table column deleted its AI flag when I altered the size of the column from BIGINT(11) to BIGINT(20)

I have a table name file_upload
It has column upload_id as BIGINT(11) AI PK NOT NULL
when I altered the tables column size to BIGINT(20) the AI flag got deleted,
Should I provide the new definition to the column everytime I alter anything from the column?
Only a little note. There is no real difference between BIGINT(10) and BIGINT(20). You can store always the same values. The size if only use with ZEROFILL.
See my Sample
mysql> CREATE TABLE `bigints` (
-> `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
-> `bigint1` BIGINT(1) UNSIGNED DEFAULT NULL,
-> `bigint10` BIGINT(10) UNSIGNED ZEROFILL DEFAULT NULL,
-> `bigint20` BIGINT(20) UNSIGNED ZEROFILL DEFAULT NULL,
-> PRIMARY KEY (`id`)
-> ) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0,04 sec)
mysql>
mysql> INSERT INTO `bigints` (`id`, `bigint1`, `bigint10`, `bigint20`)
-> VALUES
-> (1, 1, 1, 1),
-> (2, 100, 100, 100),
-> (3, 1000000000, 1000000000, 1000000000),
-> (4, 18446744073709551615, 18446744073709551615, 18446744073709551615);
Query OK, 4 rows affected (0,01 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> SELECT * from bigints;
+----+----------------------+----------------------+----------------------+
| id | bigint1 | bigint10 | bigint20 |
+----+----------------------+----------------------+----------------------+
| 1 | 1 | 0000000001 | 00000000000000000001 |
| 2 | 100 | 0000000100 | 00000000000000000100 |
| 3 | 1000000000 | 1000000000 | 00000000001000000000 |
| 4 | 18446744073709551615 | 18446744073709551615 | 18446744073709551615 |
+----+----------------------+----------------------+----------------------+
4 rows in set (0,01 sec)
mysql>

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

Have one entry automatically delete when its key is deleted 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

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'.

update all NULL fields MySQL

I'd like to update all NULL fields in one table to 0. Of course
UPDATE mytable SET firstcol=0 WHERE firstcol IS NULL
would do the job. But I wonder if thereĀ“s a smarter solution than just c&p this line for every column.
You could do this - repeat as necessary for each column:
UPDATE `table1` SET
`col1` = IFNULL(col1, 0),
`col2` = IFNULL(col2, 0);
Example:
DROP TABLE IF EXISTS `table1`;
CREATE TABLE `table1` (
`id` int(10) unsigned NOT NULL auto_increment,
`col1` int(10) unsigned,
`col2` int(10) unsigned,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `table1` VALUES
(1, 1, NULL),
(2, NULL, NULL),
(3, 2, NULL),
(4, NULL, NULL),
(5, 3, 4),
(6, 5, 6),
(7, 7, NULL);
UPDATE `table1` SET
`col1` = IFNULL(col1, 0),
`col2` = IFNULL(col2, 0);
SELECT * FROM `table1`;
+----+------+------+
| id | col1 | col2 |
+----+------+------+
| 1 | 1 | 0 |
| 2 | 0 | 0 |
| 3 | 2 | 0 |
| 4 | 0 | 0 |
| 5 | 3 | 4 |
| 6 | 5 | 6 |
| 7 | 7 | 0 |
+----+------+------+
UPDATE
If you want to alter the table structure by changing columns so that they no longer accept nulls, you could do it with a stored procedure. The following stored procedure queries the INFORMATION_SCHEMA COLUMNS for information about columns in a given database table. From that information, it builds up a prepared statement which is then used to alter the table structure. You may need to tweak it to suit your exact requirements - at the moment, it looks for INT columns which do not have NOT NULL set:
delimiter //
DROP PROCEDURE IF EXISTS no_nulls//
CREATE PROCEDURE `no_nulls` (IN param_schema CHAR(255), IN param_table CHAR(255))
BEGIN
SET #alter_cmd = (SELECT CONCAT(
'ALTER TABLE ',
param_table,
GROUP_CONCAT(
' MODIFY COLUMN ',
`column_name`, ' ',
`column_type`,
' NOT NULL'
SEPARATOR ', ')
) AS `sql_cmd`
FROM INFORMATION_SCHEMA.COLUMNS
WHERE `table_schema` = param_schema
AND `table_name` = param_table
AND LCASE(`data_type`) = 'int'
AND LCASE(`is_nullable`) = 'yes');
IF NOT ISNULL(#alter_cmd) THEN
SELECT #alter_cmd;
PREPARE stmt FROM #alter_cmd;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
END//
delimiter ;
Example:
CREATE TABLE `test`.`table1` (
`id` int(10) unsigned NOT NULL auto_increment,
`col1` int(10) unsigned,
`col2` int(10) unsigned,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CALL no_nulls('test', 'table1');
+----------------------------------------------------------------------------------------------------------------+
| #alter_cmd |
+----------------------------------------------------------------------------------------------------------------+
| ALTER TABLE table1 MODIFY COLUMN col1 int(10) unsigned NOT NULL, MODIFY COLUMN col2 int(10) unsigned NOT NULL |
+----------------------------------------------------------------------------------------------------------------+
SHOW CREATE TABLE `test`.`table1`;
CREATE TABLE `table1` (
`id` int(10) unsigned NOT NULL auto_increment,
`col1` int(10) unsigned NOT NULL,
`col2` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
The following line displays the command that is to be executed, and may be removed from the stored procedure if necessary:
SELECT #alter_cmd;
Can you just ALTER the columns to NOT NULL DEFAULT 0?
You can do this in a single statement, as per MySQL documentation:
You can issue multiple ADD, ALTER, DROP, and CHANGE clauses in a single ALTER TABLE statement, separated by commas. This is a MySQL extension to standard SQL, which allows only one of each clause per ALTER TABLE statement.
You may want to alter your columns to NOT NULL.
ALTER TABLE your_table MODIFY COLUMN your_field INT NOT NULL;
Test case:
CREATE TABLE nulltable (id INT);
INSERT INTO nulltable VALUES (1);
INSERT INTO nulltable VALUES (2);
INSERT INTO nulltable VALUES (3);
INSERT INTO nulltable VALUES (NULL);
INSERT INTO nulltable VALUES (NULL);
INSERT INTO nulltable VALUES (NULL);
INSERT INTO nulltable VALUES (5);
Result:
mysql> SELECT * FROM nulltable;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| NULL |
| NULL |
| NULL |
| 5 |
+------+
7 rows in set (0.00 sec)
mysql> ALTER TABLE nulltable MODIFY COLUMN id INT NOT NULL;
Query OK, 7 rows affected, 3 warnings (0.08 sec)
Records: 7 Duplicates: 0 Warnings: 3
mysql> SELECT * FROM nulltable;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 0 |
| 0 |
| 0 |
| 5 |
+----+
7 rows in set (0.00 sec)
Not without an intermediate technology or cursor. You could use DESCRIBE mytable; to get the column names and loop over them to build your UPDATE queries.
So it is possible. But by the time it took you to write that, you probably just could have copy and pasted ;)
This worked for me!
UPDATE `results` SET
column1 = IFNULL(column1,0),
column2 = IFNULL(column2,'');
This is mike's answer but without the quotes for columns on the left !
Note: If you are trying to set your values '0' instead of an empty string if a column's datatype is int
I don't believe there is; any statement that worked on rows that didn't satisfy the where clause would update rows you didn't intent to update. Jason's answer is correct, but, I think, a bit unsafe, unless you are really sure that's what you want.
ALTER TABLE dataBaseName.tableName
ADD COLUMN columnX INT(20) NULL DEFAULT 1 AFTER columnY;
It does the following
adds a new column columnX after columnY.
sets its value to default 1 throughout the column columnX
columnY columnX
| cellValueA | 1 |
| cellValueB | 1 |
| cellValueC | 1 |
| cellValueD | 1 |