MySQL/MariaDB ON DUPLICATE KEY UPDATE based on condition - mysql

I want to use a conditional
ON DUPLICATE KEY UPDATE
Based on the example provided in this question, suppose name is the primary key. We want to execute the following query:
INSERT INTO beautiful (name, age, col3, col 4, ..., col 100)
VALUES
('Helen', 24, ...),
('Katrina', 21, ...),
('Samia', 22, ...),
('Hui Ling', 25, ...),
('Yumie', 29, ...)
ON DUPLICATE KEY UPDATE
age = VALUES(age),
col3= VALUES(col3),
col4= VALUES(col4),
...
col100= VALUES(col100)
And (in MariaDB) I want the update to be done, only when the age of the newly received record is larger than the one already existent in the database.
Is there a way to do this?
UPDATE: Updated to reflect the fact that each record has multiple fields

Seems trivial
MariaDB [sandbox]> create table t(name varchar(20),age int default 0 , primary key(name));
Query OK, 0 rows affected (0.28 sec)
MariaDB [sandbox]>
MariaDB [sandbox]> INSERT INTO t (name, age)
-> VALUES
-> ('Helen', 24),
-> ('Katrina', 21),
-> ('Samia', 22),
-> ('Hui Ling', 25),
-> ('Yumie', 29)
-> ON DUPLICATE KEY UPDATE
-> age = if(VALUES(age) > age,values(age),age);
Query OK, 5 rows affected (0.03 sec)
Records: 5 Duplicates: 0 Warnings: 0
MariaDB [sandbox]>
MariaDB [sandbox]> select * from t;
+----------+------+
| name | age |
+----------+------+
| Helen | 24 |
| Hui Ling | 25 |
| Katrina | 21 |
| Samia | 22 |
| Yumie | 29 |
+----------+------+
5 rows in set (0.00 sec)
MariaDB [sandbox]>
MariaDB [sandbox]> INSERT INTO t (name, age)
-> VALUES
-> ('Helen', 25),
-> ('Katrina', 21),
-> ('Samia', 22),
-> ('Hui Ling', 25),
-> ('Yumie', 29)
-> ON DUPLICATE KEY UPDATE
-> age = if(VALUES(age) > age,values(age),age);
Query OK, 2 rows affected (0.02 sec)
Records: 5 Duplicates: 1 Warnings: 0
MariaDB [sandbox]>
MariaDB [sandbox]> select * from t;
+----------+------+
| name | age |
+----------+------+
| Helen | 25 |
| Hui Ling | 25 |
| Katrina | 21 |
| Samia | 22 |
| Yumie | 29 |
+----------+------+
5 rows in set (0.00 sec)
If there are n columns which update when age changes then
drop table if exists t;
create table t(name varchar(20),age int default 0 , col1 int, col2 int,col3 int,primary key(name));
INSERT INTO t (name, age, col1,col2,col3)
VALUES
('Helen', 24,1,1,1),
('Katrina', 21,1,1,1),
('Samia', 22,1,1,1),
('Hui Ling', 25,1,1,1),
('Yumie', 29,1,1,1)
ON DUPLICATE KEY UPDATE
col1 = if(VALUES(age) > age,values(col1),col1),
col2 = if(VALUES(age) > age,values(col2),col2),
col3 = if(VALUES(age) > age,values(col3),col3),
age = if(VALUES(age) > age,values(age),age);
select * from t;
INSERT INTO t (name, age, col1,col2,col3)
VALUES
('Helen', 25,2,2,2),
('Katrina', 21,2,2,2),
('Samia', 22,1,1,1),
('Hui Ling', 25,1,1,1),
('Yumie', 29,1,1,1)
ON DUPLICATE KEY UPDATE
col1 = if(VALUES(age) > age,values(col1),col1),
col2 = if(VALUES(age) > age,values(col2),col2),
col3 = if(VALUES(age) > age,values(col3),col3),
age = if(VALUES(age) > age,values(age),age);
select * from t;
MariaDB [sandbox]> select * from t;
+----------+------+------+------+------+
| name | age | col1 | col2 | col3 |
+----------+------+------+------+------+
| Helen | 25 | 2 | 2 | 2 |
| Hui Ling | 25 | 1 | 1 | 1 |
| Katrina | 21 | 1 | 1 | 1 |
| Samia | 22 | 1 | 1 | 1 |
| Yumie | 29 | 1 | 1 | 1 |
+----------+------+------+------+------+
5 rows in set (0.00 sec)
Note age has to be last updated. There is no shortcut to keying all the updateable columns. If the columns other than age are dynamic then it may be worth looking at dynamic sql. Another approach might be to load the inserts to a staging table with a trigger to update your master table.

