MySQL Select show specific column values first - mysql

I have a table with about 3 million rows that consists of cities, the countries in which the city lies and some other data.
I want to retrieve for example limited rows where the name of the city starts with a certain string but list those that lie in a specific country first.
I tried to order them by country <> "us" which is very slow as the server has to sort all found rows before applying the limit.
Now I came up with the following statement:
(SELECT * FROM cities WHERE city LIKE "ab%" AND country = "at" LIMIT 5)
UNION
(SELECT * FROM cities WHERE city LIKE "ab%" LIMIT 5)
LIMIT 5
It is very fast with any prefix length but it looks kinda dirty.
Is there a more efficient way to do this?

select top 5 *
, case when country = 'ab' then 1
else 0 end as rnk
from cities
where lower(city) like 'ab%'
or country = 'ab'
order by rnk desc;

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.

How to sum top results?

I'm wondering how one would sum the results from a query?
I want to know how many people live in total in the three biggest cities in Norway. I'm using mysql, the world.sql sample database in mysql workbench.
This is the closest I've gotten
SELECT population
FROM city
WHERE CountryCode = 'NOR'
ORDER BY population DESC
LIMIT 3
There's a few problems here namely this gives me three results instead of one, and while using LIMIT which actually limits how many results it gives, not how many it uses.
Any ideas?
You would use a subquery:
SELECT SUM(population)
FROM (SELECT population
FROM city
WHERE CountryCode = 'NOR'
ORDER BY population DESC
LIMIT 3
) cp
simply sum the result:
select sum(population) from (SELECT population
FROM city
WHERE CountryCode = 'NOR'
ORDER BY population DESC
LIMIT 3) t1
select sum(population) from (SELECT population FROM city WHERE
CountryCode = 'NOR' ORDER BY population DESC LIMIT 3) temp
Read on subqueries.
Make your current query a subquery and get sum from your subquery.
SELECT SUM(population) FROM (
SELECT population
FROM city
WHERE CountryCode = 'NOR'
ORDER BY population DESC
LIMIT 3) p
You query will now act as a virtual table, from which you can you can write a select query to get the sum

fetch and find frquency count of generalized zipcode

I have table as below
ID NAME AGE ZIPCODE
1 A 29 321345
2 B 25 321375
....
and so on about 40K records.
I want to fetch distinct zipcodes only upto 4 digits.
like 3213* include both (321345 || 321375).
So is there any similar clause as GROUP BY for this?
If only distinct zipcodes(4-digit) is need, use this:
SELECT DISTINCT LEFT(zipcodes, 4) ZIPCODE_4_DIGIT FROM tbl
If the frequency is also needed, use another:
SELECT LEFT(zipcodes, 4) ZIPCODE_4_DIGIT, COUNT(1) FREQUENCY
FROM tbl
GROUP BY ZIPCODE_4_DIGIT;

MySQL Query to find row duplicates based on condition with limit

I have two tables:
Members:
id username
Trips:
id member_id flag_status created
("YES" or "NO")
I can do a query like this:
SELECT
Trip.id, Trip.member_id, Trip.flag_status
FROM
trips Trip
WHERE
Trip.member_id = 1711
ORDER BY
Trip.created DESC
LIMIT
3
Which CAN give results like this:
id member_id flag_status
8 1711 YES
9 1711 YES
10 1711 YES
My goal is to know if the member's last three trips all had a flag_status = "YES", if any of the three != "YES", then I don't want it to count.
I also want to be able to remove the WHERE Trip.member_id = 1711 clause, and have it run for all my members, and give me the total number of members whose last 3 trips all have flag_status = "YES"
Any ideas?
Thanks!
http://sqlfiddle.com/#!2/28b2d
In that sqlfiddle, when the correct query i'm seeking runs, I should see results such as:
COUNT(Member.id)
2
The two members that should qualify are members 1 and 3. Member 5 fails because one of his trips has flag_status = "NO"
You could use GROUP_CONCAT function, to obtain a list of all of the status ordered by id in ascending order:
SELECT
member_id,
GROUP_CONCAT(flag_status ORDER BY id DESC) as status
FROM
trips
GROUP BY
member_id
HAVING
SUBSTRING_INDEX(status, ',', 3) NOT LIKE '%NO%'
and then using SUBSTRING_INDEX you can extract only the last three status flags, and exclude those that contains a NO. Please see fiddle here. I'm assuming that all of your rows are ordered by ID, but if you have a created date you should better use:
GROUP_CONCAT(flag_status ORDER BY created DESC) as status
as Raymond suggested. Then, you could also return just the count of the rows returned using something like:
SELECT COUNT(*)
FROM (
...the query above...
) as q
Although I like the simplicity of fthiella's solution, I just can't think of a solution that depends so much on data representation. In order not to depend on it you can do something like this:
SELECT COUNT(*) FROM (
SELECT member_id FROM (
SELECT
flag_status,
#flag_index := IF(member_id = #member, #flag_index + 1, 1) flag_index,
#member := member_id member_id
FROM trips, (SELECT #member := 0, #flag_index := 1) init
ORDER BY member_id, id DESC
) x
WHERE flag_index <= 3
GROUP BY member_id
HAVING SUM(flag_status = 'NO') = 0
) x
Fiddle here. Note I've slightly modified the fiddle to remove one of the users.
The process basically ranks the trips for each of the members based on their id desc and then only keeps the last 3 of them. Then it makes sure that none of the fetched trips has a NO in the flag_status. FInally all the matching meembers are counted.

how to number the datas from mysql

This is a doubt on mysql select query
let me axplain my doubt with a simple example
consider this is my query
SELECT dbCountry from tableCountry
tableCountry has fields dbCuntryId, dbCountry and dbState
I have the result as
dbCountry
india
america
england
kenya
pakisthan
I need the result as
1 india
2 america
3 england
4 kenya
5 pakisthan
the numbers 12345 must be generated with the increase in data and it is not an autoincrement id.
How can i get it
is it something like loop
You can try this:
SELECT dbCountry,
(SELECT COUNT(*) FROM tableCountry t2 WHERE t2.dbCountry <= t1.dbCountry)
AS RowNum
FROM tableCountry t1
ORDER BY dbCountry
The following should do what you need. It uses a variable that is incremented and returned for each row:
SELECT
#rownum:=#rownum+1 number,
c.dbCountry
FROM
tableCountry c,
(SELECT #rownum:=0) r
If you want the result to always be in the same order you'll need to add an order by constraint to the query, for example, ORDER BY c.dbCountry to order by the country name.