MySQL - Select 3 highest values and sort alphabetically - mysql

I have this table with thousands of rows:
id | Keyword | hits
1 cat 3
2 cats 5
3 castle 1
4 cream 2
5 car 12
. . .
I want to select the 3 rows with most hits and then order alphabetically.
So I want to return:
car
cat
cats
I have this:
SELECT keyword,hits FROM table ORDER BY hits DESC, keyword ASC LIMIT 3
but it only orders by hits and then if hits are equal it orders alphabetically.

Wrap your SELECT - that gets only the 3 rows with highest hits - as a subquery inside another query that orders by the keyword:
SELECT * --- or just: SELECT keyword
FROM
( SELECT keyword, hits
FROM table
ORDER BY hits DESC
, keyword ASC --- this is needed only in case of ties at 3rd place
LIMIT 3
) tmp
ORDER BY keyword

You could use a subquery (don't know about your mysql version).
SELECT *
FROM
(SELECT keyword, hits
FROM table
ORDER BY hits DESC
LIMIT 0,3) AS topThree
ORDER BY keyword ASC

Related

MySQL select where not in another returned data from sql statement

I have this problem where I want to first select 8 elements from a mysql database ordering by id DESC.
Then I want to select another group of results (8 items), this time order by date DESC but the results here I want to ensure that they are not already on the fisrt query the one for ordering by id.
The data is in the same table just with different columns like id,name,date,.
So far I have tried writing different queries to get the data but the data contains some similar items of which that is what I don't want.
Here are the queries I have written;
this returns 8 items sorted by id DESC
SELECT name FROM person order by id DESC LIMIT 8;
this returns 8 items also but sorted by date DESC
SELECT name FROM person order by date DESC LIMIT 8;
the returned data contain duplicate items!
You could use a nested query, first select the first 8 id's, then select the first 8 records ordered by date, excluding those id's:
SELECT name FROM person
WHERE id NOT IN
(SELECT id FROM person order by id DESC LIMIT 8) AS exc
ORDER BY date DESC LIMIT 8
The first query should return the primary key for the table. If name is the key then so be it, but probably that id field is the better choice.
Then we can write the query like this:
SELECT p.name
FROM Person p
WHERE NOT EXISTS (
SELECT 1
FROM (SELECT id FROM Person ORDER BY id DESC LIMIT 8) p0
WHERE p0.id = p.id
)
ORDER BY p.date DESC
LIMIT 8;
We could also use an exclusion join which is usually slower, but in this case reduces one level of nesting so it might do better:
SELECT p.name
FROM Person p
LEFT JOIN (
SELECT id
FROM Person
ORDER BY id DESC
LIMIT 8
) p0 ON p0.id = p.id
WHERE p0.id is null
ORDER BY p.date DESC
LIMIT 8;
One other thing to keep in mind is MySQL is strict about what kinds of subquery can use the LIMIT keyword. Specifically, you need it to be a derived table. I know the exclusion join option should qualify, but I'm less sure of the NOT EXISTS() option.
Why not generate both resultsets with a single query? We can combine window functions, order by, and limit to generate a resultset containing the top 8 rows per id and the top 8 rows per date, while avoiding duplicates:
select *
from (
select p.*,
row_number() over(order by id desc) rn_id,
row_number() over(order by date desc) rn_dt
from person p
) p
order by case when rn_id <= 8 then rn_id else 9 end, rn_dt
limit 16
In the subquery, the window functions enumerate records by descending id and date. The outer query performs a conditional sort that puts the top 8 id first, and orders the rest of the records by descending date. All that is left to do is retain the top 16 results from the query. You don't need to worry about duplicates since the table is scanned only once.
Here is a small test case:
id
date
1
2022-11-11
2
2022-11-09
3
2022-11-05
4
2022-11-06
5
2022-11-07
6
2022-11-08
7
2022-11-10
For this sample data, and given a target of 3 + 3 records (instead of 8 + 8 in our code), the query returns:
id
date
rn_id
rn_dt
7
2022-11-10
1
2
6
2022-11-08
2
4
5
2022-11-07
3
5
1
2022-11-11
7
1
2
2022-11-09
6
3
4
2022-11-06
4
6
Typically, id 7, which has both the greatest id the second latest date, shows up in the first part of the resultset (the top 3 rows are sorted by descending id), but is not repeated in the second part.
Demo on DB fiddle

Select most searched keyword from MySql group by q and user_id

I want to show most searched keyword in my website. my search log table is this:
id
user_id
q
1
1
shoes
2
2
cover
3
1
phone
4
3
shoes
5
3
shoes
6
3
cover
7
4
shoes
I write this query to get unique search q and show them sort by repeat time:
SELECT `q`, COUNT(`q`) AS `value_occurrence` FROM search_queries GROUP BY q ORDER BY `value_occurrence` DESC
but there is an issue, if some one search shoes for 10 time, my query's value_occurrence increase 10, but i want just count any keyword for any single user_id just one time.
i try this but it's not correct way:
GROUP BY q,user_id
You must count the distinct user_ids:
SELECT q, COUNT(DISTINCT user_id) AS value_occurrence
FROM search_queries
GROUP BY q
ORDER BY value_occurrence DESC

Advanced mysql query which sort down specific records on result set irrespective of its default sorting?

I have a query which actually have a sorting using order by clause. i have a table like following...
user_id user_name user_age user_state user_points
1 Rakul 30 CA 56
2 Naydee 29 NY 144
3 Jeet 40 NJ 43
.....
i have following query...
select * from users where user_state = 'NY' order by user_points desc limit 50;
This gives me the list of 50 people with most points. I wanted to give least preference to few people who's id's were known. Incase if i do not have enough 50 records then those id's should come in the last in the list. I do not want the users 2 and 3 to come on top of the list even though they have higher points... those people should come on the last of the list from the query. Is there any way to push specific records to last on result set irrespective of query sorting ?
If you want to move specific records (like user_id = 2 and 3) down to the list; Then you can run below Query:
mysql> select *,IF(user_id=2 or user_id=3,0,1) as list_order from users where user_state = 'NY' order by list_order desc, user_points desc limit 50;
select * from (
select *
from users
where user_state = 'NY'
-- this order by ensures that 2 and 3 are included
order by case when user_id in (2,3) then 1 else 2 end, user_points desc
limit 50
) as top48plus2n3
-- this order by ensures that 2 and 3 are last
order by case when user_id in (2,3) then 2 else 1 end, user_points desc
Edit: changed id by user_id and corrected outside order by (sorry about that)
On the inner select:
By using this case calculation, what you do is ensuring that records with ids equal to 2 and 3 are "important" (firstly ordered in the order by). Those receive 1 while the others receive 2 as order value, only after that points are relevant.
On the outer select:
Records with ids 2 and 3 recieve 2 as order value, while the rest recieve 1. So they go last irrespective of its "default"
Here you have a reduced fiddle http://sqlfiddle.com/#!9/377c1/1

How to get the end of an Ascending list of records with a limit?

I'm fighting a bit with a query I'm building. Let's say I've got a DB table like this:
id | some_string
----------------
1 | 'lala'
2 | 'jeje'
3 | 'poopoo'
4 | 'wicked wicked'
I now want to get the last three records (2, 3, and 4) ordered ascending by key. I tried this:
SELECT * FROM tableName LIMIT 3 ORDER BY id ASC
This gets me the first three records, instead of the last three. I can of course also use the query below, which gets me the correct records, but then I don't get them in Ascending order:
SELECT * FROM tableName LIMIT 3 ORDER BY id DESC
Does anybody know how I can get the last three records in an ascending order? All tips are welcome!
select * from (
select * from table_name order by id desc limit 3
) last_3_rows
order by id
Sort on the resulting result set ie. do a select * from (<your query here>) order by id
This is a query inside another query. that reorders your query.(SQL - How to reorder a select query that uses the limit constraint)
select * FROM (SELECT * FROM tableName LIMIT 3 ORDER BY id DESC) AN_UNUSUAL_NAME ORDER BY id ASC

MySQL select top ten records with no duplicate uid

I have the following table (user_record) with millions of rows like this:
no uid s
================
1 a 999
2 b 899
3 c 1234
4 a 1322
5 b 933
-----------------
The uid can be duplicate .What I need is to show the top ten records(need inclued uid and s) with no duplicate uid order by s (desc). I can do this by two steps in the following SQL statements:
SELECT distinct(uid) FROM user_record ORDER BY s DESC LIMIT 10
SELECT uid,s FROM user_record WHERE uid IN(Just Results)
I just wana know is there a bit more efficient way in one statement?
Any help is greatly appreciated.
ps:I also have following the SQL statement:
select * from(select uid,s from user_record order by s desc) as tb group by tb.uid order by tb.s desc limit 10
but it's slow
The simpliest would be by using MAX() to get the highest s for every uid and sorted it based on the highest s.
SELECT uid, MAX(s) max_s
FROM TableName
GROUP BY uid
ORDER BY max_s DESC
LIMIT 10
SQLFiddle Demo
The disadvantage of the query above is that it doesn't handles duplicates if for instance there are multiple uid that have the same s and turn out to be the highest value. If you want to get the highest value s with duplicate, you can do by calculating it on the subquery and joining the result on the original table.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT DISTINCT s
FROM TableName
ORDER BY s DESC
LIMIT 10
) b ON a.s = b.s
ORDER BY s DESC