My following query shows the date and the count of the emails found on each day (last 2 days)
My problem is that if no emails are found today, the today date will not be displayed on the output. (if yesterday has emails, it will show only 1 row with yesterday date and email).
How can I edit my query to always show 2 rows, today and yesterday, date and number of emails even zero?
SELECT maildate,
COUNT(*) AS totalEmails
FROM emails
WHERE maildate >= Date_add(Curdate(), interval - 2 DAY)
AND company_id = 1
GROUP BY DATE(maildate)
ORDER BY maildate desc
There are many tricks to creating a list of dates (or numeric sequences similarly). The one I like to use with MySQL is using #sqlvariables. I will typically start with a baseline value such as your date -2 days. I will do a cross-join to any other table in the database that has at least as many records as you expect in your output... Say 30 days, or a whole year 366 days, or longer. The inner sql variable prep will keep increasing itself by whatever increment (you could even do date ranges such as begin/end of a week, month, etc). Now you have your table of all possible dates you are looking to fill.
Now, I do a secondary query by the value -- in this case your email date and apply the group by. Using the where clause in this query will make IT faster since it can utilize the date on its query result set before returning for the LEFT-JOIN to the date range result set.
Now, your simple left-join gets both parts of all dates to be included and those corresponding counts that do exist.
Note the table alias "AnyTableWithAtLeast3RecordInIt" in the "JustDates" query could in-fact be your "emails" table. Since we don't care about any criteria except a record exists, and we are applying a limit of 30 days in my example, it will be instantaneous.
select
JustDates.DateToInclude,
coalesce( SumCnts.TotalEmails, 0 ) TotalEmails
from
( select
#myDate := DATE_ADD( #myDate, INTERVAL 1 DAY ) as DateToInclude
from
( select #myDate := Date_add(Curdate(), interval - 2 DAY) ) as SQLVars,
AnyTableWithAtLeast3RecordInIt
limit 30 ) JustDates
left join
( select
maildate,
COUNT(*) AS totalEmails
FROM
emails
WHERE
maildate >= Date_add(Curdate(), interval - 2 DAY)
AND company_id = 1
GROUP BY
DATE(maildate) ) SumCnts
ON JustDates.DateToInclude = SumCnts.MailDate
Now, judging by your query, but unclarified request... Your emails table CAN HAVE FUTURE DATES? Is that correct? Such as a Dr. Office and appointments are for the future and you want to get emails out for a given range. This is what I was inferring and hence had my limit to only go out 30 days... If you need longer, just extend the LIMIT clause.
You need a table that contains all dates in the needed range. If its only about today and yesterday, you can easily create it as a subquery (derived table).
SELECT Curdate() as maildate
UNION ALL
SELECT Curdate() - INTERVAL 1 DAY
http://rextester.com/ALH50651
Now you can LEFT JOIN your table and count the rows:
SELECT sub.maildate,
COUNT(m.maildate) AS totalEmails
FROM (
SELECT Curdate() as maildate
UNION ALL
SELECT Curdate() - INTERVAL 1 DAY
) sub
LEFT JOIN emails m
ON DATE(m.maildate) = sub.maildate
AND m.company_id = 1
GROUP BY sub.maildate
ORDER BY sub.maildate desc
Related
I have a table with attributes as timestamp, SensorName, Temperature, Humidity. What I doing is getting results by ordering then according to the timestamp in DESC order and I have condition to get it for either 1 day 7 days or for a previous month. But the issue is I am getting a Lot more values when I access old records. What I need to do is, since my data is ordered by timestamp, I need at most 15 to 20 rows/records for each date.Suppose I am getting data from June 10 to June 17, and each date have 100 records. I need only 10/20 records for the particular day (top 20 for each day).
My MySQL version is 5.7.26 and i tried using ROW_NUMBER but it is not helping
SELECT datestamp AS TIME,Temperature AS TEMPERATURE FROM table_name WHERE NodeAdd = 'SensorName' and datestamp >= DATE_ADD(CURRENT_DATE,INTERVAL -'$somevar' DAY) ORDER BY datestamp DESC
The above code shows how I am getting data for different days, The $somevar variable is hardcoded, If user select day it is -1 if he selects previous week, it is -7 and -30 for the previous month.. I need at most 20 rows/records for each day.
NOTE :: I don't have an ID column or a Unique/Primary Key in my Table, so I was trying to get particular rows using partitioning via dates only.
You need to create Row Number per group which is Date and then only keep records with row Row Number < 10 as you wants 10 records per group. The following script is just a guideline and you can fit it with your table structure to get your desired output-
SELECT * FROM
(
SELECT *,
(
CASE DateColumn
WHEN #DateColumn THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #DateColumn := DateColumn
END
) AS rank
FROM
(
SELECT *,CAST(<your_date_column> AS DATE) AS `DateColumn`
FROM Your_table
)A
,(SELECT #curRow := 0, #DateColumn := '') r
ORDER BY <your_date_column>
)A WHERE Rank < 11
In MySql...
How to exclude today date ( upto 2.30PM on each day ) data row from MYSQL fetch array..
How to publish today result + last 3 using PHP + mysql ...
Sample Table : ALLRESULTDATA
id price date
1 Rs.50 2015-12-09
2 Rs.5 2015-12-10
3 Rs.52 2015-12-11
4 Rs.55 2015-12-12
5 Rs.53 2015-12-13
6 Rs.53 2015-12-14
How to fetch last 3 days result ( Exclude today result upto 2.30PM afterthat publish TODAY)
mysql_query("select * from ALLRESULTDATA desc limit 4");
Here's a query that will include the row for today and the rows for the last three days:
SELECT *
FROM ALLRESULTDATA
WHERE
`date` = CURDATE()
OR
`date` >= DATE_ADD(CURDATE(), INTERVAL -3 DAY)
ORDER BY `date` DESC
LIMIT 4
SQL Fiddle: http://sqlfiddle.com/#!9/88561/3
I think you also mean to only include the result for today if the time is after 14:30, that's probably possible but it would be clunky in MySQL in my opinion.
I think you would be better grabbing the current time in php and if it's greater, running the query above and if not, run a different query, e.g. :
SELECT *
FROM ALLRESULTDATA
WHERE
`date` != CURDATE()
AND
`date` >= DATE_ADD(CURDATE(), INTERVAL -3 DAY)
ORDER BY `date` DESC
LIMIT 3
i.e. change the first conditional operator to != and change the OR to an AND and then also change the LIMIT to 3.
Today Excluded Fiddle: http://sqlfiddle.com/#!9/88561/5
EDIT
I just noticed your comment saying that the dates are stored as a VARCHAR(10), if possible I'd recommend changing your schema so that the dates are stored as DATE.
An alternative is the STR_TO_DATE() function, but I don't think the performance will be particularly nice if you have to resort to using it.
I have the following code and query:
//$month is an array of datetime objects
foreach($month as $key => $indMonth){
$formattedMonth[] = $month[$key]->format('Y-m-d');
}
$formattedMonths = implode("','",$formattedMonth);
$query = "SELECT id,date FROM table WHERE date in ('$formattedMonths') ORDER by date DESC";
The database holds dates for the past 450 days, but it is imperfect and there are some missing days. The point of the script is to retrieve data from the current day of the month and then the corresponding day on the five previous months, but I need a failsafe for when a date happens to be missing.
How can I modify this query so it picks either the date in the "where in" portion of the query or it finds the date nearest to that particular date in the array?
Is this best to do in the query, or am I better off returning a more complete data set, then using PHP to find out if the date I want is available?
MySQL offers some decent date arithmetic. For example, if you have the date '2015-11-10' (10-Nov-2015) you can get the same day three months prior with this expression:
'2015-11-10` - INTERVAL 3 MONTH
That will kick back '2015-08-10', which is what you want.
This date arithmetic works predictably even with longer and shorter months, and with leap years. For example,
'2015-03-31' - INTERVAL 1 MONTH, '2016-03-31' - INTERVAL 1 MONTH
gives back '2015-02-28', '2016-02-29' as you might expect. And
'2015-03-31' - INTERVAL 2 MONTH, '2016-03-31' - INTERVAL 2 MONTH
gives back '2015-03-31', '2016-03-31'. Perfect.
Now, only you can decide whether this predictable behavior is correct for your application: only you know what you want to do with the previous five months of data, when the day in question is near the end of the month.
Let's assume it's correct and move on. Here is a subquery that can be used to generate a sequence of six dates, one day per month ending today.
SELECT CURDATE() - INTERVAL seq.seq MONTH day_of_month
FROM ( SELECT 0 AS seq UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL
SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 6) seq
We can use this little query as a subquery, and LEFT JOIN it to your data. That would work like this:
SELECT id, day_of_month
FROM (
SELECT CURDATE() - INTERVAL seq.seq MONTH day_of_month
FROM ( SELECT 0 AS seq UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL
SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 6) seq
) days
LEFT JOIN table ON table.date = days.day_of_month
This is a cool way to do it because you'll always get at least one row in the resultset for each date in the list, even if there's nothing matching in table.
The closest date gets a little hairier. It's possible to write a query like that. But MySQL lacks a WITH clause so the query is ridiculously repetitive.
There are referer records in my database. All records include date,clicks, scrolls, countries etc.
And in here, I successfully group referer records and include average values of clicks, scrolls, countries etc. However, I also want to enable date range, like last 3 days.
SELECT *, COUNT(*), COUNT(conv), AVG(clicks), AVG(scrolls),
AVG(spent) FROM track where referid='".$memberr."' GROUP BY
referer ORDER BY ".$sortby." desc limit 0,35
How can I integrate this last 3 days query with my mysql_query above ?
FROM_UNIXTIME(date,'%Y-%m-%d') > CURDATE() - INTERVAL 3 DAY
Pretty simple thing to do, you just need to add another condition:
WHERE date >= DATE_SUB(CURDATE(), INTERVAL 3 DAY) AND date <= CURDATE()
I have a system that allows users to assign a specific file to a past or present date. The limitations are that they may only upload one file per day per user. When the user goes to upload a file the date field must default to the current date and when that date is not available it will show the first available date in the past in DESC order. Below is the relevent field names.
file_id (INT - INDEX - AUTO INCREMENT)
user_id (INT - may index this)
upload_date (INT - stores date as a unix timestamp)
The only solution I have really found would be to build them all into an array in DESC order by date and loop through until i found an empty slot. However, I feel this could really cause speed issues if the user had the past thousand days filled. I feel like I am overlooking a simple solution.
PLEASE NOTE: For one reason or another they Date is being stored as a Unix timestamp which I understand the downsides on and I am not concerned about correcting at this time.
To get the most recent date that has not been used:
select user_id, max(date) - 1
from (select ud.*,
(select max(date) from upload_date ud2 where ud2.user_id = ud.user_id and ud2.date < ud.date
) as prevdate
from upload_date ud
) ud
where date(from_unixtime(ud.prevdate)) <> date(from_unixtime(ud.date)) - 1 or
ud.prevdate is null
group by user_id
This query first gets the previous date for any given day using a correlated subquery. It then converts the time values to dates and selects any row where the previous date has a gap. The largest of the date minus one is the date you are looking for.
This SQL is untested, so it may have syntax errors.
One way to approach this is with a classic "return missing rows" query. Basically, to get a "missing" row returned from the database, you need a way to generate the "missing" rows.
To build such a query, we can start with:
SELECT MAX(t.upload_date)
FROM mytable t
WHERE t.upload_date <= NOW()
AND t.user = 'someuser'
That gets the initial date, that we are going to work backwards from.
For the "one per day" requirement, you probably want to truncate that upload_date to midnight, at least for this query. For now, we'll assume that the expression in the SELECT list is already truncated, to illustrate the approach, without bogging down in the details of dealing with a unix timestamp.
To generate a descending list of dates, starting with that initial date retrieved by the previous query...
SELECT s.upload_date - INTERVAL n.d DAY AS available_date
FROM ( SELECT MAX(t.upload_date) AS upload_date
FROM mytable t
WHERE t.upload_date <= NOW()
AND t.user = 'someuser'
) s
CROSS
JOIN ( SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) n
ORDER BY n.d DESC
With that result, we can use an anti-join pattern to find which dates are not already used. This is a LEFT JOIN and a predicate that throws out matching rows:
SELECT s.upload_date - INTERVAL n.d DAY AS available_date
FROM ( SELECT MAX(t.upload_date) AS upload_date
FROM mytable t
WHERE t.upload_date <= NOW()
AND t.user = 'someuser'
) s
CROSS
JOIN ( SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) n
LEFT
JOIN mytable u
ON u.upload_date = s.upload_date - INTERVAL n.d DAY
AND u.user = 'someuser'
WHERE u.upload_date IS NULL
ORDER BY n.d DESC
LIMIT 1
That only looks back 9 days, to get it to look back more days, just extend the inline view aliased as n to return more consecutive integers. (There's some tricks we can play with cross joins to get a whole boatload of integers.)
All that remains is working on the "matching" criteria (which works with the MySQL DATE datatype):
ON u.upload_date = s.upload_date - INTERVAL n.d DAY
into something like this:
ON u.upload_date >= UNIX_TIMESTAMP(FROM_UNIXTIME(s.upload_date)-INTERVAL n.d+1 DAY)
AND u.upload_date < UNIX_TIMESTAMP(FROM_UNIXTIME(s.upload_date)-INTERVAL n.d DAY)
And futzing with the integer timestamp value to get a MySQL DATE out of it...
SELECT DATE(FROM_UNIXTIME(s.upload_date)) - INTERVAL n.d DAY AS available_date