MySQL - selective removing duplicated records - php

I have db with few 1000 of contacts and would like to delete all duplicated records. Sql query I have at the moment works well ( when in records - tel, email, name1 are duplicated). Query deletes duplicates with lower id then last occurring record. But in some cases another fields of the record a filled in already (important ones will by title and name2). What i would like to achieve is for mysql to check if these fields are filled in and keep only the record with most information filed in.
My Query
<?php
$del_duplicate_contacts = $mysqli->query("
DELETE ca
FROM contacts ca
LEFT JOIN
(
SELECT MAX(id) id, name1, tel, email
FROM contacts
GROUP BY name1, tel, email
) cb ON ca.id = cb.id AND
ca.name1 = cb.name1 AND
ca.tel = cb.tel AND
ca.email = cb.email
WHERE cb.id IS NULL
");
?>
Example of table:
ID title name1 name2 tel email
1 John 01234 1#1.com
2 Mr John Smith 01234 1#1.com
3 John 01234 1#1.com
My query will delete record 1 and 2. I would like to keep only nr 2 and delete 1 and 3.
How I can achieve that? Is is possible? Or maybe i should involve PHP, if so How?

Use order by in group_concat, you can try this:
DELETE c1 FROM contacts c1
JOIN (
SELECT
substring_index(group_concat(id ORDER BY ((title IS NULL OR title ='') AND (name2 IS NULL OR name2 = '')), id DESC), ',', 1) AS id,
name1, tel, email
FROM contacts
GROUP BY name1, tel, email
) c2
ON c1.name1 = c2.name1 AND c1.tel = c2.tel AND c1.email = c2.email AND c1.id <> c2.id;
Demo Here

I have got solution using NOT EXIST clause instead of NOT IN
DELETE FROM contacts
WHERE NOT EXISTS (
SELECT 1 FROM (
SELECT * FROM (
SELECT * FROM contact AS tmp ORDER BY title DESC, name1 DESC, name2 DESC, email DESC, tel DESC )
as tbl group by name1)
as test WHERE contact.id= test.id
)

OOPS - This is the worst answer I have produced so far -- warning the top bit is dangerous, not sure why I did not include any group -- please continue to the bottom part, which is working now:
DELETE FROM contacts WHERE ID IN (
SELECT ID FROM (
SELECT DISTINCT a.ID
FROM contacts AS a
JOIN contacts AS b
ON a.name1 = b.name1
AND a.tel = b.tel
AND a.email = b.email
ORDER BY a.name1 DESC, a.name2 DESC, a.title DESC
LIMIT 1,100000
) AS tmp
)
LIMIT Must be 1, xxxx -- not 0, xxxx to keep the first one undeleted
As you cannot delete directly from the same table found in sub-query, just add a mask layer, so it is now tested working
Before Deleting, always double check what is going to be deleted:
SELECT * FROM contacts WHERE ID IN (
SELECT ID FROM (
SELECT DISTINCT a.ID
...
LIMIT 1,100000
) AS tmp
)
Apologies for the damage, luckily you did on a testing db
=====================================
Now here is the correct solution:
Let's check what is in the testing table:
According to question, we noticed only #2 #4 #5 is good to keep. And here is the result:
And we want to delete any records not in above list, before delete, we double check what is going to be deleted:
And we are ready to delete:
And here is the SQL, make sure you do on testing db first:
DELETE FROM contacts WHERE ID NOT IN (
SELECT * FROM (
SELECT ID FROM (
SELECT * FROM contacts ORDER BY title DESC, name1 DESC, name2 DESC, tel DESC, email DESC
) AS tmp
GROUP BY name1, tel, email
) AS del
)

This query will work without any ordering options !
DELETE FROM contacts where ID NOT IN (
SELECT ID FROM ( Select A.ID from contacts as A
join contacts AS B
ON A.name1 = B.name1
AND A.name2 = B.name2
AND A.tel = B.tel
AND A.email = B.email) As mytry);

Related

How to retrieve data with 3 tables in mysql?

I am having 2 tables :
1.internal_employee_master
id employee_name unique_id
1 Noah ABCD
2 Liam ABCD
3 William ABCD
4 Benjamin ABCD
5 Jacob EFGH
2.external_employee_master
id name unique_id
1 Elijah ABCD
2 Ethan ABCD
3 Alexander EFGH
I am using UNION query to get both tables data into single table and display this data into html table.
select id
, employee_name
, unique_id
from internal_employee_master
where unique_id = 'ABCD'
union
select id
, employee_name
, unique_id
from external_employee_master
where unique_id = 'ABCD'
I want to store payslips of both employees into single table.
I have one table payslips with emp_id and emp_type columns.
I am storing data into payslips data like:
id pay_slip emp_id emp_type
1 Noah_payslip.pdf 1 internal
2 Liam_payslip.pdf 2 internal
3 Lia_payslip.pdf 1 External
as you can see in above table i am storing emp_id and emp_type of
both the tables in single columns each.
Now, i dont undestand how to split data of internal employee and
external employee from pay_slip table and show data in html table.
Currently, i am writing below sql joins to get employee_names of
internal and external employee tables but it doesnt work for me.
$id = $_GET['id];
SELECT ps.id,ps.pdf,ps.emp_id,ps.emp_type,external_employee.name as comemp,
internal_employee.comp_empl_name as comemp
FROM pay_slip as ps
INNER JOIN internal_employee_master as internal_employee ON internal_employee.comp_trad_id = ps.trade_id
INNER JOIN external_employee_master as external_employee ON external_employee.trad_id = ps.trade_id
where ps.is_deleted = 1 AND ps.id = '".$id."'"
Please help me to join query to get name and employee_name with respect to emp_type form pay_slip table.
How about using UNION again?
SELECT
ps.id,
ps.pdf,
ps.emp_id,
ps.emp_type,
external_employee.name AS comemp,
internal_employee.comp_empl_name AS comemp
FROM
pay_slip AS ps
INNER JOIN
internal_employee_master AS internal_employee ON internal_employee.comp_trad_id = ps.trade_id
WHERE
ps.is_deleted = 1 AND ps.id = '".$id."'
AND ps.type = 'internal'
UNION ALL
SELECT
ps.id,
ps.pdf,
ps.emp_id,
ps.emp_type,
external_employee.name AS comemp,
internal_employee.comp_empl_name AS comemp
FROM
pay_slip AS ps
INNER JOIN
external_employee_master AS external_employee ON external_employee.trad_id = ps.trade_id
WHERE
ps.is_deleted = 1 AND ps.id = '".$id."'
AND ps.type = 'external'
You could try this
SELECT ps.id, ps.pay_slip, ps.emp_type, COALESCE(i.employee_name, e.name) AS name
FROM payslips ps
LEFT JOIN internal_employee_master i ON i.id = ps.emp_id AND ps.emp_type = 'internal'
LEFT JOIN external_employee_master e ON e.id = ps.emp_id AND ps.emp_type = 'External'
AND ps.id = :ID
You can see this in action here http://sqlfiddle.com/#!9/53a195/7/0
I would mention that there are a number of issues in your included tables and queries. For example, irregular column names between tables (name vs. employee_name), you've missed the is_deleted column from your example schema, and you have capitalised and non-capitalised values in the emp_type column which is confusing.

get all rows having common parents

id parent_id child_id
1 1 1
2 2 2
3 2 2
4 1 1
I have a table from which i need to get the common values from data when i query it with id... for eg if id=2 and id=3 then return
id parent_id
2 2
3 2
i have tried this after hunting a lot through various examples :
SELECT ta.user_id,ta.interest_parent_id,ta.interest_child_id
FROM user_interest ta
WHERE ta.user_id=2 AND
(SELECT COUNT(*) FROM user_interest tb
WHERE ta.interest_parent_id=tb.interest_parent_id
AND tb.user_id=3 )>1
but it responds with only:
id parent_id
2 2
any help :( im using a mysql database with php/codeigniter to do the scripting
You can give it a try:
SELECT
tOne.id,
tOne.parent_id
FROM
(
SELECT
*
FROM user_interest A
WHERE A.id IN (2,3)
) tOne
INNER JOIN
(
SELECT
*
FROM user_interest A
WHERE A.id IN (2,3)
) tTwo
ON tOne.parent_id = tTwo.parent_id
AND tOne.id <> tTwo.id
ORDER BY tOne.parent_id;
SQL FIDDLE DEMO
Any suggestion towards optimization of the query is welcome.
EDIT: SQL FIDDLE
You can make a sub SELECT:
SELECT * FROM table WHERE Name IN (SELECT Name FROM table GROUP BY Name HAVING count(*) > 1)

Select max value for a VARCHAR column - MySQL

My table for message inbox concept email is a primary in the table. I want to get the last message which is sent by particular email.
tbl_msg:
id
email
msg
Example Data:
Id email msg
1 xyz#gmail.com This is test
2 abc#gmail.com All is well
3 xyz#gmail.com This is test2
I want to get the last appearance of each email and msg
Id email msg
2 abc#gmail.com All is well
3 xyz#gmail.com This is test2
What I tried:
SELECT cnote.`id`,cnote.`email`,cnote.`msg` FROM `tbl_msg` cnote inner join (select distinct email,id from client_com group by email) as note
on cnote.id=note.id
Guide me if I wrong
Use This query:
SELECT m1.* FROM tbl_msgs m1 LEFT JOIN tbl_msgs m2 ON (m1.email = m2.email AND m1.id < m2.id) WHERE m2.id IS NULL;
This will join with the same table and with condition m1.id < m2.id , will get the latest one.
Another option is using subquery:
SELECT * FROM tbl_msgs WHERE id IN (SELECT MAX(id) FROM tbl_msgs GROUP BY email);
To get the latest messsage for each email address in the table you do not need a JOIN, you just need to ORDER and GROUP BY:
SELECT `id`, `email`, `msg`
FROM `tbl_msg`
GROUP BY `email`
ORDER BY `id` DESC
You can use this query:
SELECT id, email, msg FROM you_table
GROUP BY email
ORDER BY id DESC
With this one you will have only 1 row per email, and you'll have the last because is ordered by id DESC.
If you are running a PDO connection (The way I know to do it, not sure if there is a MySQLi way)
$theLastIdGiven = $handlerDbConnection->lastInsertId();
Or if you want to run it through a query, and get the last result, just add ORDER BY id DESC LIMIT 0, 1 and that will order the data by id in a descending form then get the first bit of data :)
Hope this helped you!
SELECT ID, email, Msg FROM tbl_msg WHERE ID IN (
(SELECT MAX(ID) FROM tbl_msg GROUP BY email))

How to count records in MySQL and merge results in PHP

I have a table which stores clients like this:
id name
-- ----
1 John
2 Jane
...
I also have another table which stores links created by clients:
id client_id link created
-- --------- ---- -----------
1 1 ... 2015-02-01
2 1 ... 2015-02-26
3 1 ... 2015-03-01
4 2 ... 2015-03-01
5 2 ... 2015-03-02
6 2 ... 2015-03-02
I need to find how many links a client has created today, this month and during all the time. I also need their name in the result, so I'll be able to craete a HTML table to display the statistics. I thought I can code as less as possible like this:
$today = $this->db->query("SELECT COUNT(*) as today, c.id as client_id, c.name FROM `links` l JOIN `clients` c ON l.client_id = c.id WHERE DATE(l.created) = CURDATE() GROUP BY c.id");
$this_month = $this->db->query("SELECT COUNT(*) as this_month, c.id as client_id, c.name FROM `links` l JOIN `clients` c ON l.client_id = c.id WHERE YEAR(l.created) = YEAR(NOW()) AND MONTH(l.created) = MONTH(NOW()) GROUP BY c.id");
$yet = $this->db->query("SELECT COUNT(*) as yet, c.id as client_id, c.name FROM `links` l JOIN `clients` c ON l.client_id = c.id WHERE GROUP BY c.id");
And then merge them in PHP as I asked HERE before, like this:
$result = array_replace_recursive($today, $this_month, $yet);
So I'll be able to loop into the result and print my HTML table.
But there are logical problems here. Everything works fine, but the result in a month is a wrong number, forexample the whole created links of one person is 1 but it shows 4 in the monthly counter! I also tried to use RIGHT JOIN in SQL query to get all clients, so array_replace_recursive in PHP could work fine as I think it doesn't work properly at the moment, but no success and got wrong results again.
Can anyone show me a way to make the job done?
This query should do it for today
$query_today="
SELECT name, id AS user_id, (
SELECT COUNT( * )
FROM links
WHERE client_id = user_id AND created = '2015-03-02'
) AS alllinks
FROM clients"
adjust the WHERE clause in the subquery for months and all
$query_month="
SELECT name, id AS user_id, (
SELECT COUNT( * )
FROM links
WHERE client_id = user_id AND created like '2015-03%'
) AS alllinks
FROM clients"
$query_all="
SELECT name, id AS user_id, (
SELECT COUNT( * )
FROM links
WHERE client_id = user_id
) AS alllinks
FROM clients"

Get a rank, based on score, from an unordered MySql Database when given a Username

Okay so I have a table that has the following
KEY username password score
The above columns are not in any specific order.
I want to send my Database a username and have it send me back what rank that user name is based on its score. So for example if I had 10 people in there and the 3rd person in has the highest score. When I pass the 3rd persons username in I want it to send back 1.
Is this possible?
I have been trying things like this
$result = mysql_query("SELECT * FROM tablename where username='$username' ORDER BY score DESC");
but it doesnt seem to give me the row number
This will handle ranks that have the same score.
SELECT d.*, c.ranks
FROM
(
SELECT Score, #rank:=#rank+1 Ranks
FROM
(
SELECT DISTINCT Score
FROM tableName a
ORDER BY score DESC
) t, (SELECT #rank:= 0) r
) c
INNER JOIN tableName d
ON c.score = d.score
// WHERE d.username = 'Helen'
SQLFiddle Demo (Ranking with duplicates)
SQLFiddle Demo (with filtering)
for example
KEY username password score Ranks
1 Anna 123 5 3
2 Bobby 345 6 2
3 Helen 678 6 2
4 Jon 567 2 4
5 Arthur ddd 8 1
for better performance, add an INDEX on column Score,
ALTER TABLE tableName ADD INDEX (Score)
SELECT
(SELECT COUNT(*)+1 FROM tablename WHERE score > t.score) as rank,
*
FROM
tablename t
where
username='$username'
The ORDER BY in your query is useless since you're only returning one row.

Categories