Related

is it possible to stop auto increment in mysql for particular record and store duplicate number

i want to insert a duplicated value into table which have auto_increamented colum.
can i insert like this
auto_incremet
+---------------+---------
| invoiceNumber | totalAmt |
+---------------+----------+
| 1 | 200 |
| 0 158
| 2 | 1200 |
| 0 122 |
| 3 65 |
| 4 | 240 |
| 5 | 330 |
| 6 | 80 |
+---------------+---------
i do not want to increment the value for particular record .
is it possible to do so?
it is possible with additional table so that i can send particular records to other table and stop it increment in first table.
but cant it be done with same table ?
Yes, but it can't be 0. It could be 1, though.
MariaDB [wow]> create table test ( id int(11) not null auto_increment, name varchar(15), key id (id));
Query OK, 0 rows affected (0.02 sec)
MariaDB [wow]> insert into test (id, name) values (1, 'mark');
Query OK, 1 row affected (0.00 sec)
MariaDB [wow]> insert into test (id, name) values (0, 'allan');
Query OK, 1 row affected (0.01 sec)
MariaDB [wow]> insert into test (id, name) values (1, 'patrick');
Query OK, 1 row affected (0.00 sec)
MariaDB [wow]> insert into test (name) values ('chris');
Query OK, 1 row affected (0.00 sec)
MariaDB [wow]> insert into test (name) values ('oliver');
Query OK, 1 row affected (0.00 sec)
MariaDB [wow]> insert into test (id, name) values (1, 'damien');
Query OK, 1 row affected (0.00 sec)
Observe:
MariaDB [wow]> select * from test;
+----+---------+
| id | name |
+----+---------+
| 1 | mark |
| 2 | allan |
| 1 | patrick |
| 3 | chris |
| 4 | oliver |
| 1 | damien |
+----+---------+
Obviously this is not a very good idea. Create a second column and call it invoice_id. Increment it using a sequence table.
But yes, to answer the question, although auto_increment must be on a key, it does not need to be on a UNIQUE key such as the primary key.

MySQL: insert select in order

