Table :
a | b
1 | 15
2 | 10
3 | 20
4 | 30
Query:
SELECT AVG(table.b) FROM table ORDER BY table.a ASC LIMIT 3
Will return 18.75 instead of expected 15.
How can I change the query to get my expected result (AVG value for a limited amount of rows)?
You need to use subquery:
SELECT AVG(b)
FROM (SELECT b
FROM table
ORDER BY table.a ASC
LIMIT 3) sub
EDIT:
Without subquery the order of execution is like:
FROM
AVG (AVG is calculated using all values)
ORDER BY (but there is only one value)
LIMIT (LIMIT 3 on one value do nothing)
With subquery the order of execution is like:
FROM
ORDER BY
LIMIT (only 3 values)
outer query AVG (average is calculated using only 3 values)
More info: Logical query processing (TOP/OFFSET FETCH is the same as LIMIT).
Try this instead:
SELECT AVG(A.b) FROM
(SELECT `table`.b FROM `table` ORDER BY `table`.a ASC LIMIT 3) A;
DEMO
Related
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
I have a table that looks like this:
id
count
1
100
2
50
3
10
4
10
I want to run select * query ,by using a new column called cumulative_sum , such that this new column is not create to the table rather is being queried (calculated) every time and use to select rows lying between limit and offset, + 1 more row after it.
Suppose I provide offset =130 and limit= 25 (i.e. range 130 to 155) then it should return row 2 row3 and row 4 (ie. last row + 1)
id
count
cumulative_sum
1
100
100
2
50
150
3
10
160
4
10
170
How to write a Mysql query for the above condition?
I trying to achieve this for quite some time now please help me to understand how to do this?
Cummulative sum column is just sum of = last rows cummulative sum + count of present row
On MySQL 8+, we can simply use SUM() as an analytic function:
SELECT id, count, SUM(count) OVER (ORDER BY id) AS cumulative_sum
FROM yourTable
ORDER BY id;
On earlier versions of MySQL, we can use a correlated subquery to find the rolling sum:
SELECT id, count,
(SELECT SUM(t2.count) FROM yourTable t2
WHERE t2.id <= t1.id) AS cumulative_sum
FROM yourTable t1
ORDER BY id;
Use Window function to achieve this (works with MySQL 8.0 and above):
SELECT id, count, sum(count) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as cummulative_sum
FROM your_table
ORDER BY id
I have a query that returns following values:
TemplateCode Total
1418 35
7419 31
7418 31
8325 17
15623 17
4997 17
I want all the rows with top 3 Total values
In my query if I include LIMIT 3 in the query then it gives only 3 which I don't want. I don't want to include LIMIT because count may vary from time to time.
How can I include condition on Total and always get top 3 count values
My current query is like:
select TemplateCode, count(*) as Total from table group by TemplateCode
order by Total desc
limit 3
I think this does what you want:
select t.*
from t
where t.Total >= (select distinct t2.Total
from t t2
order by t2.Total desc
limit 2, 1
);
This assumes that you want the third distinct value. If you just want the third value, remove the distinct.
You could use a inner join on subquery for the top 3 total
select m.TemplateCode , m.total
from my_table m
inner join (
select Total
from my_table
order by Total Desc
limit 3
) t on t.total = m.total
order by m.total, m.TemplateCode
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
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