MySQL query multiple column group by issue - mysql

This is my data set
I want to get the most common used phone and car per city.
So the result expected is
For example: in NewYork there are 2 instances of iPhone out of 3 and 2 instances of Honda car out of 3, so they are the most common used phone and car for NY.
I'm not able to express in query how to execute group by clause on these 3 columns simultaneously.
Any pointers are appreciated, thanks in advance.

I'm sure there's a shorter way but, anyway, you can do:
select x.city, x.phone, y.car
from (
select city, phone, row_number() over(partition by city order by count(*) desc) as rn
from t group by city, phone
) x
join (
select city, car, row_number() over(partition by city order by count(*) desc) as rn
from t group by city, car
) y on x.city = y.city and x.rn = 1 and y.rn = 1
Result:
city phone car
--------- -------- -----
Houston Android Kia
New York iPhone Honda
See running example at db<>fiddle.

Related

MySQL GROUP BY - get SUM of few grouped values

I have a simple db where I have users and every user have 'country', for ex:
Dmitry - US
Ann - US
John - UK
Roman - Japan
Mila - China
Jane - Australia
I want to get count of very country users, BUT I need to get TOP 3 countries users counts (US, UK, Japan for example), and all other countries users count should be summarized together as "Rest". How to do this?
So in my example this should give me this result from SQL:
US = 2
UK = 1
Japan = 1
Rest = 2
If I will make regular SQL:
SELECT count(userid) FROM users GROUP BY country
I will get results for every country, but I need only TOP 3 and all others count as "Rest" in results. Thanks!
P.S.: I tried to create SQLFiddle for this, but their website is down right now and I can't use it.
You can group by country and use ROW_NUMBER() window function to rank the countries based on the number of times they appear.
Then add another level of aggregation based on the ranking position of each country:
SELECT CASE WHEN rn <= 3 THEN country ELSE 'Rest' END country,
SUM(counter) counter
FROM (
SELECT country, COUNT(*) counter,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) rn
FROM users
GROUP BY country
) t
GROUP BY 1;
Note that the countries returned as top 3 in case of ties may be arbitrary chosen, so you could add another condition in the ORDER BY clause of ROW_NUMBER(), like:
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC, country)
which would return different but consistent results.
See the demo.

Distinct based on one row with multiple non-aggregate columns selected

I'm trying to run a query that returns distinct AddressIDs.
The row to be retuned for each AddressID should be the one with the latest ReadDate.
I also want to return the value from (non-aggregate) columns PhoneNumber, SomeCode, and Country for the given records.
There are similar questions on here to mine, but nothing seems to suit my exact situation. I've tried different subqueries and making the other columns aggregates, but I can't seem to get the results I desire.
Say the base of the query like:
select cr.AddressID, cr.ReadDate, in.PhoneNumber, in.SomeCode, in.Country
from CustomerReadings cr, in.CustomerInfo
where cr.AddressID = in.AddressID
For example, if I have a table that looks like:
AddressID ReadDate PhoneNumber SomeCode Country
1005 01/01/1997 5556565 GHS Canada
1005 05/06/2006 5556753 ROT USA
1005 08/12/2018 5552345 JKR USA
2007 02/05/2012 5558746 MSC Canada
2007 12/07/2018 5552345 RRE France
4000 03/01/1999 5552345 RRE France
4000 09/05/2007 5551243 MSR USA
I want the query results to look like:
AddressID ReadDate PhoneNumber SomeCode Country
1005 08/12/2018 5552345 JKR USA
2007 12/07/2018 5552345 RRE France
4000 09/05/2007 5551243 MSR USA
If anything is unclear please let me know and I'll update my question accordingly.
In the case of 1 table as you used in your answer example, the code works.
But when I bring in another table, I no longer get just one distinct AddressID back, eg:
select (or select distinct)
cr.AddressID, cr.ReadDate, in.PhoneNumber, in.SomeCode, in.Country
from
CustomerReadings cr,
CustomerInfo in
where
cr.AddressID = in.AddressID
and cr.ReadDate =
(select max(cr2.ReadDate)
from CustomerReadings cr2
where cr2.AddressID = cr.AddressID)
order by
2 desc,
1;
There should be questions that are very similar. I use a correlated subquery:
select t.*
from t
where t.readdate = (select max(t2.readdate) from t t2 where t2.addressid = t.addressid);
You need correlated subquery :
select t.*
from table t
where readdate = (select max(t1.readdate) from table t1 where t1.addressid = t.addressid);
If you are working with latest version of MySQL, then row_number() would helpful :
select t.*
from (select t.*,
row_number() over (partition by addressid order by readdate desc) as seq
from table t
) t
where seq = 1;
However, if the readdate has ties, then row_number() would no longer help use dense_rank() instead.

