MySQL: Select unique value in column based on another columns value - mysql

I have a table set up like this:
id | ip | name
---------------------------------
1 | 54.34.32.222 | John
2 | 23.44.64.843 | Rick
3 | 54.34.32.222 | John
4 | 23.44.64.843 | John
5 | 14.432.45.45 | Lisa
6 | 54.34.32.222 | Lisa
7 | 14.432.45.45 | Lisa
I only want to grab a unique IP per name. For example, "54.34.32.222" appears for John twice, so I only want to grab the first row. But "54.34.32.222" also appears for Lisa, so I would like to grab that IP as well.
The result should look something like this:
id | ip | name
---------------------------------
1 | 54.34.32.222 | John
2 | 23.44.64.843 | Rick
4 | 23.44.64.843 | John
5 | 14.432.45.45 | Lisa
6 | 54.34.32.222 | Lisa
How would you count the amount of times names appear? When doing so, it counts how many times the ip appears within the name, but I want the opposite.
SELECT MIN(id), COUNT(name), ip, name FROM yourTable GROUP BY ip, name

You never mentioned how you want to determine which record to retain in the case of duplicate ip-name pairs. However, based on your sample output it appears you are retaining the record with the smallest id value. In this case, we can just take the MIN(id) while grouping to get the desired result:
SELECT MIN(id), ip, name
FROM yourTable
GROUP BY ip, name
Follow the link below for a running demo:
SQLFiddle

This should work for you
SELECT min(id),ip,name FROM youTable group by ip,name

You would likely need to do a join against a derived table here to get what you want. You could also do as subselect, but I will show join solution, as for most use case it would be likely to perform better.
SELECT
yourtable.id AS id,
ip,
name
FROM yourtable
/* join regular table to a derived table
* where you have selected the first row id for each user
*/
INNER JOIN (
SELECT MIN(id)
FROM yourtable
GROUP BY name
) AS min_id_per_name
ON yourtable.id = min_id_per_name.id
ORDER BY yourtable.id

You could use the following query, which selects the lexical minimum of the IP address for any given name:
SELECT NAME, MIN(IP) AS IP
FROM TABLENAME
GROUP BY NAME
If you need the IP address corresponding to the first record found for that name (ie, the one on the record with the lowest ID):
SELECT NAME, IP
FROM TABLENAME TN
WHERE ID = (
SELECT MIN(ID)
FROM TABLENAME TN1
WHERE TN1.IP = TN.IP
AND TN1.NAME = TN.NAME
)

Related

Select 2 Column 1 record as 1 column 2 records

I have a table like this
name | place_1 | place_2
jhon | Tokyo | Nagasaki
and I want it to be like this
name | place
jhon | Tokyo
Jhon | Nagasaki
How to Select it in SQL ?
Ideally, your expected output should have been your original table structure (and data). You should consider normalizing your data in first place.
Still, if you have no control over the table structure; you can use UNION ALL:
(SELECT name, place_1 AS place
FROM your_table)
UNION ALL
(SELECT name, place_2 AS place
FROM your_table)
ORDER BY name, place DESC

Limit MySQL Results to One From Each "Group"

Suppose we have a table like the one below.
Id | Name | Group
-----------------
1 | John | 1
2 | Zayn | 2
3 | Four | 2
4 | Ben_ | 3
5 | Joe_ | 2
6 | Anna | 1
The query below will select all of them.
SELECT `Name` FROM `Table` WHERE 1;
How would I select only one person from each group? Who it is doesn't really matter, as long as there's only one name from group 1 and one name from group 2 etc.
The GROUP BY clause isn't fit for this (according to my error console) because I am selecting non aggregated values, which makes sense.
The DISTINCT clause isn't great here either, since I don't want to select the "Group" and definitely not group by their names.
If is not important the resulting name You can anawy leverage some group functions eg with max or min..
leverage the group functions
select max(name) from your_table
group by Group;
otherwise you can use subquery
select name from your_table
where Id in (select min(Id) from your_table group by Group);

Count the amount of occurrences of a another count result

