on duplicate key update result affecting all the rows of the table - mysql

I have a table of this structure:
mysql> desc securities;
+-----------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+-------------+------+-----+---------+-------+
| sym | varchar(19) | NO | PRI | | |
| bqn | int(11) | YES | | NULL | |
| sqn | int(11) | YES | | NULL | |
| tqn | int(11) | YES | | NULL | |
+-----------------+-------------+------+-----+---------+-------+
4 rows in set (0.01 sec)
I am trying to do a select and an update within the same query, so the reason I have chosen
insert into securities (sym, bqn, sqn , tqn) values('ANK', 50,0,1577798)
on duplicate key update bqn=bqn+50 , sqn=sqn+0 , tqn=tqn+1577798;
When I ran the above I observed it is in fact changing the values for all the other rows also.
Is this behaviour expected? I am using MySQL Database.

Your fiddle is missing the key, and the INSERT statement in the right panel (where it does not belong in the first place) is using different column names … *sigh*
Define the symbol column as PRIMARY KEY – and use the VALUES() syntax to get the values to add in the ON UPDATE part, so that you don’t have to repeat them every single time:
insert into securities
(symbol, buyerquan, sellerquan , totaltradedquan)
values('BANKBARODA', 73, 0, 4290270)
on duplicate key update
buyerquan=buyerquan+VALUES(buyerquan),
sellerquan=sellerquan+VALUES(sellerquan),
totaltradedquan=totaltradedquan+VALUES(totaltradedquan);
Works perfectly fine, result values are as to be expect from the input: http://sqlfiddle.com/#!2/21638f/1

Related

MySQL increase the old value by the new value when inserting new records by "on duplicate key update"

I've created a new table like this:
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| first | varchar(100) | NO | PRI | NULL | |
| last | varchar(400) | NO | PRI | NULL | |
| source | varchar(100) | NO | | NULL | |
| count | int | YES | | 1 | |
+------------+--------------+------+-----+---------+-------+
And I try to insert multiple records to this table using this:
insert into my_table(first,last,source,count) values ('a','b','c',50),('a','b','c',20),('d','e','f',30) on duplicate key update count = count + 1;
After insert, this is the content of the table:
+------------+-----------+--------+-------+
| first | last | source | count |
+------------+-----------+--------+-------+
| a | b | c | 2 |
| d | e | f | 1 |
+------------+-----------+--------+-------+
However, I'd like the count to be updated by the numbers provided in the values of the new records (i.e., 50, 20, and 30 in the provided example). So, the table should look like this:
+------------+-----------+--------+-------+
| first | last | source | count |
+------------+-----------+--------+-------+
| a | b | c | 70 |
| d | e | f | 30 |
+------------+-----------+--------+-------+
Is it possible to achieve this using "on duplicate key update" in MySQL? Or is there any other efficient way to achieve this? The table will be very large (with millions of rows).
VALUES() is the method to use, as GMB mentioned, if you are on a mysql version older than 8.0.19. However, it was deprecated as of 8.0.20, if you are using mysql 8.0.19 or newer its recommended to give an alias to the rows being inserted, and then refer to the values of the inserts by the alias like this:
insert into my_table (first, last, source, count)
values ('a','b','c',50), ('a','b','c',20), ('d','e','f',30) as newRow
on duplicate key update count = count + newRow.count;
More information can be found here: https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
Consider the VALUES() syntax, that you can use in the on duplicate key clause to refer to the column value that would otherwise have been inserted:
insert into my_table(first, last, source, count)
values ('a','b','c',50), ('a','b','c',20), ('d','e','f',30)
on duplicate key update count = count + VALUES(count);
Note: first, last and source are MySQL keywords. I would not recommend using them as column names.

MySQL: Strange behavior of UPDATE query (ERROR 1062 Duplicate entry)

