Query to delete duplicates is generating an error - mysql

I am trying to delete duplicates from a table based on the customer phone number. The inner select query below correctly selects all duplicate records which I need to delete, however the outter delete query generates the following error:
You can't specify target table 'customers' for update in FROM clause
Query:
DELETE FROM customers WHERE id IN (SELECT id from customers GROUP BY phone HAVING COUNT(phone) > 1)

In an update and delete statement, you often cannot reference the table being modified. An easy solution is to use a join:
DELETE c
FROM customers c JOIN
(SELECT phone, COUNT(*) as cnt
FROM customers c
GROUP BY phone
) p
ON c.phone = p.phone AND cnt > 1;
Note that this deletes all duplicates from the table. Normally, you want to keep one of them:
DELETE c
FROM customers c LEFT JOIN
(SELECT phone, MIN(id) as minid
FROM customers c
GROUP BY phone
) p
ON c.id = p.minid
WHERE p.phone IS NULL;

Related

Delete duplicates with condition

I have the table contacts which contains duplicate records:
id name is_contacted created_at
I need to delete duplicates, but keep the first record(among the duplicates for each name) where is_contacted=1.
If among the record duplicates there are no records where is_contacted=1, just keep the first one.
This is what I have so far:
DELETE c1 FROM contacts c1
INNER JOIN contacts c2
WHERE
c1.id > c2.id AND
c1.name = c2.name;
Assuming that is_contacted's data type is BOOLEAN and id is the primary key of the table and this is the column that defines the order and which row should be considered first, use ROW_NUMBER window function to rank the rows of each name:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY name ORDER BY is_contacted DESC, id) rn
FROM contacts
)
DELETE t
FROM contacts t INNER JOIN cte c
ON c.id = t.id
WHERE c.rn > 1;
ORDER BY is_contacted DESC, id returns the rows with is_contacted = 1 at the top (if they exist).
For versions of MySql prior to 8.0, without support of CTEs and winow functions, use a join of the table to a query that uses aggregation to get the id of the row that you want to keep:
DELETE t
FROM contacts t
INNER JOIN (
SELECT name,
COALESCE(MIN(CASE WHEN is_contacted THEN id END), MIN(id)) id
FROM contacts
GROUP BY name
) c ON c.name = t.name AND c.id <> t.id;
Below query will filter only records what you want.
You didn't mention what is primary key in your table, so I don't know how to join this back 1:1 with your whole table.
But if you are not able to determine primary key, they you can create new table using this query, drop original one and rename it to original one.
SELECT * FROM
(
SELECT *,
ROW_NUMBER(PARTITION BY name ORDER BY CASE WHEN is_contacted = 1 THEN -999999 else is_contacted END ) AS RN_
from contacts
) c
WHERE c.RN_ = 1

Mysql: Select rows from 2 tables that are not in a third table

i have three tables employees, customers and account. i want to select all the rows that are found in employees and customers but not in account. i tried SELECT *FROM employees, customers where id not in (select id from account) and it did not work, but if i remove either employees or customers it works
If neither employee or customer has an account you could union the 2 tables then test
with cte as
(
Select 'e' src,id, name from employees
union all
Select 'c' src,id, name from customers
)
select *
from cte
where not exists (select 1 from accounts where accounts_id = cte.id);
Or you could use a cross join (or a comma join without a where clause)
SELECT
FROM employees e, customers c
where not exists (select 1 from accounts where accounts_id = e.id) and
not exists (select 1 from accounts where accounts_id = c.id);
The point being that you have to test the id from both tables. With a union there is one id to test with a cross join there are two. Using a not exists means the query is more efficient than an in(s).

Database SQL query to delete the duplicate emails

I have to write a SQL query to delete all duplicate email entries in a table named Person, keeping only unique emails based on its smallest Id. Id is the primary key column for this table.
This is the query I wrote:
delete from Person
where Email not in (select distinct Email from Person)
But I get this error:
You can't specify target table 'Person' for update in FROM clause
You Can Try This .
DELETE p1 FROM Person p1
INNER JOIN Person p2
WHERE
t1.id < t2.id AND
t1.Email = t2.Email ;
You are using MySQL, which does not allow that syntax. Use join instead:
delete p
from Person p left join
(select email, min(id) as min_id
from person p
group by email
) pp
on pp.id = min_id
where pp.min_id is null; --no match
Of course, your logic is wrong anyway. But even with the correct logic, not in/not exists is not going to work in MySQL. You need to use a join of some sort.
you can use IN keyword after grouping the Person table to Email
delete from Person where id not in (
select min(id) as ID from Person group by Email)

MySQL - Return the last record on the second table then return all in the first table

I have two tables customers and orders, below is the structure.
Table - contacts
id
Table - orders
id
contact_id
How can I select all from contacts table but only select the latest record from the orders table?
SELECT contacts.*,
Max(orders.id)
FROM contacts
LEFT JOIN orders
ON contacts.id = orders.contact_id
GROUP BY contacts.id;
But I always gets NULL if I use LEFT JOIN, it only have value if I use INNER JOIN.
select the latest record in orders and group it first
select contacts.*, orders.id
from contacts
left join (select max(id) as id, contact_id
from orders
group by contact_id) orders
on contacts.id = orders.contact_id
You can try to use UNION like
select * from orders order by id desc limit 1
UNION
select * from contacts
In order to aggregate max value with all columns from contacts table, add all columns from contacts table after group by function
I trust the answer provided by Alex should work well. The following query shall list all records from contacts and the last id from orders table.
SELECT
c.*,
(SELECT Max(o.id) FROM orders o
INNER JOIN contacts c1 ON o.id=c1.id
)as last_order_id
FROM contacts c

mysql query result issue with join

I have multiple tables as table_1 has id , p_code, profile_status, name and table_2 has id, p_code, availablity and table_3 has id, p_code, status...
How to get all records form all tables depend on p_code.
table_2 and table_3 has few records. if p_code not in table_2 and table_3 then echo 'no' in results.
currently i am using my query as below
select t.id, t.p_code,t.name,t.num_rooms, t.profile_status, t.distance FROM (
( SELECT id , p_code, profile_status, name,num_rooms, 3956 * 2 * ASIN(SQRT( POWER(SIN(($origLatAirport - latitude)*pi()/180/2),2)
+COS($origLatAirport*pi()/180 )*COS(latitude*pi()/180)
*POWER(SIN(($origLonAirport-longitude)*pi()/180/2),2)))
as distance FROM property WHERE profile_status=1 having distance < ".$dist." ) ) as t
How to add table_2 and table_3 and fetch results.
Pleasr reply soon. I am stuck here.
In your query you are doing CROSS JOIN and what you desire, is probably INNER JOIN.
In MySQL the CROSS JOIN behaves like JOIN and INNER JOIN of without using any condition.
The CROSS JOIN returns all rows form user multiplied by all rows from user_inbox - for every user you get inboxes of all users.
You should specify condition for your JOIN statement.
$sql_alt = mysql_query(
"select i.*,u.images, u.firstname, u.lastname
from user_inbox i INNER JOIN user u ON i.to_id = u.user_id
where i.to_id = '$user_id'");
Also it is good habit have the same names for primary and foreign keys, so I think you should have user_id or user_id_to instead of to_id in your user_inbox table. This is of course not absolutely necessary.