We have a table called entries which stores user information against a date. Users are allowed to enter the database once per day. Some example data:
+----+------------------+----------+
| id | email | date |
+----+------------------+----------+
| 1 | marty#domain.com | 04.09.13 |
| 2 | john#domain.com | 04.09.13 |
| 3 | marty#domain.com | 05.09.13 |
+----+------------------+----------+
I need to work out how many times there are X entries with the same email in the database. For example, the result should look like this for the above data, where we have 1 instance of one entry and 1 instance of 2 entries:
+---------------+-------+
| times_entered | total |
+---------------+-------+
| 1 | 1 |
| 2 | 1 |
+---------------+-------+
I've tried a few things but the furthest I have been able to get is getting a count of the amount of times each email address was found. It seems like all I need to do from here is collate those results and perform another COUNT on those groups to get the totals, but I'm unsure of how I can do that.
Usually it can be something like
select times_entered, count(*) from
( select count(*) times_entered from entries group by email )
group by times_entered
Not sure if it works for MySQL though...
The following will get the number of records per email:
SELECT COUNT(1) AS times_entered, email
FROM entries
GROUP BY email
Therefore, using this query as a derived table, we can group by the number of records to get the count (we do not need to select the email column in the subquery because we don't care about it):
SELECT times_entered, COUNT(1) AS total
FROM
(
SELECT COUNT(1) AS times_entered
FROM entries
GROUP BY email
) x
GROUP BY times_entered
SQL Fiddle demo
SQL Fiddle demo with a slightly larger data set
It could be something like this:
SELECT times_entered, COUNT( times_entered ) AS total
FROM (
SELECT COUNT( email ) AS times_entered
FROM `entries`
WHERE 1
GROUP BY email
) AS tmp
GROUP BY times_entered;

MySQL conditionally populate column 3 based on DISTINCT involving 2 other columns in one table

Had a good read through similar topics but I can't quite a) find one to match my scenario, or b) understand others enough to fit / tailor / tweek to my situation.
I have a table, the important fields being;
+------+------+--------+--------+
| ID | Name | Price |Status |
+------+------+--------+--------+
| 1 | Fred | 4.50 | |
| 2 | Fred | 4.50 | |
| 3 | Fred | 5.00 | |
| 4 | John | 7.20 | |
| 5 | John | 7.20 | |
| 6 | John | 7.20 | |
| 7 | Max | 2.38 | |
| 8 | Max | 2.38 | |
| 9 | Sam | 21.00 | |
+------+------+--------+--------+
ID is an auto-incrementing value as records get added throughout the day.
NAME is a Primary Key field, which can repeat 1 to 3 times in the whole table.
Each NAME will have a PRICE value, which may or may not be the same per NAME.
There is also a STATUS field that need to be populated based on the following, which is actually the part I am stuck on.
Status = 'Y' if each DISTINCT name has only one price attached to it.
Status = 'N' if each DISTINCT name has multiple prices attached to it.
Using the table above, ID's 1, 2 and 3 should be 'N', whilst 4, 5, 6, 7, 8 and 9 should be 'Y'.
I think this may well involve some form of combination of JOINs, GROUPs, and DISTINCTs but I am at a loss on how to put that into the right order for SQL.
In order to get the count of distinct Price values per name, we must use a GROUP BY on the Name field, but since you also want to display all names ungrouped but with an additional Status field, we must first create a subselect in the FROM clause which groups by the name and determines whether the name has multiple price values or not.
When we GROUP BY Name in the subselect, COUNT(DISTINCT price) will count the number of distinct price values for each particular name. Without the DISTINCT keyword, it would simply count the number of rows where price is not null.
In conjunction with that, we use a CASE expression to insert N into the Status column if there is more than one distinct Price value for the particular name, otherwise, it will insert Y.
The subselect only returns one row per Name, so to get all names ungrouped, we join that subselect to the main table on the condition that the subselect's Name = the main table's Name:
SELECT
b.ID,
b.Name,
b.Price,
a.Status
FROM
(
SELECT Name, CASE WHEN COUNT(DISTINCT Price) > 1 THEN 'N' ELSE 'Y' END AS Status
FROM tbl
GROUP BY Name
) a
INNER JOIN
tbl b ON a.Name = b.Name
Edit: In order to facilitate an update, you can incorporate this query using JOINs in the UPDATE like so:
UPDATE
tbl a
INNER JOIN
(
SELECT Name, CASE WHEN COUNT(DISTINCT Price) > 1 THEN 'N' ELSE 'Y' END AS Status
FROM tbl
GROUP BY Name
) b ON a.Name = b.Name
SET
a.Status = b.Status
Assuming you have an unfilled Status column in your table.
If you want to update the status column, you could do:
UPDATE mytable s
SET status = (
SELECT IF(COUNT(DISTINCT price)=1, 'Y', 'N') c
FROM (
SELECT *
FROM mytable
) s1
WHERE s1.name = s.name
GROUP BY name
);
Technically, it should not be necessary to have this:
FROM (
SELECT *
FROM mytable
) s1
but there is a mysql limitation that prevents you to select from the table you're updating. By wrapping it in parenthesis, we force mysql to create a temporary table and then it suddenly is possible.

Specific duplicate selection in MySQL

I did take a look in and outside of SO and still don't know if this can be done. I have a table that looks like this:
User ID | Role | First Name | Last Name | Email |<br>
0001 | K | John | Smith | e#e.co|<br>
0002 | Q | Jane | Dickens | q#q.co|<br>
0003 | K | John | Smith | e#e.co|<br>
0004 | J | Jack | Paper | j#j.co|<br>
As you can see, the table contains a duplicate due to a user entering their information two separate times. I want to display the rows that
1. have the same first name
2. have the same last name
3. have the same email
4. do NOT have the same User ID
I can get the first three conditions to work with an inner join subquery, but I get 0 returned results when ever I try to to add in the fourth condition.
Thanks in advance for your help!
SELECT GROUP_CONCAT(`User ID`) as IDs, Role, `First Name`, `Last Name`, Email
FROM users_table
GROUP BY Role,`First Name`,`Last Name`,Email
Will give a table like
IDs | Role | First Name | Last Name | Email |
0001,0003 | K | John | Smith | e#e.co|
0002 | Q | Jane | Dickens | q#q.co|
0004 | J | Jack | Paper | j#j.co|
The trick is to GROUP BY everything except ID.
You could also do:
SELECT COUNT(`User ID`) as numIDs, GROUP_CONCAT(`User ID`) as IDs,
Role, `First Name`, `Last Name`, Email
FROM users_table
GROUP BY Role,`First Name`,`Last Name`,Email
HAVING numIDs > 1
to get
numIDs |IDs | Role | First Name | Last Name | Email |
2 |0001,0003 | K | John | Smith | e#e.co|
Anyhow, you get the point of how to vary it to your purposes.
Try something like:
select *
from tb_users
where (first_name, last_name, email) in
(select first_name, last_name, email
from tb_users
group by first_name, last_name, email
having count(*) > 1)
I am assuming that your table does not contain true duplicate rows (where the User ID also matches). I think it should be as simple as this to get the relevant rows back (with my adjusted column and table naming scheme):
SELECT ui.user_id, ui.role, ui.first_name, ui.last_name, ui.email
FROM user_info AS ui
INNER JOIN user_info AS udi
ON ui.first_name = udi.first_name
AND ui.last_name = udi.last_name
AND ui.email = udi.email
AND ui.user_id != udi.user_id;
I would use count(*) and group by clause.
Like this
SELECT count(*) as count FROM table group by concat(firstname,'\n',last_name) having count=1;
I think you want to delete duplicate rows. Only take count by group by and delete duplicate rows.
select * from tb_users
group by first_name, last_name, email
having count(*) > 1
You don't have to download extensions; go left bottom corner, click settings, click Keyboard Shortcuts, and search "duplication". You should see as in the below image and assign the key combination you want. I choose shift+D because I don't want to lose the current behavior of ctrl+D it is also very beneficial.