How to limit data to only the last 5 results - mysql

I have the query below to fetch the number of row and group them by week. It works great.
SELECT
WEEKOFYEAR(searched_on) AS weekno,
COUNT(*) AS num_search,
SUBDATE(searched_on, INTERVAL WEEKDAY(searched_on) DAY), INTERVAL + 0 DAY AS date_of_week,
FROM table
GROUP BY WEEK(DATE_SUB(searched_on, INTERVAL 1 DAY)) ORDER BY weekno ASC
How can I modify this in order to select only the last 5 results without using ORDER BY weekno DESC LIMIT 5 in order not to alter the way data has been arranged. Thanks

You could add the ORDER BY weekno DESC LIMIT 5 and push the query into a subquery, then have the outer query reorder ascending:
SELECT * FROM (
SELECT
WEEKOFYEAR(searched_on) AS weekno,
COUNT(*) AS num_search,
SUBDATE(searched_on, INTERVAL WEEKDAY(searched_on) DAY), INTERVAL + 0 DAY AS date_of_week,
FROM table
GROUP BY WEEK(DATE_SUB(searched_on, INTERVAL 1 DAY))
ORDER BY weekno DESC LIMIT 5
) inner
ORDER BY weekno

Simply use the limit used in mysql.
add the limit 5 after the code ORDER BY weekno ASC. means add it at the end of query.
Here is the tutorial.
Example
Use the below query:
SELECT
WEEKOFYEAR(searched_on) AS weekno,
COUNT(*) AS num_search,
SUBDATE(searched_on, INTERVAL WEEKDAY(searched_on) DAY), INTERVAL + 0 DAY AS date_of_week,
FROM table
GROUP BY WEEK(DATE_SUB(searched_on, INTERVAL 1 DAY)) ORDER BY weekno ASC
LIMIT 5 OFFSET (num_search-5) //add this line

Related

Need to optimize SQL Query - Taking lot a of time for execution

We have a below query which takes approximately 6-8 secs to execute.
Total number of records : 522954
(SELECT
*
FROM
tbl_insights_copy
WHERE insightscat = 21
AND submitedon >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))
ORDER BY submitedon DESC
LIMIT 5)
UNION
(SELECT
*
FROM
tbl_insights_copy
WHERE insightscat = 22
AND submitedon >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))
ORDER BY submitedon DESC
LIMIT 5)
UNION
(SELECT
*
FROM
tbl_insights_copy
WHERE insightscat = 23
AND submitedon >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))
ORDER BY submitedon DESC
LIMIT 5)
UNION
(SELECT
*
FROM
tbl_insights_copy
WHERE insightscat = 24
AND submitedon >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))
ORDER BY submitedon DESC
LIMIT 5)
Can someone help to optimize this query as to reduce the execution time.
Thanks in advance.
The only thing you are changing between one select and another, is the filter value of the column insightscat I am not sure that this is what you want but....
You may try the IN instruction for this. Example:
SELECT
*
FROM
tbl_insights_copy
WHERE insightscat in (20,21,22,23,24)
AND submitedon >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))
ORDER BY submitedon DESC
For this query:
SELECT ic.*
FROM tbl_insights_copy ic
WHERE insightscat = 21 AND
submitedon >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))
ORDER BY submitedon DESC
LIMIT 5
You want an index on tbl_insights_copy(insightscat, submittedon).
This should work for all the subqueries. This is probably the best approach with MySQL.
SELECT t1.*
FROM (SELECT t.* ,ROW_NUMBER() OVER (ORDER BY insightscat) AS Row
FROM
(select * from
tbl_insights_copy
WHERE insightscat in (20,21,22,23,24)
AND submitedon >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))
ORDER BY submitedon DESC ) as t ) t1
WHERE Row <= 5
Without SHOW CREATE TABLE, I am going to have to guess that you do not have the optimal
INDEX(insightscat, submitedon)
Since the SELECTs are distinct, use UNION ALL instead of the default UNION DISTINCT. This will avoid an unnecessary (but fast) de-dup pass over the 20 rows.
If you want 20 rows
If so, my suggestions above might be best.
If you want 6 rows
If you want only the latest 5 from any of those insightscats, then there are 3 possibilities.
Plan A
What Developer90 says in his Comment.
Plan B
( SELECT ... ORDER BY ORDER BY submitedon DESC LIMIT 5)
UNION ALL
( SELECT ... ORDER BY ORDER BY submitedon DESC LIMIT 5)
UNION ALL
( SELECT ... ORDER BY ORDER BY submitedon DESC LIMIT 5)
UNION ALL
( SELECT ... ORDER BY ORDER BY submitedon DESC LIMIT 5)
ORDER BY ORDER BY submitedon DESC LIMIT 5;
Each SELECT is very fast with my index. Then, the 20 rows of the UNION get sorted again and LIMIT 5 is applied. Again, very fast.
Using the IN as suggested by Developer90, may or may not effectively use my index. What version are you using?
Plan C
This option is hit or miss. That is, its performance depends heavily on the distribution of the data as to whether it will be very fast or very slow: Developer90 + INDEX(submitedon) (not including insightscat).

