I have a table name named 'employee'. Table creation code given below:
create table employee(name varchar(50),ph_no varchar(10),e_id varchar(5),pay_scale varchar(5),year varchar(4));
The table content is like below:
insert into employee(name,ph_no,pay_scale,year) values('AMIT','123456','PL-10','2019');
insert into employee(name,ph_no,pay_scale,year) values('AMIT','123456','PL-10','2020');
insert into employee(name,ph_no,pay_scale,year) values('AMIT','123456','PL-11','2021');
insert into employee(name,ph_no,pay_scale,year) values('AMIT','123456','PL-11','2022');
+------+--------+------+-----------+------+
| name | ph_no | e_id | pay_scale | year |
+------+--------+------+-----------+------+
| AMIT | 123456 | NULL | PL-10 | 2019 |
| AMIT | 123456 | NULL | PL-10 | 2020 |
| AMIT | 123456 | NULL | PL-11 | 2021 |
| AMIT | 123456 | NULL | PL-11 | 2022 |
+------+--------+------+-----------+------+
Now I want to update 'e_id', first it will check whether the same e_id is in the table anywhere or not, if it is not in the table then only it will update the rows with given e_id, else it will not going to update.
So, my upgradation query is below:
update employee
set e_id='0132'
where concat_ws(',',name,ph_no,pay_scale)=concat_ws(',','AMIT','123456','PL-10')
and not exists (select e_id
from employee
group by e_id
having count(*)>=1);
But it is giving the following error:
ERROR 1093 (HY000): You can't specify target table 'employee' for update in FROM clause
I have tried the below query:
update employee set e_id='0132' where
concat_ws(',',name,ph_no,pay_scale)=concat_ws(',','AMIT','123456','PL-10') and
e_id not in
(select e_id from
(select e_id from employee group by e_id having count(*)>=1) as t);
But this also cannot update the table and showing below result:
Query OK, 0 rows affected (0.01 sec)
Rows matched: 0 Changed: 0 Warnings: 0
also tried the below code:
update employee set
employee.e_id='0132' where
employee.e_id not in (select * from
(select f.e_id from
employee f inner join employee b on
b.name=f.name and b.ph_no=f.ph_no and b.pay_scale=f.pay_scale) as tmp)
and employee.name='AMIT' and employee.ph_no='123456' and employee.pay_scale='PL-10';
but this also cannot update the table and gives below result:
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
Please help. Thank you in advance.
NULL doesn't play the way some people expect with NOT IN: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=24c176ff4d4e2c52309aaca14cc121c5 So, just put WHERE e_id IS NOT NULL in the sub-query. Also, HAVING COUNT(*) >= 1 can be removed as it's always going to return a value of 1 or more...
update
employee
set
e_id='0132'
where
name = 'AMIT'
and ph_no = '123456'
and pay_scale = 'PL-10'
and e_id not in (select e_id from
(select distinct e_id
from employee
where e_id IS NOT NULL
)
as t
);
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=2a0b036a7d1db9138e3ab29af3d346f8
Related
select tf.id from text_fields as tf WHERE tf.study_id NOT IN (select id from studies)
I'm using this to find orphaned records and it works fine.
However when I try and assign the result to a var, using SET, I get the "Subquery returns more than 1 row" error.
SET #text_field_ids := (select tf.id from text_fields as tf WHERE tf.study_id NOT IN (select id from studies))
For context I'm want to use the var to then text_fields records, e.g.
DELETE from text_fields WHERE id IN #text_field_ids
Incidentally I have tried passing the subquery directly to DELETE such as:
DELETE from text_fields WHERE id IN (select id from text_fields as tf WHERE tf.study_id NOT IN (select id from studies))
But this gives the error You can't specify target table 'text_fields' for update in FROM clause because apparently you can't use the table being deleted from in the WHERE clause.
You can use temporary table :
create temporary table if not exists mytmptable select tf.id as id from text_fields as tf WHERE tf.study_id NOT IN (select id from studies)
and then you can use it in delete :
DELETE from text_fields WHERE id IN (select Id from mytmptable)
The usual way round a ERROR 1093 (HY000): Table 'USERS' is specified twice, both as a target for 'DELETE' and as a separate source for data error is to push the select a bit deeper for example see second delete below-
MariaDB [sandbox]> SELECT ID FROM USERS;
+----+
| ID |
+----+
| 1 |
| 2 |
| 3 |
| 6 |
| 7 |
| 8 |
| 10 |
| 12 |
| 14 |
| 15 |
| 16 |
| 17 |
| 18 |
| 19 |
+----+
14 rows in set (0.00 sec)
MariaDB [sandbox]>
MariaDB [sandbox]> DELETE FROM USERS
-> WHERE ID IN (SELECT ID FROM USERS WHERE ID IN (18,19))
-> ;
ERROR 1093 (HY000): Table 'USERS' is specified twice, both as a target for 'DELETE' and as a separate source for data
MariaDB [sandbox]>
MariaDB [sandbox]> DELETE FROM USERS
-> WHERE ID IN (SELECT ID FROM (SELECT ID FROM USERS WHERE ID IN (18,19)) U )
-> ;
Query OK, 2 rows affected (0.11 sec)
MariaDB [sandbox]>
MariaDB [sandbox]> SELECT ID FROM USERS;
+----+
| ID |
+----+
| 1 |
| 2 |
| 3 |
| 6 |
| 7 |
| 8 |
| 10 |
| 12 |
| 14 |
| 15 |
| 16 |
| 17 |
+----+
12 rows in set (0.00 sec)
Since the query
select tf.id from text_fields as tf WHERE tf.study_id NOT IN (select id from studies)
returns more than 1 row, assigning the recordset to a variable fails. however if you really need to assign it to a variable you can do it as
select `tf`.`id` from `text_fields` as tf WHERE `tf`.`study_id` NOT IN
(select `id` from `studies`) into #text_field_ids;
or
SELECT #text_field_ids := `tf`.`id` from `text_fields` as tf WHERE `tf`.`study_id` NOT IN
(select `id` from `studies`);
more info about this is here
Alternatively, If your db user has create temporary table priviliges, you can create a temporary table, to select your records into that lives through your session. Please note that not to suffer performance penalty, the temporary table is created with memory engine.
CREATE TEMPORARY TABLE IF NOT EXISTS temp_table ( INDEX(`id`) )
ENGINE=Memory
AS (
select `tf`.`id` from `text_fields` as `tf` WHERE `tf`.`study_id` NOT IN (select `id` from `studies`)
);
# and remove the records with
delete from `text_fields` where id in (select `id` from `temp_table`)
I need updating on the same MySQL table the row when the values of columns xID and ID are equals.
This is one example:
mysql> SELECT
Euro,
ALMACEN,
Imagen,
xID,
ID
FROM
`tbl_g`
WHERE
xID IN (2025)
OR ID IN (2025);
+--------+----------+--------+------+------+
| Euro | ALMACEN | Imagen | xID | ID |
+--------+----------+--------+------+------+
| 7742,8 | ARGUALAS | NULL | NULL | 2025 |
| NULL | EMPALME | | 2025 | 4441 |
+--------+----------+--------+------+------+
2 rows in set
I have tried this SQL Update query without success, because the row with xID number 2025 not update with values of row with ID 2025 :
mysql> UPDATE `tbl_g` kkk,
`tbl_g` jjj
SET kkk.Euro = jjj.Euro
WHERE
kkk.ID = jjj.xID
AND kkk.xID IS NOT NULL;
Query OK, 0 rows affected
Rows matched: 0 Changed: 0 Warnings: 0
How to do resolve this?
Can you help me?
Thank you in advance for any help, really appreciated.
Update the table using a JOIN (self Join) and use a WHERE clause to check the column Euro is null.
Query
update `tbl_g` as `t1`
join `tbl_g` as `t2`
on `t1`.`xID` = `t2`.`ID`
set `t1`.`Euro` = `t2`.`Euro`
where `t1`.`Euro` is null;
Note: There is a chance for multiple rows which satisfy the condition.
You need a self join for example
drop table if exists t;
create table t(Euro decimal(10,2) , ALMACEN varchar(20), Imagen int, xID int, ID int);
insert into t values
( 7742.80 , 'ARGUALAS' , NULL , NULL , 2025),
( NULL , 'EMPALME' , null , 2025 , 4441);
update t t1 join t t2 on t2.xid = t1.id
set t2.euro = t1.euro;
select * from t;
MariaDB [sandbox]> select * from t;
+---------+----------+--------+------+------+
| Euro | ALMACEN | Imagen | xID | ID |
+---------+----------+--------+------+------+
| 7742.80 | ARGUALAS | NULL | NULL | 2025 |
| 7742.80 | EMPALME | NULL | 2025 | 4441 |
+---------+----------+--------+------+------+
2 rows in set (0.00 sec)
The problem is related to autoincrement with mysql. What I'm trying to achieve is to increment an ID value based on the customer number. So basically i insert data sets without any order into a table. Each time a new customer is inserted, i would like the id column to be incremented, but of course kept for every row related to the customer, see the table below. Is there any way to achieve that via sql? I tried my luck with multiple primary keys and also looked into partioning, but was not able to figure it out by myself.
you can use a query like this:
INSERT INTO autoinc (cid,info,customer)
SELECT
COALESCE(max(cid),0) +1
, 'A Customer 1'
, 12345
FROM autoinc
WHERE customer = 12345;
sample
mysql> SELECT * from autoinc;
Empty set (0,00 sec)
mysql> INSERT INTO autoinc (cid,info,customer)
-> SELECT
-> COALESCE(max(cid),0) +1
-> , 'A Customer 1'
-> , 12345
-> FROM autoinc
-> WHERE customer = 12345;
Query OK, 1 row affected (0,00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> SELECT * from autoinc;
+----+------+--------------+----------+
| id | cid | info | customer |
+----+------+--------------+----------+
| 1 | 1 | A Customer 1 | 12345 |
+----+------+--------------+----------+
1 row in set (0,00 sec)
mysql> INSERT INTO autoinc (cid,info,customer)
-> SELECT
-> COALESCE(max(cid),0) +1
-> , 'A Customer 1'
-> , 12345
-> FROM autoinc
-> WHERE customer = 12345;
Query OK, 1 row affected (0,00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> SELECT * from autoinc;
+----+------+--------------+----------+
| id | cid | info | customer |
+----+------+--------------+----------+
| 1 | 1 | A Customer 1 | 12345 |
| 2 | 2 | A Customer 1 | 12345 |
+----+------+--------------+----------+
2 rows in set (0,00 sec)
mysql> INSERT INTO autoinc (cid,info,customer)
-> SELECT
-> COALESCE(max(cid),0) +1
-> , 'B Customer 2'
-> , 9876
-> FROM autoinc
-> WHERE customer = 9876;
Query OK, 1 row affected (0,00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> SELECT * from autoinc;
+----+------+--------------+----------+
| id | cid | info | customer |
+----+------+--------------+----------+
| 1 | 1 | A Customer 1 | 12345 |
| 2 | 2 | A Customer 1 | 12345 |
| 3 | 1 | B Customer 2 | 9876 |
+----+------+--------------+----------+
3 rows in set (0,00 sec)
mysql> INSERT INTO autoinc (cid,info,customer)
-> SELECT
-> COALESCE(max(cid),0) +1
-> , 'A Customer 1'
-> , 12345
-> FROM autoinc
-> WHERE customer = 12345;
Query OK, 1 row affected (0,00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> SELECT * from autoinc;
+----+------+--------------+----------+
| id | cid | info | customer |
+----+------+--------------+----------+
| 1 | 1 | A Customer 1 | 12345 |
| 2 | 2 | A Customer 1 | 12345 |
| 3 | 1 | B Customer 2 | 9876 |
| 4 | 3 | A Customer 1 | 12345 |
+----+------+--------------+----------+
4 rows in set (0,00 sec)
mysql>
What you probably need is to have different values for ID for each customer. The easiest way to achieve this is to use an AUTO_INCREMENT column as PK of your table.
It is an implementation detail that for consecutively inserted rows an AUTO_INCREMENT column has consecutive values. And the previous statement is not even true. It just happens some times, it is not guaranteed. If an INSERT statement is enclosed in a transaction that is rolled back, the value generated by that insert is skipped. Also, if an INSERT statements that use ON DUPLICATE KEYS UPDATE tries to insert many rows but some of them already exist in the table then the IDs generated for the duplicate keys are skipped.
What I want to stress out is that there is no point trying to get consecutive values using an AUTO_INCREMENT column and it is not even possible.
Back to your problem, if the column ID is the PK of the table and its type is INT AUTO_INCREMENT then MySQL guarantees there won't be two rows having the same value in the ID column and this also satisfies your need to have different values for ID for all the rows with the same value in customer.
You could procedurally do this using a stored procedure, which I won't elaborate on (unless requested) as it isn't a simple query (as you're asking for).
A hacky solution would be to bulk insert into a new joining table:
CREATE TABLE auto_inc_customer_id (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
customer_id INT UNSIGNED NOT NULL, -- Could/should add a FK constraint
PRIMARY KEY (id)
) ENGINE=innodb;
INSERT INTO auto_inc_customer_id SELECT NULL, DISTINCT(Customer) FROM YourExistingTable;
See: http://dev.mysql.com/doc/refman/5.7/en/ansi-diff-select-into-table.html
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I remove duplicate rows?
Remove duplicates using only a MySQL query?
I have a large table with ~14M entries. The table type is MyISAM ans not InnoDB.
Unfortunately, I have some duplicate entries in this table that I found with the following request :
SELECT device_serial, temp, tstamp, COUNT(*) c FROM up_logs GROUP BY device_serial, temp, tstamp HAVING c > 1
To avoid these duplicates in the future, I want to convert my current index to a unique constraint using SQL request :
ALTER TABLE up_logs DROP INDEX UK_UP_LOGS_TSTAMP_DEVICE_SERIAL,
ALTER TABLE up_logs ADD INDEX UK_UP_LOGS_TSTAMP_DEVICE_SERIAL ( `tstamp` , `device_serial` )
But before that, I need to clean up my duplicates!
My question is : How can I keep only one entry of my duplicated entries? Keep in mind that my table contain 14M entries, so I would like avoid loops if it is possible.
Any comments are welcome!
Creating a new unique key on the over columns you need to have as uniques will automatically clean the table of any duplicates.
ALTER IGNORE TABLE `table_name`
ADD UNIQUE KEY `key_name`(`column_1`,`column_2`);
The IGNORE part does not allow the script to terminate after the first error occurs. And the default behavior is to delete the duplicates.
Since MySQL allows Subqueries in update/delete statements, but not if they refer to the table you want to update, I´d create a copy of the original table first. Then:
DELETE FROM original_table
WHERE id NOT IN(
SELECT id FROM copy_table
GROUP BY column1, column2, ...
);
But I could imagine that copying a table with 14M entries takes some time... selecting the items to keep when copying might make it faster:
INSERT INTO copy_table
SELECT * FROM original_table
GROUP BY column1, column2, ...;
and then
DELETE FROM original_table
WHERE id IN(
SELECT id FROM copy_table
);
It was some time since I used MySQL and SQL in general last time, so I´m quite sure that there is something with better performance - but this should work ;)
This is how you can delete duplicate rows... I'll write you my example and you'll need to apply to your code. I have Actors table with ID and I want to delete the rows with repeated first_name
mysql> select actor_id, first_name from actor_2;
+----------+-------------+
| actor_id | first_name |
+----------+-------------+
| 1 | PENELOPE |
| 2 | NICK |
| 3 | ED |
....
| 199 | JULIA |
| 200 | THORA |
+----------+-------------+
200 rows in set (0.00 sec)
-Now I use a Variable called #a to get the ID if the next row have the same first_name(repeated, null if it's not).
mysql> select if(first_name=#a,actor_id,null) as first_names,#a:=first_name from actor_2 order by first_name;
+---------------+----------------+
| first_names | #a:=first_name |
+---------------+----------------+
| NULL | ADAM |
| 71 | ADAM |
| NULL | AL |
| NULL | ALAN |
| NULL | ALBERT |
| 125 | ALBERT |
| NULL | ALEC |
| NULL | ANGELA |
| 144 | ANGELA |
...
| NULL | WILL |
| NULL | WILLIAM |
| NULL | WOODY |
| 28 | WOODY |
| NULL | ZERO |
+---------------+----------------+
200 rows in set (0.00 sec)
-Now we can get only duplicates ID:
mysql> select first_names from (select if(first_name=#a,actor_id,null) as first_names,#a:=first_name from actor_2 order by first_name) as t1;
+-------------+
| first_names |
+-------------+
| NULL |
| 71 |
| NULL |
...
| 28 |
| NULL |
+-------------+
200 rows in set (0.00 sec)
-the Final Step, Lets DELETE!
mysql> delete from actor_2 where actor_id in (select first_names from (select if(first_name=#a,actor_id,null) as first_names,#a:=first_name from actor_2 order by first_name) as t1);
Query OK, 72 rows affected (0.01 sec)
-Now lets check our table:
mysql> select count(*) from actor_2 group by first_name;
+----------+
| count(*) |
+----------+
| 1 |
| 1 |
| 1 |
...
| 1 |
+----------+
128 rows in set (0.00 sec)
it works, if you have any question write me back
Greetings,
How would one go about performing two UPDATE statements in one query, for example:
UPDATE albums SET isFeatured = '0' WHERE isFeatured = '1'
combined with
UPDATE albums SET isFeatured = '1' WHERE id = '$id'
Basically, when a new album is featured, the previously featured album is switched back to normal and the newly featured one is set to active.
Thanks!
Try this:
UPDATE albums SET isFeatured = IF(id!='$id', '0','1')
When you have to do this sort of thing it is an indicator that your data model is wrong and could do with some fixing.
So, I'd recommend to add a seperate table featured_albums (FK: int id_album) and use that to determine if the album is featured.
Your update becomes
DELETE FROM featured_album; INSERT INTO featured_album SET id_album = $id;
When selecting join the tables
SELECT album.id,
album.name,
( id_album IS NOT NULL ) AS isfeatured
FROM album
LEFT JOIN featured_album ON id_album = album.id
As requested to expand on the above basically I'm suggesting adding a table that will contain a row indicating the currently selected album. This is a 1 to 1 relationship, i.e. one record in the album table has one related record in the feature_albums table. See Types of Relationship.
You remove the isFeatured field from the album table and add a new table.
CREATE TABLE `featured_album` (
`id_album` INTEGER NOT NULL,
FOREIGN KEY (id_album) REFERENCES `album` (`id`)
);
The DELETE FROM .. INSERT INTO line sets the featured album by creating an entry in the table.
The SELECT statement with the LEFT JOIN will pull in the records from the album table and join those that match from the featured_album table, in our case only one record will match so as there is one field in the featured_album table it will return NULL for all records except the featured album.
So if we did
SELECT album.id, album.name, featured_album.id_album as isFeatured0
FROM album
LEFT JOIN featured_album ON id_album = album.id
We'd get something like the following:
+----+----------------+------------+
| id | name | isFeatured |
+----+----------------+------------+
| 1 | Rumours | NULL |
| 2 | Snowblind | NULL |
| 3 | Telegraph road | 3 |
+----+----------------+------------+
i.e. a NULL for isFeatured or an ID.
By adding the ( id_album IS NOT NULL ) AS isfeatured and using the first query we get
+----+----------------+------------+
| id | name | isfeatured |
+----+----------------+------------+
| 1 | Rumours | 0 |
| 2 | Snowblind | 0 |
| 3 | Telegraph road | 1 |
+----+----------------+------------+
i.e. 0/1 for isfeatured which makes things more readable, although if you're processing the results in PHP it won't make a difference to your code.
You can use CASE WHEN statement and remember to set original value where necessary (ELSE clause below) and order CASE conditions as required (in statement below isFeatured will be 0 if row having requested id also has isFeatured = 1, to change it swap WHEN clauses).
UPDATE albums
SET isFeatured = CASE
WHEN isFeatured = '1' THEN '0'
WHEN id = '$id' THEN '1'
ELSE isFeatured
END
You just can't. You can only select one group of records that should be updated and can then only perform one operation on all of them. It's not possible to do
UPDATE x SET col1 = 1 WHERE col1 = 0 AND col1 = 0 WHERE col1 = 1;
Be careful when using functions to work around this, as they need to be evaluated for every row and this can become really expensive.
MySQL is unable to use the index when it is inside an if function:
You need an index on the function which is not possible in MySQL.
see also: How does one create an index on the date part of DATETIME field in MySql
I am using the employee test database http://dev.mysql.com/doc/employee/en/employee.html
mysql> describe employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.01 sec)
mysql> select count(*) from employees;
+----------+
| count(*) |
+----------+
| 300024 |
+----------+
1 row in set (0.37 sec)
Set all genders to male so it mimics the question.
mysql> update employees set gender = 'M';
Query OK, 1 row affected (9.11 sec)
Rows matched: 300024 Changed: 1 Warnings: 0
mysql> select emp_no, gender from employees order by emp_no limit 2;
+--------+--------+
| emp_no | gender |
+--------+--------+
| 10001 | M |
| 10002 | M |
+--------+--------+
2 rows in set (0.00 sec)
Set one employee to female.
(Notice it uses the index and is almost instant.)
mysql> update employees set gender = 'F' where emp_no = 10001;
Query OK, 1 row affected (0.14 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Now we use the suggested answer. (Notice it does not use the index and touches every row.)
mysql> update employees set gender = if(emp_no=10002, 'F', 'M');
Query OK, 2 rows affected (10.67 sec)
Rows matched: 300024 Changed: 2 Warnings: 0
Will an index help?
> mysql> create index employees_gender_idx on employees(gender);
Query OK, 300024 rows affected (21.61 sec)
Records: 300024 Duplicates: 0 Warnings: 0
> mysql> update employees set gender = if(emp_no=10001, 'F', 'M');
Query OK, 2 rows affected (9.02 sec)
Rows matched: 300024 Changed: 2 Warnings: 0
Nope.
It was also said that MySQL is only going to look at the rows that need to be changed.
mysql> update employees set gender = 'M';
Query OK, 1 row affected (8.78 sec)
Rows matched: 300024 Changed: 1 Warnings: 0
Guess not. What if use a WHERE clause?
mysql> update employees set gender = 'M' where gender ='F';
Query OK, 0 rows affected (0.03 sec)
Rows matched: 0 Changed: 0 Warnings: 0
Gee that fast, now it used the index.
Mysql has no idea what the IF function will return and must do a full table scan. Notice that WHERE really does mean where and SET really does mean set. You can't expect the DB to just arrange all your clauses to get good performance.
The correct solution is to issue two updates (which if use indexes will be almost instant.)
Notice, it was said elsewhere that MySQL will magically know only update the rows it needs to change.
Adding an alternate method to the excellent answer provided by
#too where instead of CASE IF statement is used -
UPDATE album
-> SET isFeatured = IF (
-> isFeatured = '1', '0', IF (
-> id = '$id', '1', isFeatured
-> ));
I don't think you can, or at least not in a neat or practical way.
If you're wanting to do one call from php/whatever then you can seperate them with semicolons thus:
UPDATE albums SET isFeatured = '0' WHERE isFeatured = '1';UPDATE albums SET isFeatured = '1' WHERE id = '$id';