I have a query that selects 3 random items from database table but I need to apply some more logic to the query based on the value of a field in the query.
This is what I have so far hope it makes sense. Have not fully tested it yet figured I would run it through you guys first see if there is anything that jumps out.
SELECT ord.id, keyword, url, daily_max
FROM orders AS ord
LEFT JOIN product_tasks AS tsk ON tsk.id = ord.task_id
LEFT JOIN product_groups AS grp ON grp.id = tsk.product_group
WHERE (
status = 'approved' AND
ord.total_actions_today < tsk.daily_max AND
grp.id = 1 AND
country_code = '$country' AND
(
CASE WHEN daily_max >= 5 THEN last_displayed < (NOW() - INTERVAL 30 MINUTE)
CASE WHEN daily_max >10 THEN last_displayed < (NOW() - INTERAVAL 5 MINUTE)
ELSE last_displayed < (NOW() - INTERAVAL 60 MINUTE)
)
)
GROUP BY ord.id
ORDER BY RAND()
LIMIT 3
Just tested the query and as I suspected I have my syntax wrong so any help would be appreciated:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CASE WHEN daily_max >10 THEN last_displayed < (NOW() - INTERAVAL 5 MINUTE) ' at line 14
Edit - Fixed
(After several attempts + edits):
SELECT ord.id, keyword, url, daily_max
FROM orders AS ord
LEFT JOIN product_tasks AS tsk ON tsk.id = ord.task_id
LEFT JOIN product_groups AS grp ON grp.id = tsk.product_group
WHERE (
status = 'approved' AND
ord.total_actions_today < tsk.daily_max AND
grp.id = 1 AND
country_code = 'us'
AND last_displayed <
CASE WHEN (daily_max >= 5) THEN (NOW() - INTERVAL 30 MINUTE)
WHEN (daily_max >10) THEN (NOW() - INTERVAL 5 MINUTE)
ELSE (NOW() - INTERVAL 60 MINUTE)
END
)
GROUP BY ord.id
ORDER BY RAND()
LIMIT 3
http://sqlfiddle.com/#!2/ac4499/1
Solved DOH missing ) in the where statement
Move the last_displayed out of the CASE, such that it is compared to a single value as projected out of the CASE statement:
WHERE...
AND last_displayed <
CASE WHEN (daily_max >= 5) THEN (NOW() - INTERVAL 30 MINUTE)
WHEN (daily_max >10) THEN (NOW() - INTERVAL 5 MINUTE)
ELSE (NOW() - INTERVAL 60 MINUTE)
END;
Also note a couple of typos - INTERVAL not INTERAVAL, and just one CASE is required.
SqlFiddle here
Your structure of your WHERE was not correct. Try the following:
SELECT ord.id, keyword, url, daily_max
FROM orders AS ord
LEFT JOIN product_tasks AS tsk ON tsk.id = ord.task_id
LEFT JOIN product_groups AS grp ON grp.id = tsk.product_group
WHERE (
status = 'approved' AND
ord.total_actions_today < tsk.daily_max AND
grp.id = 1 AND
country_code = '$country' AND
(
CASE WHEN daily_max >10 THEN (NOW() - INTERAVAL 5 MINUTE)
WHEN daily_max >= 5 THEN (NOW() - INTERVAL 30 MINUTE)
ELSE (NOW() - INTERAVAL 60 MINUTE)
END > last_displayed
)
)
GROUP BY ord.id
ORDER BY RAND()
LIMIT 3
Related
I need to get all the Paid and Provisional Entry from the last 7 days but I keep getting everything returned. I am unsure what I am doing wrong I have read through quite a few posts on here and cant fathom what its. MySQL 5.6 if it makes any diffrence to what I have been doing.
SELECT
DATE_FORMAT(FROM_UNIXTIME(ct.entry_date),'%d/%m/%Y') AS booking_date,
cd.field_id_69 AS marriage_date,
cd.field_id_54 AS email_address,
CONCAT(cd.field_id_9, ' ', cd.field_id_10) AS bride_name,
CONCAT(cd.field_id_13, ' ', cd.field_id_14) AS groom_name,
ctco.title AS centre_and_course_date,
(SELECT DATE_FORMAT(FROM_UNIXTIME(col_id_1),'%d/%m/%Y') FROM
exp_channel_grid_field_50 cg WHERE cg.entry_id = ctco.entry_id ORDER BY
cg.row_id DESC LIMIT 1) AS course_end_date,
ct.status AS payment_status
FROM exp_channel_titles ct
JOIN exp_channel_data cd ON cd.entry_id = ct.entry_id
JOIN exp_relationships rco ON rco.parent_id = ct.entry_id AND rco.field_id = 41
JOIN exp_channel_titles ctco ON rco.child_id = ctco.entry_id
WHERE ct.channel_id = 2
AND ct.entry_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY)
AND ct.status = 'Paid'
AND ct.status = 'Provisional';
There is problem in your WHERE statement
AND ct.entry_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND ct.status = 'Paid' AND ct.status = 'Provisional';
The BETWEEN keyword needs to be followed by 2 dates, so ct.status = 'Paid' will be converted to DATE which return NULL. Hence your WHERE statement turns into.
AND ct.entry_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND NULL AND ct.status = 'Provisional';
The result is that your query will return all data that have status = 'Provisional'.
You could try to modify the WHERE statement to
AND ct.entry_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE() AND ct.status IN ('Paid', 'Provisional');
The syntax of BETWEEN is BETWEEN startdate AND enddate. You wrote:
ct.entry_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND ct.status = 'Paid'
So it's using DATE_SUB(CURDATE(), INTERVAL 7 DAY) as the starting date, and ct.status = 'Paid' as the ending date. I'm surprised it's matching anything.
Change it to:
ct.entry_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE()
Or if you don't have any dates in the future, you can simply write:
ct.entry_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
I'm using MySQL 5.0, and I need to fine tune this query. Can anyone please tell me what tuning I can do in this?
SELECT DISTINCT(alert_master_id) FROM alert_appln_header
WHERE created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
AND alert_master_id NOT IN (
SELECT DISTINCT(alert_master_id) FROM alert_details
WHERE end_date IS NULL AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
UNION
SELECT DISTINCT(alert_master_id) FROM alert_sara_header
WHERE sara_master_id IN
(SELECT alert_sara_master_id FROM alert_sara_lines
WHERE end_date IS NULL) AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
) LIMIT 5000;
The first thing that I'd do is rewrite the subqueries as joins:
SELECT h.alert_master_id
FROM alert_appln_header h
JOIN schedule_config c
ON c.schedule_name = 'Purging_Config'
LEFT JOIN alert_details d
ON d.alert_master_id = h.alert_master_id
AND d.end_date IS NULL
AND d.created_date < CURRENT_DATE - INTERVAL c.parameters DAY
LEFT JOIN (
alert_sara_header s
JOIN alert_sara_lines l
ON l.alert_sara_master_id = s.sara_master_id
)
ON s.alert_master_id = h.alert_master_id
AND s.end_date IS NULL
AND s.created_date < CURRENT_DATE - INTERVAL c.parameters DAY
WHERE h.created_date < CURRENT_DATE - INTERVAL c.parameters DAY
AND d.alert_master_id IS NULL
AND s.alert_master_id IS NULL
GROUP BY h.alert_master_id
LIMIT 5000
If it's still slow after that, re-examine your indexing strategy. I'd suggest indexes over:
alert_appln_header(alert_master_id,created_date)
schedule_config(schedule_name)
alert_details(alert_master_id,end_date,created_date)
alert_sara_header(sara_master_id,alert_master_id,end_date,created_date)
alert_sara_lines(alert_sara_master_id)
OK, this may be just a shot in the dark, but I think you don't need as many DISTINCT here.
SELECT DISTINCT(alert_master_id) FROM alert_appln_header
WHERE created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
AND alert_master_id NOT IN (
-- removed distinct here --
SELECT alert_master_id FROM alert_details
WHERE end_date IS NULL AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
UNION
-- removed distinct here --
SELECT alert_master_id FROM alert_sara_header
WHERE sara_master_id IN
(SELECT alert_sara_master_id FROM alert_sara_lines
WHERE end_date IS NULL)
AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
) LIMIT 5000;
Since using the DISTINCT is very costly, try to avoid it. In the first WHERE clause you are checking for ids that are NOT within some result, so it shouldn't matter if in that result some ids appear more than once.
Trying to clean up a long query I have come up with a modified version, but now the old query with certain parameters returns 6 rows, but my version returns none. I have been straining my eyes to spot the difference but cannot find any. Here are the WHERE clauses from the two versions with identical set of parameters:
The ugly (but working) version:
SELECT ...
FROM table as a
WHERE 1=1
and a.title LIKE '%Manager%'
and a.status='Approved'
and (a.effected_date<=now())
and (
(DATE_ADD(a.effected_date, INTERVAL 30 DAY) >= now() AND a.is_hotjob=0)
or
(DATE_ADD(a.effected_date, INTERVAL 30 DAY) >= now() AND a.is_hotjob=1)
)
My cleaned (but broken) version:
SELECT ...
FROM `table` AS `a`
WHERE CONCAT(`a`.`title`, ' | ', `a`.`job_detail_section`) LIKE '%Manager%' AND
`a`.`status` = 'Approved' AND
`a`.`effected_date` <= '2013-12-30' AND
(`a`.`effected_date` >= '2013-11-30' OR `a`.`is_hotjob` = '1')
You got the last AND/OR backwards:
`a`.`effected_date` <= '2013-12-30' OR
(`a`.`effected_date` >= '2013-11-30' AND `a`.`is_hotjob` = '1')
EDIT: after reformatting, the answer looks more like:
SELECT ...
FROM `table` AS `a`
WHERE CONCAT(`a`.`title`, ' | ', `a`.`job_detail_section`) LIKE '%Manager%' AND
`a`.`status` = 'Approved' AND
`a`.`effected_date` <= now() AND
`a`.`effected_date` >= DATE_ADD(a.effected_date, INTERVAL 30 DAY)
and POSSIBLY:
AND `a`.`is_hotjob` IN (0,1)
As a substitute of-
(DATE_ADD(a.effected_date, INTERVAL 30 DAY) >= now() AND a.is_hotjob=0)
or
(DATE_ADD(a.effected_date, INTERVAL 30 DAY) >= now() AND a.is_hotjob=1)
You could use the following-
(`a`.`effected_date` >= '2013-11-30' AND `a`.`is_hotjob` in (0, 1))
But according to the additional info i.e. a.is_hotjob has value either 0 or 1 then there is no need to mention it in the condition at all. So basically the following format will be sufficient-
`a`.`effected_date` >= '2013-11-30'
First of all: sorry for the title, but maybe I will find a better one later.
I asked this some minutes ago, but since I was not able to describe what I want I try it again :)
Here is my table structure:
http://sqlfiddle.com/#!2/b25f9/37
The table is used to store user sessions.
Out of this I would like to generate a stacked bar chart that should show how many active users I have. My idea was that I group the users based on their online-times of the last days like this
Lets say its friday:
Group B: Users that were online thursday (and today)
Group C: Users that were not online thursday but wednesday (and today)
Group D: Users that were not online thursday or wednesday but tuesday (and today)
Group E: Users that were not online thursday, wednesday or tuesday but last monday, sunday or saturday (and today)
Group A: Users that do not match the other groups (but were only today)
I only want to know the number of users in those groups (for a specific day)
a user can only be in ONE of these groups (for the same day)
Another Update: Accidently (by copy&paste) had starttime = ... or starttime = ... but it should be starttime = ... or endtime = ...
UPDATE:
To explain my query in more detail (in the final query there are even more comments):
First we simply got
SELECT
...
FROM gc_sessions s
WHERE DATE(starttime) = CURDATE() OR DATE(endtime) = CURDATE()
That's nothing more like saying "give me all users whose session started today or ended today". Having to consider those two times again and again makes the query a bit clumsy, but actually it's not that complicated.
So, usually we would use the COUNT() function to count something, obviously, but since we want "conditional counting", we simply use the SUM() function and tell it when to add 1 and when not.
SUM (CASE WHEN ... THEN 1 ELSE 0 END) AS a_column_name
The SUM() function examines now each row in the result set of sessions from today. So for each user in this result set we look if this user was online the date we specify. It doesn't matter how many times he/she was online, so for performance reasons we use EXISTS. With EXISTS you can specify a subquery which stops as soon as something is found, so it doesn't matter what it returns when something is found, as long as it's not NULL. So don't get confused why I selected 1. In the subquery we have to connect the user which is currently examined from the outer query with the user from the inner query (subquery) and specify the time window. If all criterias meet count 1 else 0 like explained before.
SUM(CASE WHEN
EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 1 DAY)
OR (date(endtime) = CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS todayAndYesterday,
Then we make a column for each condition and voila, you have all you need in one query. So with your updated question your criteria has changed, we just have to add more rules:
SELECT
/*this is like before*/
SUM(CASE WHEN
EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 1 DAY)
OR (date(endtime) = CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS FridayAndThursday,
SUM(CASE WHEN
EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 2 DAY)
OR (date(endtime) = CURDATE() - INTERVAL 2 DAY)))
/*this one here is a new addition, since you don't want to count the users that were online yesterday*/
AND NOT EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 1 DAY)
OR (date(endtime) = CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS FridayAndWednesdayButNotThursday,
SUM(CASE WHEN
EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 3 DAY) /* minus 3 days to get tuesday*/
OR (date(endtime) = CURDATE() - INTERVAL 3 DAY)))
/*this is the same as before, we check again that the user was not online between today and tuesday, but this time we really use BETWEEN for convenience*/
AND NOT EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) BETWEEN CURDATE() - INTERVAL 2 DAY AND CURDATE() - INTERVAL 1 DAY)
OR (date(endtime) BETWEEN CURDATE() - INTERVAL 2 DAY AND CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS FridayAndTuesdayButNotThursdayAndNotWednesday,
.../*and so on*/
FROM gc_sessions s
WHERE DATE(starttime) = CURDATE() OR DATE(endtime) = CURDATE()
So, I hope you get the idea now. Any more questions? Feel free to ask.
end of update
Answer to previous version of question:
select
SUM(CASE WHEN EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 1 DAY)
OR (date(starttime) = CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS todayAndYesterday,
SUM(CASE WHEN EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) BETWEEN CURDATE() - INTERVAL 2 DAY AND CURDATE() - INTERVAL 1 DAY)
OR (date(starttime) BETWEEN CURDATE() - INTERVAL 2 DAY AND CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS todayAndYesterdayOrTheDayBeforeYesterday,
SUM(CASE WHEN EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) BETWEEN CURDATE() - INTERVAL 7 DAY AND CURDATE() - INTERVAL 1 DAY)
OR (date(starttime) BETWEEN CURDATE() - INTERVAL 7 DAY AND CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS todayAndWithinTheLastWeek
from gc_sessions s
where date(starttime) = CURDATE()
or date(endtime) = CURDATE()
Instead of relying on session table, I suggest you to create separate table, which stores 2 fields, date and user_id.
Every time user logs-in you need to insert new entry into this table.
This way you will be able to retrieve all the 3 requirement of yours.
Example table:
CREATE TABLE `test`.`user_login_history` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`userid` INTEGER UNSIGNED NOT NULL,
`date` DATETIME NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE = InnoDB;
Once a user login, check whether he/she has login today or not:
select count(*) from user_login_history where
userid = 1 and `date` = '2013-01-28 00:00:00';
If the returned value is 1, means he/she has login today. no changes needed.
but, if the returned value is 0, means he/she has not login today. So record it down.
insert into user_login_history(userid,`date`)values(1,'2013-01-28 00:00:00');
Q1. How many users were online TODAY that were also online YESTERDAY?
select count(*) from user_login_history u where
u.`date` = '2013-01-28 00:00:00' and
(
select count(*) from user_login_history v where
v.`date` = '2013-01-27 00:00:00' and
v.userid = u.userid
) = 1;
Q2. How many users were online TODAY that were also online within in the last TWO DAYS
select count(*) from user_login_history u where
u.`date` = '2013-01-28 00:00:00' and
(
select count(*) from user_login_history v where
v.`date` >= '2013-01-26 00:00:00' and
v.`date` <= '2013-01-27 00:00:00' and
v.userid = u.userid
) > 0;
Q3. How many users were online TODAY that were also online within the last 7 DAYS
select count(*) from user_login_history u where
u.`date` = '2013-01-28 00:00:00' and
(
select count(*) from user_login_history v where
v.`date` >= '2013-01-21 00:00:00' and
v.`date` <= '2013-01-27 00:00:00' and
v.userid = u.userid
) > 0;
For yesterday
select id from gc_sessions where id in
(
select id
from gc_sessions
where starttime > subdate(current_date, 2)
and endtime < subdate(current_date, 1)
)
and starttime > subdate(current_date, 1);
For 2 Days
select id from gc_sessions where id in
(
select id
from gc_sessions
where starttime > subdate(current_date, 3)
and endtime < subdate(current_date, 1)
)
and starttime > subdate(current_date, 1);
For 7 Days
select id from gc_sessions where id in
(
select id
from gc_sessions
where starttime > subdate(current_date, 8)
and endtime < subdate(current_date, 1)
)
and starttime > subdate(current_date, 1);
You need to add a subquery that loads the data from the specified range (eg, 1day/2day/7days) and compares it with the data for the current day.
set #range = 7;
select * from gc_sessions
WHERE user in (SELECT user from gc_sessions
where starttime between subdate(current_date, #range) AND subdate(current_date, 1))
AND starttime > subdate(current_date, 0)
Where #range holds information about the number of days. See your expanded sql fiddle at - http://sqlfiddle.com/#!2/9584b/24
SELECT today.user
, GROUP_CONCAT(DISTINCT today.ip) ip
FROM gc_sessions today
JOIN gc_sessions yesterday
ON DATE(yesterday.starttime) = DATE(today.starttime) - INTERVAL 1 DAY
AND today.user = yesterday.user
WHERE DATE(today.starttime) = '2013-01-10'
GROUP
BY today.user;
I would like to combine different results from the same table as one big result.
SELECT host_name,stats_avgcpu,stats_avgmem,stats_avgswap,stats_avgiowait
FROM sar_stats,sar_hosts,sar_appgroups,sar_environments
WHERE stats_host = host_id
AND host_environment = env_id
AND env_name = 'Staging 2'
AND host_appgroup = group_id
AND group_name = 'Pervasive'
AND DATE(stats_report_time) = DATE_SUB(curdate(), INTERVAL 1 DAY)
SELECT AVG(stats_avgcpu),AVG(stats_avgmem),AVG(stats_avgswap),AVG(stats_avgiowait)
FROM sar_stats
WHERE stats_id = "stat_id of the first query" and DATE(stats_report_time)
BETWEEN DATE_SUB(curdate(), INTERVAL 8 DAY) and DATE_SUB(curdate(), INTERVAL 1 DAY)
SELECT AVG(stats_avgcpu),AVG(stats_avgmem),AVG(stats_avgswap),AVG(stats_avgiowait)
FROM sar_stats
WHERE stats_id = "stat_id of the first query" and DATE(stats_report_time)
BETWEEN DATE_SUB(curdate(), INTERVAL 31 DAY) and DATE_SUB(curdate(), INTERVAL 1 DAY)
Desired output would be something like ...
host_name|stats_avgcpu|stats_avgmem|stats_avgswap|stats_avgiowait|7daycpuavg|7daymemavg|7dayswapavg|7dayiowaitavg|30daycpuavg|30daymemavg|....etc
SQL Fiddle
http://sqlfiddle.com/#!8/4930b/3
It seems like this is what you want. I updated the first query to use proper ANSI JOIN syntax and then for the additional two queries they were joined via a LEFT JOIN on the stats_host field:
SELECT s.stats_host,
h.host_name,
s.stats_avgcpu,
s.stats_avgmem,
s.stats_avgswap,
s.stats_avgiowait,
s7.7dayavgcpu,
s7.7dayavgmem,
s7.7dayavgswap,
s7.7dayavgiowait,
s30.30dayavgcpu,
s30.30dayavgmem,
s30.30dayavgswap,
s30.30dayavgiowait
FROM sar_stats s
INNER JOIN sar_hosts h
on s.stats_host = h.host_id
INNER JOIN sar_appgroups a
on h.host_appgroup = a.group_id
and a.group_name = 'Pervasive'
INNER JOIN sar_environments e
on h.host_environment = e.env_id
and e.env_name = 'Staging 2'
LEFT JOIN
(
SELECT s.stats_host,
AVG(s.stats_avgcpu) AS '7dayavgcpu',
AVG(s.stats_avgmem) AS '7dayavgmem',
AVG(s.stats_avgswap) AS '7dayavgswap',
AVG(s.stats_avgiowait) AS '7dayavgiowait'
FROM sar_stats s
WHERE DATE(stats_report_time) BETWEEN DATE_SUB(curdate(), INTERVAL 8 DAY) AND DATE_SUB(curdate(), INTERVAL 1 DAY)
GROUP BY s.stats_host
) s7
on s.stats_host = s7.stats_host
LEFT JOIN
(
SELECT s.stats_host,
AVG(s.stats_avgcpu) AS '30dayavgcpu',
AVG(s.stats_avgmem) AS '30dayavgmem',
AVG(s.stats_avgswap) AS '30dayavgswap',
AVG(s.stats_avgiowait) AS '30dayavgiowait'
FROM sar_stats s
WHERE DATE(s.stats_report_time) BETWEEN DATE_SUB(curdate(), INTERVAL 31 DAY) AND DATE_SUB(curdate(), INTERVAL 1 DAY)
GROUP BY s.stats_host
) s30
on s.stats_host = s30.stats_host
WHERE DATE(s.stats_report_time) = DATE_SUB(curdate(), INTERVAL 1 DAY);
see SQL Fiddle with Demo