I have a MySQL database the stores news articles with the publications date (just day information), the source, and category. Based on these I want to generate a table that holds the article counts w.r.t. to these 3 parameters.
Since for some combinations of these 3 parameters there might be no article, a simple GROUP BY won't do. I therefore first generate a table news_article_counts with all possible combinations of the 3 parameters, and an default article_count of 0 -- like this:
SELECT * FROM news_article_counts;
+--------------+------------+----------+---------------+
| published_at | source | category | article_count |
+------------- +------------+----------+---------------+
| 2016-08-05 | 1826089206 | 0 | 0 |
| 2016-08-05 | 1826089206 | 1 | 0 |
| 2016-08-05 | 1826089206 | 2 | 0 |
| 2016-08-05 | 1826089206 | 3 | 0 |
| 2016-08-05 | 1826089206 | 4 | 0 |
| ... | ... | ... | ... |
+--------------+------------+----------+---------------+
For testing, I now created a temporary table tmp as the GROUP BY result from the original news article table:
SELECT * FROM tmp LIMIT 6;
+--------------+------------+----------+-----+
| published_at | source | category | cnt |
+--------------+------------+----------+-----+
| 2016-08-05 | 1826089206 | 3 | 1 |
| 2003-09-19 | 1826089206 | 4 | 1 |
| 2005-08-08 | 1826089206 | 3 | 1 |
| 2008-07-22 | 1826089206 | 4 | 1 |
| 2008-11-26 | 1826089206 | 8 | 1 |
| ... | ... | ... | ... |
+--------------+------------+----------+-----+
Given these two tables, the following query works as expected:
SELECT * FROM news_article_counts c, tmp t
WHERE c.published_at = t.published_at AND c.source = t.source AND c.category = t.category;
But now I need to update the article_count of table news_article_counts with the values in table tmp where the 3 parameters match up. For this I'm using the following query (I've tried different ways but with the same results):
UPDATE
news_article_counts c
INNER JOIN
tmp t
ON
c.published_at = t.published_at AND
c.source = t.source AND
c.category = t.category
SET
c.article_count = t.cnt;
Executing this query yields this error:
ERROR 1062 (23000): Duplicate entry '2018-04-07 14:46:17-1826089206-1' for key 'uniqueIndex'
uniqueIndex is a joint index over published_at, source, category of table news_article_counts. But this shouldn't be a problem since I do not -- as far as I can tell -- update any of those 3 values, only article_count.
What confuses me most is that in the error it mentions the timestamp I executed the query (here: 2018-04-07 14:46:17). I have no absolutely idea where this comes into play. In fact, some rows in news_article_counts now have 2018-04-07 14:46:17 as value for published_at. While this explains the error, I cannot see why published_at gets overwritten with the current timestamp. There is no ON UPDATE CURRENT_TIMESTAMP on this column; see:
CREATE TABLE IF NOT EXISTS `test`.`news_article_counts` (
`published_at` TIMESTAMP NOT NULL,
`source` INT UNSIGNED NOT NULL,
`category` INT UNSIGNED NOT NULL,
`article_count` INT UNSIGNED NOT NULL DEFAULT 0,
UNIQUE INDEX `uniqueIndex` (`published_at` ASC, `source` ASC, `category` ASC))
ENGINE = MyISAM
DEFAULT CHARACTER SET = utf8mb4;
What am I missing here?
UPDATE 1: I actually checked the table definition of news_article_counts in the database. And there's indeed the following:
mysql> SHOW COLUMNS FROM news_article_counts;
+---------------+------------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+-------------------+-----------------------------+
| published_at | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| source | int(10) unsigned | NO | | NULL | |
| category | int(10) unsigned | NO | | NULL | |
| article_count | int(10) unsigned | NO | | 0 | |
+---------------+------------------+------+-----+-------------------+-----------------------------+
But why is on update CURRENT_TIMESTAMP set. I double and triple-checked my CREATE TABLE statement. I removed the joint index, I added an artificial primary key (auto_increment). Nothing help. I've even tried to explicitly remove these attributes from published_at with:
ALTER TABLE `news_article_counts` CHANGE `published_at` `published_at` TIMESTAMP NOT NULL;
Nothing seems to work for me.
It looks like you have the explicit_defaults_for_timestamp system variable disabled. One of the effects of this is:
The first TIMESTAMP column in a table, if not explicitly declared with the NULL attribute or an explicit DEFAULT or ON UPDATE attribute, is automatically declared with the DEFAULT CURRENT_TIMESTAMP and ON UPDATE CURRENT_TIMESTAMP attributes.
You could try enabling this system variable, but that could potentially impact other applications. I think it only takes effect when you're actually creating a table, so it shouldn't affect any existing tables.
If you don't to make a system-level change like this, you could add an explicit DEFAULT attribute to the published_at column of this table, then it won't automatically add ON UPDATE.

Error 1062: duplicate entry in non-primary field

