I have a MySQL 5.5 Trigger which is failing due to multiple results being returned. The problem is, I'm attempting to return one result at a time by using a unique field 'id' field.
CREATE TRIGGER insert_com_app_title AFTER INSERT
ON com_apps FOR EACH ROW
INSERT IGNORE INTO com_myisam_app_titles(id_company, title) VALUES((SELECT a.id_company, b.title FROM com_apps a JOIN epf_application b ON a.application_id = b.application_id WHERE NEW.id = a.id));
The (relevant) table structure is as follows:
**com_apps**
id, id_company, application_id
**com_myisam_app_titles**
id_company, title
**epf_application**
application_id, title
I'm assuming something is wrong with New.ID, however even if the value is wrong the id field of com_apps is a PRIMARY auto-incremented key, so even in that case I would return one incorrect result.
You shouldn't nest select in a values statement. SQL offers much better syntax:
INSERT IGNORE INTO com_myisam_app_titles(id_company, title)
SELECT a.id_company, b.title
FROM com_apps a JOIN
epf_application b
ON a.application_id = b.application_id
WHERE NEW.id = a.id;
The problem is that the underlying query is returning more than one row and the values statement cannot handle that. The above fixes that problem.
If you only want one row, add a limit 1 to the query:
INSERT IGNORE INTO com_myisam_app_titles(id_company, title)
SELECT a.id_company, b.title
FROM com_apps a JOIN
epf_application b
ON a.application_id = b.application_id
WHERE NEW.id = a.id
LIMIT 1;
Related
So I have a situation where I have two tables. One is the base table (called _Keys in this example), with a unique primary key. Then there is another table with multiple rows of a data for each id in _Keys (this second table is Extra).
I need to select the largest value for each primary key in _Keys from Extra. I have made an SQLFiddle to model the problem here.
This is the query I'm currently using, but the issue is that it will only select one value for the Extra table, not one value per row.
Select * from _Keys
LEFT JOIN
(Select * from Extra ORDER BY value2 DESC LIMIT 1) as e
ON e.id = _Keys.id;
For my example SQL Fiddle I used this database schema:
CREATE TABLE _Keys(id int, value int);
INSERT INTO _Keys (id, value) VALUES (1, 5),(2, 3),(3, 4);
CREATE TABLE Extra(id int, value2 int);
INSERT INTO Extra (id, value2) VALUES (1, 3),(1, 1),(2, 4),(2, 6),(3, 3),(3, 5);
Basically my result is here. Only the first row from the _Keys table gets its data from the second table.
In MySQL, how can I achieve selecting one row from Extras for each row in _Keys?
I believe I understand what you are trying to do but I'm not sure.
You are getting NULL values because of the LIMIT, it only returns the first row. You also need to use GROUP BY.
To get the largest value, your can use MAX.
Try this.
SELECT * from _Keys
LEFT JOIN
(SELECT id, MAX(value2) AS value2 FROM Extra GROUP BY id) as e
ON e.id = _Keys.id;
Your joined table Select * from Extra ORDER BY value2 DESC LIMIT 1 will contain only one row because of LIMIT. Try this:
Select * from _Keys
LEFT JOIN
(Select id, max(value2) from Extra group by id) as e
ON e.id = _Keys.id;
You can try this query with better performance :
SELECT k.id, MAX(e.value2) AS value2
FROM _Keys k
INNER JOIN Extra e
ON (k.id = e.id)
GROUP BY k.id;
I ran this query:
Insert into transaction(matric,surname,other,level,bk_id,bk_title)
values(
(select matric,surname,others,level from member_master),
(select isbn,bk_title from book_master)
)
but I got this error:
column count doesn't match value count at row 1
You have to use the same columns which you have mentioned in the insert statement. Presently your insert statement mentions matric,surname,other,level,bk_id,bk_title columns whereas the columns in select are different. Try like this:
Insert into transaction(matric,surname,other,level,bk_id,bk_title)
values
(select m.matric,m.surname,m.others,m.level,b.isbn,b.bk_title
from member_master m inner join book_master b on m.id = b.id)
Assuming that the two tables are linked with the ID column
I have a table that has training history that has been modified by many different users over the years. This has cause the same training record to be entered twice. I want to create a table that replicates the main table and insert all duplicate records.
What constitutes a duplicate record is if the employee_id, course_code, and completion_date all match.
I can create the duplicate table and I have a select statement that appears to pull duplicates, but it pulls only one of them and I need it to pull both (or more) of them. This is because one person may have entered the training record with a different course name but the id, code, and date are the same so it is a duplicate entry. So by pulling all the duplicates I can validate that that is the case.
Here is my SELECT statement:
SELECT *
FROM
training_table p1
JOIN
training_table p2 ON (
p1.employee_id = p2.employee_id
AND p1.course_code = p2.course_code
AND p1.completion.date = p2.completion_date)
GROUP BY p1.ssn;
The query runs and returns what appear to be unique rows. I would like all of the duplicates. And whenever I try to INSERT it into an identical table I get an error stating my column count doesn't match my value count.
Any help would be great.
This will select any duplicate rows for insertion into your new table.
SELECT p1.*
FROM training_table p1
JOIN
(SELECT employee_id, course_code, completion_date
FROM training_table
GROUP BY employee_id, course_code, completion_date
HAVING COUNT(*) > 1
) dups
ON p1.employee_id = dups.employee_id
AND p1.course_code = dups.course_code
AND p1.completion_date = dups.completion_date
;
Try to use CROSS JOIN (Cartesian Product Join) instead JOIN only. For insert try INSERT INTO TABLE (column1, column2, column3) SELECT column1, column2, column3 FROM TABLE; in same order.
Thanks for the help. I had discovered the answer shortly after I posted the question (even though I had looked for the answer for over an hour :) ) Here is what I used:
SELECT *
FROM training_table mto
WHERE EXISTS
(
SELECT 1
FROM training_table mti
WHERE mti.employee_id = mto.employee_ie
AND mti.course_code = mto.course_code
AND mti.completion_date = mto.completion_date
LIMIT 1, 1
)
I just added the INSERT statement and it worked.
Thanks.
I have a table with some ids + titles. I want to make the title column unique, but it has over 600k records already, some of which are duplicates (sometimes several dozen times over).
How do I remove all duplicates, except one, so I can add a UNIQUE key to the title column after?
This command adds a unique key, and drops all rows that generate errors (due to the unique key). This removes duplicates.
ALTER IGNORE TABLE table ADD UNIQUE KEY idx1(title);
Edit: Note that this command may not work for InnoDB tables for some versions of MySQL. See this post for a workaround. (Thanks to "an anonymous user" for this information.)
Create a new table with just the distinct rows of the original table. There may be other ways but I find this the cleanest.
CREATE TABLE tmp_table AS SELECT DISTINCT [....] FROM main_table
More specifically:
The faster way is to insert distinct rows into a temporary table. Using delete, it took me a few hours to remove duplicates from a table of 8 million rows. Using insert and distinct, it took just 13 minutes.
CREATE TABLE tempTableName LIKE tableName;
CREATE INDEX ix_all_id ON tableName(cellId,attributeId,entityRowId,value);
INSERT INTO tempTableName(cellId,attributeId,entityRowId,value) SELECT DISTINCT cellId,attributeId,entityRowId,value FROM tableName;
DROP TABLE tableName;
INSERT tableName SELECT * FROM tempTableName;
DROP TABLE tempTableName;
Since the MySql ALTER IGNORE TABLE has been deprecated, you need to actually delete the duplicate date before adding an index.
First write a query that finds all the duplicates. Here I'm assuming that email is the field that contains duplicates.
SELECT
s1.email
s1.id,
s1.created
s2.id,
s2.created
FROM
student AS s1
INNER JOIN
student AS s2
WHERE
/* Emails are the same */
s1.email = s2.email AND
/* DON'T select both accounts,
only select the one created later.
The serial id could also be used here */
s2.created > s1.created
;
Next select only the unique duplicate ids:
SELECT
DISTINCT s2.id
FROM
student AS s1
INNER JOIN
student AS s2
WHERE
s1.email = s2.email AND
s2.created > s1.created
;
Once you are sure that only contains the duplicate ids you want to delete, run the delete. You have to add (SELECT * FROM tblname) so that MySql doesn't complain.
DELETE FROM
student
WHERE
id
IN (
SELECT
DISTINCT s2.id
FROM
(SELECT * FROM student) AS s1
INNER JOIN
(SELECT * FROM student) AS s2
WHERE
s1.email = s2.email AND
s2.created > s1.created
);
Then create the unique index:
ALTER TABLE
student
ADD UNIQUE INDEX
idx_student_unique_email(email)
;
Below query can be used to delete all the duplicate except the one row with lowest "id" field value
DELETE t1 FROM table_name t1, table_name t2 WHERE t1.id > t2.id AND t1.name = t2.name
In the similar way, we can keep the row with the highest value in 'id' as follows
DELETE t1 FROM table_name t1, table_name t2 WHERE t1.id < t2.id AND t1.name = t2.name
This shows how to do it in SQL2000. I'm not completely familiar with MySQL syntax but I'm sure there's something comparable
create table #titles (iid int identity (1, 1), title varchar(200))
-- Repeat this step many times to create duplicates
insert into #titles(title) values ('bob')
insert into #titles(title) values ('bob1')
insert into #titles(title) values ('bob2')
insert into #titles(title) values ('bob3')
insert into #titles(title) values ('bob4')
DELETE T FROM
#titles T left join
(
select title, min(iid) as minid from #titles group by title
) D on T.title = D.title and T.iid = D.minid
WHERE D.minid is null
Select * FROM #titles
delete from student where id in (
SELECT distinct(s1.`student_id`) from student as s1 inner join student as s2
where s1.`sex` = s2.`sex` and
s1.`student_id` > s2.`student_id` and
s1.`sex` = 'M'
ORDER BY `s1`.`student_id` ASC
)
The solution posted by Nitin seems to be the most elegant / logical one.
However it has one issue:
ERROR 1093 (HY000): You can't specify target table 'student' for
update in FROM clause
This can however be resolved by using (SELECT * FROM student) instead of student:
DELETE FROM student WHERE id IN (
SELECT distinct(s1.`student_id`) FROM (SELECT * FROM student) AS s1 INNER JOIN (SELECT * FROM student) AS s2
WHERE s1.`sex` = s2.`sex` AND
s1.`student_id` > s2.`student_id` AND
s1.`sex` = 'M'
ORDER BY `s1`.`student_id` ASC
)
Give your +1's to Nitin for coming up with the original solution.
Deleting duplicates on MySQL tables is a common issue, that usually comes with specific needs. In case anyone is interested, here (Remove duplicate rows in MySQL) I explain how to use a temporary table to delete MySQL duplicates in a reliable and fast way (with examples for different use cases).
In this case, something like this should work:
-- create a new temporary table
CREATE TABLE tmp_table1 LIKE table1;
-- add a unique constraint
ALTER TABLE tmp_table1 ADD UNIQUE(id, title);
-- scan over the table to insert entries
INSERT IGNORE INTO tmp_table1 SELECT * FROM table1 ORDER BY sid;
-- rename tables
RENAME TABLE table1 TO backup_table1, tmp_table1 TO table1;
I have mySQL database with some duplicate entries. They have the same field - phone. But they also had fields which differs. At example I have two entries with same phone, but first entry has rating filed = default_value and second entry has rating field = 5.
So I must merge this entries and only then delete duplicates...
More common example:
entry1.phone==123
entry1.phone==etry2.phone
entry1.rating!=entry2.phone
entry1.rating==default_value(0)
entry2.rating==5
merge
entry1.phone==123
entry1.rating==5
entry2 is deleted
I don't think you can do this in SQL efficiently. One slow way to do it is something like:
CREATE TEMPORARY TABLE tmp_table (...);
INSERT INTO tmp_table SELECT phone, max(rating) FROM table GROUP BY phone;
TRUNCATE table;
INSERT INTO table SELECT * FROM tmp_table;
A better way would be a stored procedure or an external script. Select all rows from the table ordered by phone and do the grouping/merging/deleting manually (iterate over the results, compare to the phone value from the previous row, if it's different you have a new group, etc.). Writing stored procedures in MySQL is painful though, so I'm not going to write the code for you. :)
It sounds like you don't really need to merge any records if you are just trying to update the first record with the non-default rating. I think you can just delete any records with the default rating.
Select a.*
from tbl a
inner join tbl b
on a.Phone = b.Phone
and a.Rating < b.Rating
Delete a
from tbl a
inner join tbl b
on a.Phone = b.Phone
and a.Rating < b.Rating
If you truly have to update the first record and delete the second record, you can do something similar if you have an autoincrement ID. The next example is what I would do to update the first record if an ID exists. This is only reliable if you only have phone numbers duplicated one time.
Update a
Set a.Rating = b.Rating
from tbl a
inner join tbl b
on a.Phone = b.Phone
and a.Rating < b.Rating
and a.ID < b.ID
Delete a
from tbl a
inner join tbl b
on a.Phone = b.Phone
and a.Rating = b.Rating
and b.ID > a.ID
Hope this helps.
-Ranthalion