How to merge the tailing fields in a sorted result in SQL?

If the following command is executed, we could see that there are 6 results.
> select competition.region, count(competition.score) as cnt from competition group by competition.region order by cnt desc;
## --Result--
CN 80
USA 60
SE 40
US 10
JP 10
KR 8
But what I want is to merge the fields from 4th line to the end into a field called 'other':
## --Result--
CN 80
USA 60
SE 40
OTHER 28
Is there any way to achieve that in a single SQL? Thanks!
Use a derived table to merge the others together:
select region, count(*) as cnt
(select case when competition.region in ('CN', 'USA', 'SE') then competition.region
else 'OTHER'
end as region
from competition) as dt
group by region
order by cnt desc
Edit:
Return 3 most common regions separately. The rest of them as OTHER.
select region, count(*) as cnt
(
select case when region in (select region from competition
group by region
order by count(*) desc
limit 3) then region
else 'OTHER'
end as region
) as dt
group by region
order by cnt desc
Note that only one region will be returned if it's a 3:rd place tie.

How can I select the first time a number shows up in more than one column in MySQL?

I have a table of flights, which have an origin and destination city, represented as a foreign id.
A very simplified example of this table looks like:
id | origin | destination
023 1 3
044 3 2
332 2 1
509 1 3
493 1 4
I need to get the first time that a city shows up as an origin or a destination; a list of all the flights that contain a city that hasn't been flown to or from yet.
What I would like to get for the above example would be:
023: 1, 3
044: 2
493: 4
Flights 332 and 509 aren't in the output because they only visit cities that have already been visited.
Here's what I've tried:
(SELECT distinct(origin), distinct(destination) FROM flights ORDER BY id)
Doesn't work because you can't select more than one distinct column
SELECT (distinct(origin) FROM flights ORDER BY id) UNION (distinct (destination) FROM flights ORDER BY id)
Doesn't work because of syntax errors, but mainly because it doesn't take into account that a city should be unique in the origin and destination columns.
If there's not a quick way to do this in SQL I'm also happy to just iterate through and keep track of cities that have been visited (this app has literally one user, and he doesn't care about a few milliseconds of computation because he's over 80), but I'd love to know just so that I can learn more about SQL!
This does it:
SELECT id, GROUP_CONCAT(city ORDER BY city) cities
FROM (
SELECT city, min(id) id
FROM (
SELECT origin city, MIN(id) id
FROM flights
GROUP BY city
UNION
SELECT destination city, MIN(id) id
FROM flights
GROUP BY city) u
GROUP BY city) x
GROUP BY id
DEMO

Order by COUNT per value

I have a table which stores IDs and the city where the store is located.
I want to list all the stores starting with the stores that are in the city where there are the most stores.
TABLE
ID CITY
1 NYC
2 BOS
3 BOS
4 NYC
5 NYC
The output I want is the following since I have the most stores in NYC, I want all the NYC location to be listed first.
1 NYC
4 NYC
5 NYC
2 BOS
3 BOS
SELECT count(City), City
FROM table
GROUP BY City
ORDER BY count(City);
OR
SELECT count(City) as count, City
FROM table
GROUP BY City
ORDER BY count;
Ahh, sorry, I was misinterpreting your question. I believe Peter Langs answer was the correct one.
This one calculates the count in a separate query, joins it and orders by that count (SQL-Fiddle):
SELECT c.id, c.city
FROM cities c
JOIN ( SELECT city, COUNT(*) AS cnt
FROM cities
GROUP BY city
) c2 ON ( c2.city = c.city )
ORDER BY c2.cnt DESC;
This solution is not a very optimal one so if your table is very large it will take some time to execute but it does what you are asking.
select c.city, c.id,
(select count(*) as cnt from city c2
where c2.city = c.city) as order_col
from city c
order by order_col desc
That is, for each city that you come across you are counting the number of times that that city occurs in the database.
Disclaimer: This gives what you are asking for but I would not recommend it for production environments where the number of rows will grow too large.
SELECT `FirstAddressLine4`, count(*) AS `Count`
FROM `leads`
WHERE `Status`='Yes'
AND `broker_id`='0'
GROUPBY `FirstAddressLine4`
ORDERBY `Count` DESC
LIMIT 0, 8