How to select only one data from duplicate data in mysql? - mysql

I have table bio
ID Name Country Address
1 Dan America A
2 Dan Japan B
3 Dan Canada C
4 Marcus China D
5 Kurtis Nepal E
6 Kurtis Mexico F
7 Jack Indonesia G
I need to select only one from the duplicate value of column "Name". I expect the result like this.
ID Name Country Address
1 Dan America A
4 Marcus China D
5 Kurtis Nepal E
7 Jack Indonesia G
I used this query
SET SESSION sql_mode = ( SELECT REPLACE ( ##sql_mode, 'ONLY_FULL_GROUP_BY', '' ) );
Select * from bio group by name;
Is there any other way without using SET SESSION sql_mode = ( SELECT REPLACE ( ##sql_mode, 'ONLY_FULL_GROUP_BY', '' ) ); since if i didn't use that, it return error.
I have tried answer with forpass answer but it run very slow. Here is the Explain query.
id select_type table type possible_keys rows filtered Extra
1 PRIMARY b ALL 1095012 100.00 Using where
2 DEPENDENT SUBQUERY t ALL PRIMARY,semua 1095012 3.33 Range checked for each record (index map: 0x3)

You can do it with NOT EXISTS:
SELECT b.*
FROM bio b
WHERE NOT EXISTS (
SELECT 1
FROM bio t
WHERE t.Name = b.Name AND t.ID < b.ID
)

It can be easily achieved in MySQL 8.0 using the ROW_NUMBER() OVER (PARTITION BY ) window function. But in 5.7 you have to emulate the same function with variables. Something like below.
SELECT ID, Name, Country, Address
FROM (
SELECT *, IF (#prev <> name, #rn: = 0, #rn),
#prev: = Name,
#rn: = #rn + 1 AS rn
FROM bio,
(SELECT #rn: = 0 ) rn,
(SELECT #prev: = '') prev
ORDER BY Address ASC
) t
WHERE rn = 1;
Alternatively you can use simple join to avoid mentioning the column names
SELECT b1.*
FROM bio b1
JOIN
(
SELECT Name, Min(ID) AS ID FROM bio
GROUP BY Name
) b2
ON b1.Name = b2.Name AND b1.ID = b2.ID;

Related

how to get duplicate data from table with sql

I have a table directors and need to get all duplicated rows
Checking columns is name and phone_number
Table directors
uuid
name
phone_number
5esd
ari
111-222-333
6dcv
lee
111-222-333
56js
poo
667-784-343
tug8
ari
866-653-343
I need these rows:
uuid
name
phone_number
5esd
ari
111-222-333
6dcv
lee
111-222-333
tug8
ari
866-653-343
ecause two upper rows has same phone number and last record has same name as first row
What I tried is
select d1.* from directors as d1
join (
select d2.* from directors d2
group by `d2`.`uuid`
having count(d2.phone_number) > 1
or count(d2.name) > 1
) d2 on d1.uuid = d2.uuid;
Just one of possible options:
select t.* from t
join (
select phone_number from t group by phone_number having count(phone_number) > 1
) d on t.phone_number = d.phone_number;
https://sqlize.online/sql/psql14/f7d63b0d5d06a4d6d428798da644dcbb/
One more example:
select t.* from t
join t t_copy using(phone_number)
where t.uuid != t_copy.uuid;
https://sqlize.online/s/MW
A couple of options you can use:
select *
from t
where exists (
select * from t t2
where t2.phone_number = t.phone_number and t2.uuid != t.uuid
);
select * from (
select *, Count(*) over(partition by phone_number) cnt
from t
)t
where cnt > 1

change the mysql order of the result with union

So i get 10 results from my first select and 1 from the other one after union like this:
(SELECT a.*,
b.*
FROM all a,
names b
WHERE b.name_id = a.name_id
ORDER BY name_id DESC
LIMIT 10)
UNION
(SELECT a.*,
b.*
FROM all a,
names b
WHERE b.name_id = a.name_id
ORDER BY request_id ASC
LIMIT 1)
i would like to get the result of the second select as the second last result like this
********
name_id 100
name_id 99
name_id 98
name_id 97
name_id 96
name_id 95
name_id 94
name_id 93
name_id 92
name_id 1 <- second select result as second last result
name_id 91
********
Can someone help pls?
Synthesize a row number column for the query as it stands and shuffle positions as needed.
SELECT x.name
, x.name_id
FROM (
SELECT #rownum:=#rownum + 1 as row_number,
t.name,
t.name_id
FROM (
-- original query from the question starts here
(SELECT b.name,
a.name_id
FROM allx a,
names b
WHERE b.name_id = a.name_id
ORDER BY name_id DESC
LIMIT 10)
UNION
(SELECT b.name,
a.name_id
FROM allx a,
names b
WHERE b.name_id = a.name_id
ORDER BY request_id ASC
LIMIT 1)
) t,
(SELECT #rownum := 0) r
) x
ORDER BY CASE row_number
WHEN 10 THEN 11
WHEN 11 THEN 10
ELSE row_number
END
;
(Note that the query has been sightly modified to avoid syntax errors / support the demo: table all has been named allx, explicit projections of the union's subqueries).
That gets complicated quickly thus next to ad hoc reporting it is preferable to synthesize an attribute in the subqueries of the union that reflects a global order.
Demo here (SQL fiddle)
Credits
Row number synthesizing taken from this SO answer
Interesting question given
+----+--------+
| id | sname |
+----+--------+
| 1 | sname1 |
| 2 | sname2 |
| 3 | sname3 |
| 4 | sname4 |
| 5 | sname5 |
| 6 | sname6 |
+----+--------+
6 rows in set (0.001 sec)
(select id,sname,#r:=#r+1 rn
from users
cross join(select #r:=0) r
order by sname desc limit 3
)
union
(
select u.id,u.sname,
#r:=#r - .9
from users u
left join (select id from users order by sname desc limit 3) u1 on u1.id = u.id
where u1.id is null
order by u.id asc limit 0,1
)
order by rn;
Where a variable is used to calculate a row number in the first sub query, since this variable is not reset in the second query a simple piece of arithmetic works out where to position the second sub query result. Note the second sub query uses a left join to check that the result has not already appeared in the first sub query,
I would suggest union all and three selects:
SELECT an.*
FROM ((SELECT a.*, n.*, 1 as ord
FROM all a JOIN
names n
ON n.name_id = a.name_id
ORDER BY n.name_id DESC
LIMIT 9
) UNION ALL
(SELECT a.*, n.*, 3 as ord
FROM all a JOIN
names n
ON n.name_id = a.name_id
ORDER BY n.name_id DESC
LIMIT 9 OFFSET 9
) UNION ALL
(SELECT a.*, b.*
FROM all a JOIN
names n
WHERE n.name_id = a.name_id
ORDER BY request_id ASC
LIMIT 1
)
) an
ORDER BY ord, name_id;

Mysql - Calculate rank grouped by two columns

I have a mysql table like so:
Tbl
[Username Number Type]
manos 5 A
manos 6 B
maria 2 A
maria 3 B
maria 1 C
nick 7 A
nick 4 C
aaron 8 A
I want to create a view where I will have the ranks of each user (by larger Number), grouped by Type, in comparison to the other users in each Type. More specifically, I would like the output to be:
[Username Rank Type]
manos 3 A
manos 1 B
maria 4 A
maria 2 B
maria 2 C
nick 2 A
nick 1 C
aaron 1 A
I have tried the following:
select Username, count(*) as Rank, Type
from Tbl as aw
where
Number <= (
select Number
from Tbl
where Type = aw.Type
)
group by Username, Type
The result is that I get the Subquery returns more than 1 row error.
Any help would be appreciated!
Edit: changed the names of columns as suggested. Also a fiddle to help: http://sqlfiddle.com/#!9/55ec3f
Edit 2: Some clarification and corrections to the "all combinations rank".
Lets say we have another table called Teams:
Teams
[Username Team]
manos T1
manos T2
maria T1
maria T2
nick T1
nick T2
aaron T3
In this case I want to extract ranks on each combination of group(s) and Types, i.e. for Manos I would like:
Rank against people in T1 with type A
Rank against people in T2 with type A
Rank against people in T1 with type B
Rank against people in T2 with type B
Rank against people in T1 AND T2 with type A
Rank against people in T1 AND T2 with type B
For the sake of sanity, I renamed your group column...
SELECT username
, user_group
, number
, CASE WHEN #prev=user_group THEN #i:=#i+1 ELSE #i:=1 END rank
, #prev:=user_group
FROM my_table x
, (SELECT #prev:=null, #i:=0) vars
ORDER
BY user_group
, number DESC;
Correct code:
select Username, (select Number
from Tbl as t
where t.Type = aw.Type and t.Username = aw.Username) as Rank, Type from Tbl as aw where
Number <= (
select Number
from Tbl as t
where t.Type = aw.Type and t.Username = aw.Username
) group by Username, Type

How do you add two values from the same table and store in calculated record in the same table?

Hi I have one tables here:
equipment
ID, Owner, Type, Count
1 Bob phone 10
2 Larry computer 11
1 Bob computer 11
What I am trying to do is add the computers that Bob, with id 1, to the computers of Larry's, with id 2. I'm trying to increase the count. The count should be 11+11=22. The new computer count for id 2 should be 22 and should update like this in the database:
equipment
ID, Owner, Type, Count
1 Bob phone 10
2 Larry computer 22
1 Bob computer 11
If Bob did not have any computer, meaning there was no record with ID = 2, then the record should be created.
Here is my SQL:
INSERT INTO EQUIPMENT(`ID`, `OWNER`, `TYPE`, `COUNT`)
SELECT 1 as t.ID, t.OWNER, t.TYPE, t.COUNT
FROM EQUIPMENT t
WHERE t.ID = 2
on duplicate key
update
COUNT = COUNT + t.COUNT;
Why are you using insert to update a row?
update equipment e cross join
(select e1.* from equipment e1 where e1.id = 1) as e1
select e.count = e.count + e1.count
where e.id = 2;
Some thing like this
UPDATE Yourtable a
JOIN Yourtable b
ON a.NAME = b.NAME
AND a.ID = b.ID + 1
SET a.Count = a.Count + b.Count

SQL: finding differences between rows

I want to count how many times each user has rows within '5' of eachother.
For example, Don - 501 and Don - 504 should be counted, while Don - 501 and Don - 1600 should not be counted.
Start:
Name value
_________ ______________
Don 1235
Don 6012
Don 6014
Don 6300
James 9000
James 9502
James 9600
Sarah 1110
Sarah 1111
Sarah 1112
Sarah 1500
Becca 0500
Becca 0508
Becca 0709
Finish:
Name difference_5
__________ _____________
Don 1
James 0
Sarah 2
Becca 0
Use the ABS() function, in conjunction with a self-join in a subquery:
So, something like:
SELECT name, COUNT(*) / 2 AS difference_5
FROM (
SELECT a.name name, ABS(a.value - b.value)
FROM tbl a JOIN tbl b USING(name)
WHERE ABS(a.value - b.value) BETWEEN 1 AND 5
) AS t GROUP BY name
edited as per Andreas' comment.
Assuming that each name -> value pair is unique, this will get you the count of times the value is within 5 per name:
SELECT a.name,
COUNT(b.name) / 2 AS difference_5
FROM tbl a
LEFT JOIN tbl b ON a.name = b.name AND
a.value <> b.value AND
ABS(a.value - b.value) <= 5
GROUP BY a.name
As you'll notice, we also have to exclude the pairs that are equal to themselves.
But if you wanted to count the number of times each name's values came within 5 of any value in the table, you can use:
SELECT a.name,
COUNT(b.name) / 2 AS difference_5
FROM tbl a
LEFT JOIN tbl b ON NOT (a.name = b.name AND a.value = b.value) AND
ABS(a.value - b.value) <= 5
GROUP BY a.name
See the SQLFiddle Demo for both solutions.
Because the OP also wants de zero counts, we'll need a self- left join. Extra logic is needed if one person has two exactly the same values, these should also be counted only once.
WITH cnts AS (
WITH pair AS (
SELECT t1.zname,t1.zvalue
FROM ztable t1
JOIN ztable t2
ON t1.zname = t2.zname
WHERE ( t1.zvalue < t2.zvalue
AND t1.zvalue >= t2.zvalue - 5 )
OR (t1.zvalue = t2.zvalue AND t1.ctid < t2.ctid)
)
SELECT DISTINCT zname
, COUNT(*) AS znumber
FROM pair
GROUP BY zname
)
, names AS (
SELECT distinct zname AS zname
FROM ztable
GROUP BY zname
)
SELECT n.zname
, COALESCE(c.znumber,0) AS znumber
FROM names n
LEFT JOIN cnts c ON n.zname = c.zname
;
RESULT:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 14
zname | znumber
-------+---------
Sarah | 3
Don | 1
Becca | 0
James | 0
(4 rows)
NOTE: sorry for the CTE, I had not seen th mysql tag,I just liked the problem ;-)
SELECT
A.Name,
SUM(CASE WHEN (A.Value < B.Value) AND (A.Value >= B.Value - 5) THEN 1 ELSE 0 END) Difference_5
FROM
tbl A INNER JOIN
tbl B USING(Name)
GROUP BY
A.Name