I need to select all data from a table 'followup' between followup_date and first of next month (including 1rst of next month). The format of my date in DB (obtained from an API) is d-m-Y.
The follow-up date for example is: 18-07-2020.
I have the following query:
SELECT * from followup
WHERE DATEDIFF(STR_TO_DATE(`followup_date`,'%d-%m-%Y'), DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH,'%Y-%m-01'))- 1 < 0 ;
I am getting -15 days as difference and getting records correctly, including 1rst. Is the query correct and efficient and will it work correctly for all months.
Requesting suggestions from experts for improvements, if any.
You should convert your followup_date column to a DATE type. You can then make your query sargable by removing the function calls on followup_date and simply comparing it with the target date:
SELECT * from followup
WHERE `followup_date` <= DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH,'%Y-%m-01')
I suspect adding one day to LAST_DAY(CURDATE()) might be more efficient:
SELECT * from followup
WHERE `followup_date` <= LAST_DAY(CURDATE()) + INTERVAL 1 DAY
Demo on SQLFiddle
I'm trying to work out how to create a solution that will allow me to query a table that has a timestamp, and in return get a time series data. The request consists of start/end date & time, granularity type (minute, hour, day, week, month and year) and granularity value. Having tried to use in a query something like
GROUP BY ROUND(UNIX_TIMESTAMP(created_at) DIV 60)
to get the results per one minute, or DIV 300 for every five minutes is fine. The problem lies further up for calculating months and years' seconds which will be inaccurate. I've stumbled upon the generate_series in PGSQL (MySQL alternative) and am stuck trying to tie them together. How do I calculate a count of rows, for example, for two days, on a 15 minute granularity? It's a complex question that I'll probably have to break down further.
I have already visited #1 and #2, but they are incomplete.
To me it seems that rounding will only be allowed to certain level and I'd have to restrict it (i.e .for 2 months period there cannot be hourly breakdown).
EDIT
It gave me the wrong impression - I would not have to calculate monthly figures based on seconds using the query like:
SELECT DATE_FORMAT(MIN(created_at),'%d/%m/%Y %H:%i:%s' as date,
COUNT(*) AS count FROM guests
GROUP BY ROUND(UNIX_TIMESTAMP(created_at) / 300)
It's only going to do grouping based on minimum value. But the question still stands - is the best approach really to go through the time period using granularity value and "slice" the data that way without loosing too much accuracy?
It seems that the only approach is to run sub-queries for a set of data (i.e. for a period of two months, generate 15 minute intervals timestamps, group the data into them and produce an aggregate) without dividing the original timestamp to produce the rounded approximation.
Let's say you have a gigantic table measure with two columns datestamp and temp.
Let's say you want to see the temperature every six minutes (10x per hour) for the last week. You can do this sort of thing. We'll get to defining trunc in a moment.
SELECT trunc(datestamp) datestamp, AVG(temp) temp
FROM measure
WHERE datestamp >= CURDATE() - INVERVAL 7 DAY
GROUP BY trunc(datestamp)
ORDER BY trunc(datestamp)
That works for any reasonable definition of trunc. In this case trunc(t) returns the beginning of the six-minute period in which t occurs. So, trunc('1942-12-07 08:45:17') gives 1942-12-07 08:42:00).
Here's a query that works for every six minute interval.
SELECT DATE_FORMAT(datestamp,'%Y-%m-%d %H:00') +
INTERVAL (MINUTE(datestamp) -
MINUTE(datestamp) MOD 6) datestamp,
AVG(temp) temp
FROM measure
WHERE datestamp >= CURDATE() - INVERVAL 7 DAY
GROUP BY DATE_FORMAT(datestamp,'%Y-%m-%d %H:00') +
INTERVAL (MINUTE(datestamp) -
MINUTE(datestamp) MOD 6)
ORDER BY 1
This uses inbuilt date arithmetic rather than unix timestamp arithmetic.
You can use a stored function to make this easier to read.
DELIMITER $$
DROP FUNCTION IF EXISTS TRUNC_N_MINUTES$$
CREATE
FUNCTION TRUNC_N_MINUTES(datestamp DATETIME, n INT)
RETURNS DATETIME DETERMINISTIC NO SQL
COMMENT 'truncate to N minute boundary. For example,
TRUNCATE_N_MINUTES(sometime, 15) gives the nearest
preceding quarter hour'
RETURN DATE_FORMAT(datestamp,'%Y-%m-%d %H:00') +
INTERVAL (MINUTE(datestamp) -
MINUTE(datestamp) MOD n) MINUTE$$
DELIMITER ;
Then your query will say
SELECT TRUNC_N_MINUTES(datestamp, 6) datestamp, AVG(temp) temp
FROM measure
WHERE datestamp >= CURDATE() - INVERVAL 7 DAY
GROUP BY TRUNC_N_MINUTES(datestamp, 6)
ORDER BY TRUNC_N_MINUTES(datestamp, 6)
If you want to summarize by 5, 10, 15, or minute boundaries (three items per hour) simply use that number in place of 6.
You'll need different trunc() functions for hours, etc.
The trunc() function for daily summaries is DATE(datestamp).
For monthly summaries it is LAST_DAY(datestamp). For example,
SELECT LAST_DAY(datestamp) month_ending, AVG(temp) temp
FROM measure
GROUP BY LAST_DAY(datestamp)
ORDER BY LAST_DAY(datestamp)
yields a month-by-month summary.
I'm trying to subtract now() - the created datetime field from 30 days to get the days remaining as a datetime field, mysql gives me an error for this sort of thing.
SELECT id, created, INTERVAL 30 DAY - CURRENT_DATE - created as timeleft FROM tablename
Use the Date add function to subtract 30 days like this.
SELECT DATE_ADD(current_date, interval -30 day);
Return the difference in days between two date values:
SELECT DATEDIFF(current_date, "2017-06-15");
You can use the combination of these functions to achieve the desired result.
In mysql you should translate the datetime to unix_timestamp to calculate the difference between the two days
SELECT id, created, 30 - (unix_timestamp(CURRENT_DATE) - unix_timestamp(created )) / 3600/24 as timeleft FROM tablename
I have a requirement of counting the no. of records inserted into a table for every half an hour.say from 11 to 11 30 if there 5 records and 11 30 to 12 if there are 4 records how to find the no. of records
You'd need the datetime each row was inserted; it's easiest if that is a column in the table. (We'll assume here that the column is named inserted_dt.)
All that we really need is an expression that operates on inserted_dt to return a single value for every value within a given half hour.
If we needed "hour" intervals, and not "half-hour" intervals, it would be very easy:
DATE_FORMAT(t.inserted_dt,'%Y-%m-%d %H:00:00')
Let's define the first "half-hour" ranges as minutes >= '00' AND minutes < '30'
To get the "minutes" out of the inserted_dt column, we could use either of
EXTRACT(MINUTE FROM t.inserted_dt)
DATE_FORMAT(t.inserted_dt,'%i')
We can use a conditional test to determine whether the minutes value is less than 30, or flip it around and test for greater than or equal to thirty:
DATE_FORMAT(t.inserted_dt,'%i')+0 >= 30
We can put that back together with the "year-month-day-hour", by adding an interval of either 0 or 30 minutes,
DATE_FORMAT(t.inserted_dt,'%Y-%m-%d %H:00:00')
+ INTERVAL 30*(DATE_FORMAT(t.inserted_dt,'%i')+0>=30) MINUTE
(There are lots of expressions we could use to do something similar; this one is just one of the shortest we can use to return a DATETIME datatype
Now, we just add the expression to the SELECT list of our query, we get a value that identifies the "halfhour".
To get a "count" for each half hour range, that's just a simple COUNT() aggregate and a GROUP BY. The "trick" is that we use the new "halfhour" expression in the GROUP BY clause.
SELECT DATE_FORMAT(t.inserted_dt,'%Y-%m-%d %H:00:00')
+ INTERVAL 30*(DATE_FORMAT(t.inserted_dt,'%i')+0>=30) MINUTE AS halfhour
, COUNT(*)
FROM mytable t
GROUP BY halfhour
Obviously, add a WHERE clause if you only want to return results for a specified datetime range,
SELECT DATE_FORMAT(t.inserted_dt,'%Y-%m-%d %H:00:00')
+ INTERVAL 30*(DATE_FORMAT(t.inserted_dt,'%i')+0>=30) MINUTE AS halfhour
, COUNT(*)
FROM mytable t
WHERE t.inserted_dt >= '2014-08-12'
AND t.inserted_dt < '2014-08-12' + INTERVAL 1 DAY
GROUP BY halfhour
I want to get first day of every corresponding month of current year. For example, if user selects '2010-06-15', query demands to run from '2010-06-01' instead of '2010-06-15'.
Please help me how to calculate first day from selected date. Currently, I am trying to get desirable using following mysql select query:
Select
DAYOFMONTH(hrm_attendanceregister.Date) >=
DAYOFMONTH(
DATE_SUB('2010-07-17', INTERVAL - DAYOFMONTH('2010-07-17') + 1 DAY
)
FROM
hrm_attendanceregister;
Thanks
Is this what you are looking for:
select CAST(DATE_FORMAT(NOW() ,'%Y-%m-01') as DATE);
You can use the LAST_DAY function provided by MySQL to retrieve the last day of any month, that's easy:
SELECT LAST_DAY('2010-06-15');
Will return:
2010-06-30
Unfortunately, MySQL does not provide any FIRST_DAY function to retrieve the first day of a month (not sure why). But given the last day, you can add a day and subtract a month to get the first day. Thus you can define a custom function:
DELIMITER ;;
CREATE FUNCTION FIRST_DAY(day DATE)
RETURNS DATE DETERMINISTIC
BEGIN
RETURN ADDDATE(LAST_DAY(SUBDATE(day, INTERVAL 1 MONTH)), 1);
END;;
DELIMITER ;
That way:
SELECT FIRST_DAY('2010-06-15');
Will return:
2010-06-01
There is actually a straightforward solution since the first day of the month is simply today - (day_of_month_in_today - 1):
select now() - interval (day(now())-1) day
Contrast that with the other methods which are extremely roundabout and indirect.
Also, since we are not interested in the time component, curdate() is a better (and faster) function than now(). We can also take advantage of subdate()'s 2-arity overload since that is more performant than using interval. So a better solution is:
select subdate(curdate(), (day(curdate())-1))
This is old but this might be helpful for new human web crawlers XD
For the first day of the current month you can use:
SELECT LAST_DAY(NOW() - INTERVAL 1 MONTH) + INTERVAL 1 DAY;
You can use EXTRACT to get the date parts you want:
EXTRACT( YEAR_MONTH FROM DATE('2011-09-28') )
-- 201109
This works well for grouping.
You can use DATE_FORMAT() function in order to get the first day of any date field.
SELECT DATE_FORMAT(CURDATE(),'%Y-%m-01') as FIRST_DAY_CURRENT_MONTH
FROM dual;
Change Curdate() with any other Date field like:
SELECT DATE_FORMAT(purchase_date,'%Y-%m-01') AS FIRST_DAY_SALES_MONTH
FROM Company.Sales;
Then, using your own question:
SELECT *
FROM
hrm_attendanceregister
WHERE
hrm_attendanceregister.Date) >=
DATE_FORMAT(CURDATE(),'%Y-%m-01')
You can change CURDATE() with any other given date.
There are many ways to calculate the first day of a month, and the following are the performance in my computer (you may try this on your own computer)
And the winner is LAST_DAY(#D - interval 1 month) + interval 1 day
set #D=curdate();
select BENCHMARK(100000000, subdate(#D, (day(#D)-1))); -- 33 seconds
SELECT BENCHMARK(100000000, #D - INTERVAL (day(#D) - 1) DAY); -- 33 seconds
SELECT BENCHMARK(100000000, cast(DATE_FORMAT(#D, '%Y-%m-01') as date)); -- 29 seconds
SELECT BENCHMARK(100000000, LAST_DAY(#D - interval 1 month) + interval 1 day); -- 26 seconds
I'm surprised no one has proposed something akin to this (I do not know how performant it is):
CONCAT_WS('-', YEAR(CURDATE()), MONTH(CURDATE()), '1')
Additional date operations could be performed to remove formatting, if necessary
use date_format method and check just month & year
select * from table_name where date_format(date_column, "%Y-%m")="2010-06"
SELECT LAST_DAY(date) as last_date, DATE_FORMAT(date,'%Y-%m-01') AS fisrt_date FROM table_name
date=your column name
The solutions that use last_day() and then add/subtract a month and a day are not interchangeable.
Example:
date_sub(date_add(last_day(curdate()), interval 1 day), interval 3 month)
always works for any supplied number of months you want to go back
date_add(date_sub(last_day(now()), interval 3 month), interval 1 day)
will fail in some cases, for instance if your current month has 30 days and the month you're subtracting back to (and then adding a day) has 31.
date_add(subdate(curdate(), interval day(?) day), interval 1 day)
change the ? for the corresponding date
This works fine for me.
date(SUBDATE("Added Time", INTERVAL (day("Added Time") -1) day))
** replace "Added Time" with column name
Use Cases:
If you want to reset all date fields except Month and Year.
If you want to retain the column format as "date". (not as "text" or "number")
Slow (17s):
SELECT BENCHMARK(100000000, current_date - INTERVAL (day(current_date) - 1) DAY);
SELECT BENCHMARK(100000000, cast(DATE_FORMAT(current_date, '%Y-%m-01') as date));
If you don't need a date type this is faster:
Fast (6s):
SELECT BENCHMARK(100000000, DATE_FORMAT(CURDATE(), '%Y-%m-01'));
SELECT BENCHMARK(100000000, DATE_FORMAT(current_date, '%Y-%m-01'));
select big.* from
(select #date := '2010-06-15')var
straight_join
(select * from your_table where date_column >= concat(year(#date),'-',month(#date),'-01'))big;
This will not create a full table scan.