I have recently started to work with MySQL for my study job and now face following problem:
My predecessor created a textmining table of the following structure:
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| TokenId | int(11) | NO | PRI | 0 | |
| Value | varchar(255) | YES | | NULL | |
| Frequency | int(11) | YES | | NULL | |
| PMID | int(11) | YES | MUL | NULL | |
+------------+--------------+------+-----+---------+-------+
In the context of restructuring, I added the following column:
+------------+--------------+------+-----+---------+-------+
| NewTokenId | int(11) | YES | | NULL | |
+------------+--------------+------+-----+---------+-------+
If I now run the query:
insert into TitleToken(NewTokenId) select t.TokenId from Token as t, TitleToken as tt where t.Value = tt.Value
or even the query:
insert into TitleToken(NewTokenId) values(1);
I get following output:
ERROR 1062 (23000): Duplicate entry '0' for key 'PRIMARY'
As I said, I am relatively new to (hands-on) *SQL and it feels like a stupid mistake, but since the column NewTokenId is no primary key, not unique and even Null is YES, I thought I'd be able to insert basically anything I want.
Any hint would be appreciated... thanks in advance :)
The problem here is that you have a default value for the primary key "TokenID", if you do not insert a value for the key in your insert statement the system will automatically insert 0. However, if there is another tuple with the same value for this attribute (which is probable because the default is 0) you will get that error.
You are attempting to perform an insert into a table without providing a unique value for TokenId. By default, according to the table description, TokenId defaults to 0, you cannot have multiple identical values in that column.

ERROR 1054 (42S22): Unknown column 'marks' in 'field list'