Check if last update time is greater than 10 minutes

I have the following query:
SELECT * FROM event_incidents order by last_update desc limit 1;
What I want is to get the first row and check if the last_update time on that row is greater than 10 minutes from the current time.
You can try like this:
select * from event_incidents
where last_update >= (NOW() - INTERVAL 10 MINUTE)
ORDER BY last_update desc
LIMIT 1;
You can do this using aggregation and a comparison in the where. If you have an index on last_update then:
SELECT (case when MAX(last_update) >= date_sub(now(), interval 10 minute)
then 'recent'
else 'ancient'
end)
FROM event_incidents ;
I'm not sure what you want to return, so I made up "recent" and "ancient".
Note:
If you just want a flag on a single row being returned:
SELECT ei.*,
(last_update > date_sub(now(), interval 10 minute)) as RecencyFlag
FROM event_incidents
ORDER BY last_update desc
LIMIT 1;

Select between dates issues

I have this SQL:
$sql="SELECT *
FROM table
WHERE expiresdate >= Date(Now())
AND expiresdate <= Date_add(Date(Now()), INTERVAL 10 day)
ORDER BY expiresdate ASC";
it should basically show all rows in the database that are going to expire within 10 days time however, lets say the expiredate was 2013-03-06 - this row will not display on any day after the expiredate
does anyone have any ideas?
This should be what you need:
SELECT
*
FROM
`table`
WHERE
expiresdate <= CURDATE() + INTERVAL 10 DAY
ORDER BY
expiresdate ASC

Count SQL results form first query in second query in single statement

Here's my SQL statement:
(select * from items
where items.created > curdate() - interval 2 week
order by items.created desc limit 0,10000000000)
union all
(select * from items
where items.created < curdate() - interval 2 week
order by items.popularity desc limit 0,15)
I'm trying to figure out a way to limit the entire result of the query to a certain number (say 25). As it is now, this result returns an unlimited number for the first result (which is what I want), then returns 15 for the second result. I want to be able to limit the whole query so that even if the first result returns 8, the second result returns 17, total 25.
I believe to do this, I have to use count() somehow in the first query, then subtract that from the total I want and use that number as the 2nd query's limit. I have no idea how this is done.
Thanks in advance!
Here is the required query -
select *
from
((select * from items
where items.created > curdate() - interval 2 week
order by items.created desc limit 0,10000000000)
union all
(select * from items
where items.created < curdate() - interval 2 week
order by items.popularity desc)) t
limit 0,25
Another select:
select * from
(
(select * from items
where items.created > curdate() - interval 2 week
order by items.created desc limit 0,10000000000)
union all
(select * from items
where items.created < curdate() - interval 2 week
order by items.popularity desc)
) uniond_tables_alias
limit 25
The uniond_tables_alias is an alias for the uniond section, you can choose any name you want.
No need for nested queries, simply do:
(select * from items
where items.created > curdate() - interval 2 week
order by items.created desc) # remove LIMIT here
UNION ALL
(select * from items
where items.created < curdate() - interval 2 week
order by items.popularity desc) # remove LIMIT here
LIMIT 25; # add LIMIT here
This'll return the 25 first results from the first SELECT if there's at least 25. Otherwise it will fill up the remaining results with the second SELECT results until the limit of 25 is reached.
SELECT 1 AS sortkey, * from items ....
UNION ALL
SELECT 2 AS sortkey, * from items ....
ORDER BY sortkey, etc.
LIMIT 25

SELECT records MySQL: between date + 1

Say I want to SELECT all records between two dates plus one record before and one record after that date? All records are ordered by date.
You could use a union combined with the limit statement. Something like what's below (untested, don't have access to mysql).
(select column from table where datefield > startdate and datefield < stopdate)
union
(select column from table where datefield < startdate order by datefield desc limit 1)
union
(select column from table where datefield > stopdate order by datefield limit 1)
This will give you the next row regardless of where it falls date-wise.
Thanks for syntax fix, ponies.
(select * from t where date < start_date order by date desc limit 1)
union (select * FROM t WHERE date between start_date and end_date)
union (select * from t where date > end_date order by date asc limit 1)
You can use functions to add or subtract values, like this:
select * from table where field1 < ADDDATE( CURTIME() , INTERVAL 1 DAY)
Check this link where there are some examples.
SELECT *
FROM table
WHERE date BETWEEN DATE_ADD(current_date(), INTERAL -1 DAY)
AND DATE_ADD(current_date(), INTERVAL 1 DAY);