I want to insert data into a table in a specific order. This is because I need to give each entry a specific ID. What I am using is a select statement:
select (#i := #i + 1) as id, ...
order by column
The problem I am having is that this does not seem to work. I get the result I want from the select query. However, when I try to insert the data into the table the order by statement is ignored. Is there any way to force the correct order in the insert statement?
What I want is this:
+----+------+-------------+
| id | name | breadcrumbs |
+----+------+-------------+
| 1 | test | 01 |
| 5 | -d | 01,05 |
| 4 | c | 04 |
| 6 | e | 06 |
| 2 | -a | 06,02 |
| 3 | --b | 06,02,03 |
+----+------+-------------+
To become this:
+----+------+-------------+
| id | name | breadcrumbs |
+----+------+-------------+
| 1 | test | 01 |
| 2 | -d | 01,05 |
| 3 | c | 04 |
| 4 | e | 06 |
| 5 | -a | 06,02 |
| 6 | --b | 06,02,03 |
+----+------+-------------+
In a separate temporary table.
I would make certain that #i is initalised see select in from clause below
MariaDB [sandbox]> drop table if exists t;
Query OK, 0 rows affected (0.14 sec)
MariaDB [sandbox]>
MariaDB [sandbox]> create table t(id int, name varchar(10), breadcrumbs varchar(100));
Query OK, 0 rows affected (0.18 sec)
MariaDB [sandbox]> insert into t values
-> ( 1 , 'test' , '01' ),
-> ( 5 , '-d' , '01,05' ),
-> ( 4 , 'c' , '04' ),
-> ( 6 , 'e' , '06' ),
-> ( 2 , '-a' , '06,02' ),
-> ( 3 , '--b' , '06,02,03');
Query OK, 6 rows affected (0.01 sec)
Records: 6 Duplicates: 0 Warnings: 0
MariaDB [sandbox]>
MariaDB [sandbox]> drop table if exists t1;
Query OK, 0 rows affected (0.13 sec)
MariaDB [sandbox]> create table t1 as
-> select
-> #i:=#i+1 id,
-> t.name,t.breadcrumbs
-> from (select #i:=0) i,
-> t
-> order by breadcrumbs;
Query OK, 6 rows affected (0.22 sec)
Records: 6 Duplicates: 0 Warnings: 0
MariaDB [sandbox]>
MariaDB [sandbox]> select * from t1;
+------+------+-------------+
| id | name | breadcrumbs |
+------+------+-------------+
| 1 | test | 01 |
| 2 | -d | 01,05 |
| 3 | c | 04 |
| 4 | e | 06 |
| 5 | -a | 06,02 |
| 6 | --b | 06,02,03 |
+------+------+-------------+
6 rows in set (0.00 sec)
I want to insert data into a table in a specific order.
There is no internal order to the records in a MySQL database table. Tables are modeled after unordered sets. The only order which exists is the one you apply by using an ORDER BY clause when you query. So moving forward, instead of worrying about the order in which your records are inserted, you should instead make sure that your table has the necessary columns and data to order your result sets the way you want.

MySQL: Insert Random Date value dependent on some other date attribute

In a table X in mysql db i want to insert random anniversary date for every row having date of birth before 1/01/1990 in a new empty column. Please help me out as i m a novice in MySQL
You can use RAND() to generate a random integer in a range (let's say in a range between 23 and 75) like this
SELECT FLOOR(23 + RAND() * (75 - 23))
Your update statement to fill anniversary column of your table then might look like
UPDATE person
SET anniversary = dob + INTERVAL FLOOR(23 + RAND() * (75 - 23)) YEAR
WHERE dob < '1990-01-01'
Let's do a quick test
mysql> CREATE TABLE person
-> (
-> id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> name varchar(32) ,
-> dob DATE ,
-> anniversary DATE
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO person (name, dob)
-> VALUES
-> ('John', '1972-01-15'),
-> ('Phil', '1964-05-23'),
-> ('Mark', '1948-12-10'),
-> ('Steven', '1991-02-28'),
-> ('Helen', '1987-07-01');
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> UPDATE person
-> SET anniversary = dob + INTERVAL FLOOR(23 + RAND() * (75 - 23)) YEAR
-> WHERE dob SELECT * FROM person;
+----+--------+------------+-------------+
| id | name | dob | anniversary |
+----+--------+------------+-------------+
| 1 | John | 1972-01-15 | 1999-01-15 |
| 2 | Phil | 1964-05-23 | 2022-05-23 |
| 3 | Mark | 1948-12-10 | 1979-12-10 |
| 4 | Steven | 1991-02-28 | NULL |
| 5 | Helen | 1987-07-01 | 2050-07-01 |
+----+--------+------------+-------------+
5 rows in set (0.00 sec)

What does "USING table1, table1 as vtable" mean in MySQL?

I'm new to MySQL and I'm having a bit of trouble figuring out what this means:
DELETE from keywords USING keywords, keywords as vtable
WHERE (keywords.id > vtable.id) && (keywords.keyword=vtable.keyword)
Specifically, what does this part USING keywords, keywords as vtable mean?
Is there a better way to write this query that would be equivalent? I've read that creating virtual tables isn't efficient.
Also, is the . separating the table and the column?
vtable is an alias for the keywords table so that it can be self-joined. No different from any alias, but selection of the specific alias vtable may have led you to believe that there is some special virtual table mechanism involved. There isn't.
From an efficiency standpoint, this looks like a normal use of a self-join. It should be reasonably efficient, if there are indexes on the id and perhaps keywords field.
Also the dot ., does indeed separate table and column name (or table alias and column name).
Here's the same query with a different alias name:
DELETE from keywords
USING keywords, keywords as k2
WHERE (keywords.id > k2.id) && (keywords.keyword=k2.keyword)
And here's the whole query done a little differently, but maybe less confusingly, with a JOIN:
DELETE keywords
FROM keywords
INNER JOIN keywords as k2 ON keywords.keyword = k2.keyword
WHERE keywords.id > k2.id
This is a very fabulous method of query as whenever you got stuck in a condition such that you want to manipulate in a table by comparing itself from a record of same table.
The USING keyword can use a list of columns that appear in both tables and is equivalent to saying C.ID = O.ID AND S.ID = O.ID.
mysql>
mysql> CREATE TABLE Employee(
-> id int,
-> first_name VARCHAR(15),
-> last_name VARCHAR(15),
-> start_date DATE,
-> end_date DATE,
-> salary FLOAT(8,2),
-> city VARCHAR(10),
-> description VARCHAR(15)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql>
mysql> create table job (
-> id int,
-> title VARCHAR(20)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql>
mysql> insert into Employee(id,first_name, last_name, start_date, end_Date, salary, City, Description)
-> values (1,'Jason', 'Martin', '19960725', '20060725', 1234.56, 'Toronto', 'Programmer');
Query OK, 1 row affected (0.02 sec)
mysql>
mysql> insert into Employee(id,first_name, last_name, start_date, end_Date, salary, City, Description)
-> values(2,'Alison', 'Mathews', '19760321', '19860221', 6661.78, 'Vancouver','Tester');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> insert into Employee(id,first_name, last_name, start_date, end_Date, salary, City, Description)
-> values(3,'James', 'Smith', '19781212', '19900315', 6544.78, 'Vancouver','Tester');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> insert into Employee(id,first_name, last_name, start_date, end_Date, salary, City, Description)
-> values(4,'Celia', 'Rice', '19821024', '19990421', 2344.78, 'Vancouver','Manager');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> insert into Employee(id,first_name, last_name, start_date, end_Date, salary, City, Description)
-> values(5,'Robert', 'Black', '19840115', '19980808', 2334.78, 'Vancouver','Tester');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> insert into Employee(id,first_name, last_name, start_date, end_Date, salary, City, Description)
-> values(6,'Linda', 'Green', '19870730', '19960104', 4322.78,'New York', 'Tester');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> insert into Employee(id,first_name, last_name, start_date, end_Date, salary, City, Description)
-> values(7,'David', 'Larry', '19901231', '19980212', 7897.78,'New York', 'Manager');
Query OK, 1 row affected (0.02 sec)
mysql>
mysql> insert into Employee(id,first_name, last_name, start_date, end_Date, salary, City, Description)
-> values(8,'James', 'Cat', '19960917', '20020415', 1232.78,'Vancouver', 'Tester');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> insert into job (id, title) values (1,'Tester');
Query OK, 1 row affected (0.01 sec)
mysql> insert into job (id, title) values (2,'Accountant');
Query OK, 1 row affected (0.00 sec)
mysql> insert into job (id, title) values (3,'Developer');
Query OK, 1 row affected (0.00 sec)
mysql> insert into job (id, title) values (4,'Coder');
Query OK, 1 row affected (0.00 sec)
mysql> insert into job (id, title) values (5,'Director');
Query OK, 1 row affected (0.00 sec)
mysql> insert into job (id, title) values (6,'Mediator');
Query OK, 1 row affected (0.00 sec)
mysql> insert into job (id, title) values (7,'Proffessor');
Query OK, 1 row affected (0.00 sec)
mysql> insert into job (id, title) values (8,'Programmer');
Query OK, 1 row affected (0.01 sec)
mysql> insert into job (id, title) values (9,'Developer');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> select * from job;
+------+------------+
| id | title |
+------+------------+
| 1 | Tester |
| 2 | Accountant |
| 3 | Developer |
| 4 | Coder |
| 5 | Director |
| 6 | Mediator |
| 7 | Proffessor |
| 8 | Programmer |
| 9 | Developer |
+------+------------+
9 rows in set (0.00 sec)
mysql> select * from Employee;
+------+------------+-----------+------------+------------+---------+-----------+-------------+
| id | first_name | last_name | start_date | end_date | salary | city | description |
+------+------------+-----------+------------+------------+---------+-----------+-------------+
| 1 | Jason | Martin | 1996-07-25 | 2006-07-25 | 1234.56 | Toronto | Programmer |
| 2 | Alison | Mathews | 1976-03-21 | 1986-02-21 | 6661.78 | Vancouver | Tester |
| 3 | James | Smith | 1978-12-12 | 1990-03-15 | 6544.78 | Vancouver | Tester |
| 4 | Celia | Rice | 1982-10-24 | 1999-04-21 | 2344.78 | Vancouver | Manager |
| 5 | Robert | Black | 1984-01-15 | 1998-08-08 | 2334.78 | Vancouver | Tester |
| 6 | Linda | Green | 1987-07-30 | 1996-01-04 | 4322.78 | New York | Tester |
| 7 | David | Larry | 1990-12-31 | 1998-02-12 | 7897.78 | New York | Manager |
| 8 | James | Cat | 1996-09-17 | 2002-04-15 | 1232.78 | Vancouver | Tester |
+------+------------+-----------+------------+------------+---------+-----------+-------------+
8 rows in set (0.00 sec)
mysql>
mysql>
mysql>
The syntax looks like the following:
mysql>
mysql> SELECT C.First_Name, C.Last_Name, O.title
-> FROM Employee AS C
-> LEFT JOIN job as O USING (ID);
+------------+-----------+------------+
| First_Name | Last_Name | title |
+------------+-----------+------------+
| Jason | Martin | Tester |
| Alison | Mathews | Accountant |
| James | Smith | Developer |
| Celia | Rice | Coder |
| Robert | Black | Director |
| Linda | Green | Mediator |
| David | Larry | Proffessor |
| James | Cat | Programmer |
+------------+-----------+------------+
8 rows in set (0.00 sec)
mysql>
mysql>
mysql>
mysql> drop table job;
Query OK, 0 rows affected (0.00 sec)
mysql> drop table Employee;
Query OK, 0 rows affected (0.01 sec)
mysql>
mysql>
In this example, the USING keyword would be like using the ON keyword with C.ID = O.ID following it.
This is another shortcut to save time.
mysql>

Enforce order on composite primary key

Say I have the following table
item_a_id, item_b_id, value
Where item_a_id and item_b_id are a composite primary key. In my example a,b and b,a are equivalent. Therefore I want to ensure that item_a_id < item_b_id. Obviously the application logic will enforce this but is there a way to ensure the database does too?
In a reasonably current version of MySql you can use triggers to emulate a check constraint that produces the desired behavior.
Well, in your case, you could use trigger to check values before insert/update and swap it to ensure item_a_id will always less than item_b_id.
Assuming the table name is item_links, you could try this:
DELIMITER |
CREATE TRIGGER ensure_a_b_before_insert BEFORE INSERT ON item_links
FOR EACH ROW
BEGIN
IF NEW.item_a_id > NEW.item_b_id THEN
SET #tmp = NEW.item_b_id;
SET NEW.item_b_id = NEW.item_a_id;
SET NEW.item_a_id = #tmp;
END IF;
END;
|
CREATE TRIGGER ensure_a_b_before_update BEFORE UPDATE ON item_links
FOR EACH ROW
BEGIN
IF NEW.item_a_id > NEW.item_b_id THEN
SET #tmp = NEW.item_b_id;
SET NEW.item_b_id = NEW.item_a_id;
SET NEW.item_a_id = #tmp;
END IF;
END;
|
DELIMITER ;
Here's what I got when I test inserting:
mysql> INSERT INTO `item_links` (`item_a_id`, `item_b_id`, `value`)
-> VALUES ('1', '2', 'a')
-> , ('3', '2', 'b')
-> , ('4', '1', 'c');
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM `item_links`;
+-----------+-----------+-------+
| item_a_id | item_b_id | value |
+-----------+-----------+-------+
| 1 | 2 | a |
| 2 | 3 | b |
| 1 | 4 | c |
+-----------+-----------+-------+
3 rows in set (0.00 sec)
Update works, too:
mysql> UPDATE `item_links`
-> SET `item_a_id` = 100, `item_b_id` = 20
-> WHERE `item_a_id` = 1 AND `item_b_id` = 2;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT * FROM `item_links`;
+-----------+-----------+-------+
| item_a_id | item_b_id | value |
+-----------+-----------+-------+
| 20 | 100 | a |
| 2 | 3 | b |
| 1 | 4 | c |
+-----------+-----------+-------+
3 rows in set (0.00 sec)