This is a very simple MySQL query.
INSERT INTO users_questions (user_id, question_id, mcopt_id,timestamp)
VALUES (50053, 875, 3092, '2015-08-22 18:01:44');
When I use it I get
ERROR 1054 (42S22): Unknown column 'marks' in 'field list'
marks is a column in the same table whose default value is set to NULL and in the above query I don't even use the column name marks.
So why exactly am i getting the error?
Structure of table:
+-------------+-----------+------+-----+-------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-----------+------+-----+-------------------+-------+
| user_id | int(11) | NO | PRI | NULL | |
| question_id | int(11) | NO | PRI | NULL | |
| mcopt_id | int(11) | NO | | NULL | |
| timestamp | timestamp | NO | | CURRENT_TIMESTAMP | |
| marks | int(11) | NO | | NULL | |
+-------------+-----------+------+-----+-------------------+-------+
Just to make it clear I also get the error when I provide the value of marks
INSERT INTO users_questions (user_id, question_id, mcopt_id, timestamp, marks) VALUES (50053, 875, 3094, '2015-08-22 19:15:07', 1)
`
Sometimes, when you implement wrong trigger, it happens.
So just drop your trigger by using:
DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name
and it actually worked in my Mysql case. Maybe helpful for some of you.
A:
create table users_questions2
( user_id int not null,
question_id int not null,
mcopt_id int not null,
timestamp timestamp not null,
marks int not null
);
describe users_questions2;
+-------------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-----------+------+-----+-------------------+-----------------------------+
| user_id | int(11) | NO | | NULL | |
| question_id | int(11) | NO | | NULL | |
| mcopt_id | int(11) | NO | | NULL | |
| timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| marks | int(11) | NO | | NULL | |
+-------------+-----------+------+-----+-------------------+-----------------------------+
INSERT INTO users_questions2 (user_id, question_id, mcopt_id, timestamp) VALUES (50053, 875, 3092, '2015-08-22 18:01:44');
Error Code: 1364. Field 'marks' doesn't have a default value 0.047 sec
INSERT INTO users_questions2 (user_id, question_id, mcopt_id, timestamp,marks) VALUES (50053, 875, 3092, '2015-08-22 18:01:44',1);
-- 1 row(s) affected
INSERT INTO users_questions2 (user_id, question_id, mcopt_id, timestamp,marks) VALUES (50053, 875, 3092, '2015-08-22 18:01:44',null);
Error Code: 1048. Column 'marks' cannot be null 0.000 sec
B:
drop table users_questions2;
create table users_questions2
( user_id int null,
question_id int null,
mcopt_id int null,
timestamp timestamp null,
marks int null
);
describe users_questions2;
+-------------+-----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-----------+------+-----+---------+-------+
| user_id | int(11) | YES | | NULL | |
| question_id | int(11) | YES | | NULL | |
| mcopt_id | int(11) | YES | | NULL | |
| timestamp | timestamp | YES | | NULL | |
| marks | int(11) | YES | | NULL | |
+-------------+-----------+------+-----+---------+-------+
INSERT INTO users_questions2 (user_id, question_id, mcopt_id, timestamp) VALUES (50053, 875, 3092, '2015-08-22 18:01:44');
1 row(s) affected
So the only way I can get my describe table to look like yours is if they are not null columns (section A above).
Which means your columns do not accept nulls.
Edit:
show variables like "%version%";
+-------------------------+------------------------------+
| Variable_name | Value |
+-------------------------+------------------------------+
| innodb_version | 5.6.24 |
| protocol_version | 10 |
| slave_type_conversions | |
| version | 5.6.24-log |
| version_comment | MySQL Community Server (GPL) |
| version_compile_machine | x86_64 |
| version_compile_os | Win64 |
+-------------------------+------------------------------+
Column marks is defined as not nullable with default value NULL. I suppose that is the problem. You should assign a value in the insert or change default value
The table you are reference in the query does not have a column named marks. First check that you have to correct query which failed and not looking at a different query. Specially when the error message say the table doesn't have a marks column and your query doesn't even have this column written then you are looking at the wrong query. Then check the table you are using and that it has a column named marks. Your error message has nothing to do about NULL or NOT NULL.
I found a similar problem with the command:
INSERT INTO `rel_artsizeprice` (`art_id`, `artsize_id`, `price_tax`) VALUES (1, 3, 2.5);
ERROR 1054 (42S22): Unknown column ' 2.5' in 'field list'
If I delete the space before 2.5, it works:
INSERT INTO `rel_artsizeprice` (`art_id`, `artsize_id`, `price_tax`) VALUES (1, 3,2.5);
Query OK, 1 row affected (0.06 sec)
I a large list of such insert values there are only some places, which generates an error. So I think, that there is an error in the source of the commandline tool (readline or mysql).
I used:
mysql --version
mysql Ver 15.1 Distrib 10.0.26-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2
The table You had created will not accept the Null value you have to define the value of marks. It cannot be null probably you have used the not null. if you want your column to be null better use simply null.
One more technical Mistake you are doing here is defining two primary keys. A table should only have one primary key it can have enormous unique keys but primary key should only be one.
You must use single quote marks before and after each record.
INSERT INTO users_questions (user_id, question_id, mcopt_id,timestamp)
VALUES ('50053', '875', '3092', '2015-08-22 18:01:44');

Can a primary key be empty? If yes why did this alter cause this result?

I have the following table:
mysql> DESC my_contacts;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | varchar(20) | NO | PRI | | |
| location | varchar(20) | YES | | NULL | |
| city | varchar(20) | YES | | NULL | |
| state | varchar(2) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
4 rows in set (0.01 sec)
If I do a select all I get:
mysql> SELECT * FROM my_contacts;
+----+--------------+------+-------+
| id | location | city | state |
+----+--------------+------+-------+
| 1 | Chester,NJ | NULL | NULL |
| 2 | Katy,TX | NULL | NULL |
| 3 | San Mateo,CA | NULL | NULL |
+----+--------------+------+-------+
3 rows in set (0.00 sec)
I run the following command:
INSERT INTO my_contacts (city,state)
VALUES
(SUBSTRING_INDEX(location,',',1),RIGHT(location,2));
My purpose was to populate the columns city and state with the part before the comma and the part after the comma from the location column.
But the following happened to my table:
mysql> INSERT INTO my_contacts (city,state)
-> VALUES
-> (SUBSTRING_INDEX(location,',',1),RIGHT(location,2));
Query OK, 1 row affected (0.02 sec)
mysql> SELECT * FROM my_contacts;
+----+--------------+------+-------+
| id | location | city | state |
+----+--------------+------+-------+
| | NULL | NULL | NULL |
| 1 | Chester,NJ | NULL | NULL |
| 2 | Katy,TX | NULL | NULL |
| 3 | San Mateo,CA | NULL | NULL |
+----+--------------+------+-------+
4 rows in set (0.00 sec)
I get a record and the id which is the primary key is empty. How is this possible?
I mean it is not NULL but a primary key is not supposed to be empty either right?
You defined your id field as a varchar, which is a dumb idea when you're using it to store integers. an empty field is NOT null. a zero-length string is still a valid string, and therefore a valid id value as far as your table is concerned. Try inserting ANOTHER blank string and you'll get a primary key violation:
INSERT INTO yourtable (id) VALUES (''); // will not work
The id field should be an int type. That'd disallow "empty" values.
primary keys are unique so if you alter the table, then the second row will attempt to add an empty value and fail. as a result, it will attempt the next possible value. If you want the first value not to be empty, you can set a default value.
It's not empty. It's probably an empty string. Note that the datatype is varchar(20).
Well, you didn't assign a value to the primary key field, so the default is NULL.
.
You want to modify the table so the primary key is auto_increment.
You can use a varchar as a foreign key related to another database table, but if you wish to use it as a numerical key, you should utilize a numerical data type such as int.
I know this doesn't answer the precise question regarding the primary key, but as your question does point out the fact you are also having issues parsing out the city and state from your location column, here's the query you would want to use (note you want an UPDATE to modify existing rows, not an INSERT which will add new rows rather than columns):
UPDATE my_contacts
SET
city = substr(location, 1, locate(',', location) - 1),
state = substr(location, locate(',', location) + 1);