PostgreSQL's date_trunc in mySQL
Recently, I have been getting familiar with PostgreSQL(using 8.2) and found the date_trunc function extremely useful for easily matching time stamps between certain days/months/etc. The real usefulness of the function, I believe, comes from the fact that it keeps the output in the format of a timestamp.
I have had to switch to mySQL(5.0) and find some of the date functions rather lacking in comparison. The extract function seems useful and the date function I have found solves some of my problems, but is there any way to replicate PostgreSQL's date_trunc?
Following is an example of how I used to use date_trunc to match queried timestamps to only the last 4 months including the current month, but only if a week has passed into this month already:
WHERE date_trunc('month', QUERY_DATE) BETWEEN
date_trunc('month', now()) - INTERVAL '4 MONTH' AND
date_trunc('month', now() - INTERVAL '1 WEEK')
I have no idea how to recreate such a stipulati开发者_如何学JAVAon in mySQL. So, my question at the end of the day, is whether this type of query can be accomplished in mySQL by trying replicate date_trunc(and how) or whether I need to start looking at these types of queries in a different way to make them work in mySQL(and suggestions on how to do that)?
The extract function seems useful and the date function I have found solves some of my problems, but is there any way to replicate PostgreSQL's date_trunc?
Indeed, EXTRACT
looks like it's going to be the closest match for this specific case.
Your original code in PG:
WHERE date_trunc('month', QUERY_DATE) BETWEEN
date_trunc('month', now()) - INTERVAL '4 MONTH' AND
date_trunc('month', now() - INTERVAL '1 WEEK')
Using EXTRACT
:
WHERE EXTRACT(YEAR_MONTH FROM QUERY_DATE)
BETWEEN
EXTRACT(YEAR_MONTH FROM NOW() - INTERVAL 4 MONTH)
AND
EXTRACT(YEAR_MONTH FROM NOW() - INTERVAL 1 WEEK)
While it should be functionally identical, this is actually mangling the dates into a YYYYMM string before doing the comparison.
Another option would be using DATE_FORMAT
to rebuild the date string and force it to the beginning of the month:
WHERE DATE_FORMAT(QUERY_DATE, '%Y-%m-01')
BETWEEN
DATE_FORMAT(NOW() - INTERVAL 4 MONTH, '%Y-%m-01')
AND
DATE_FORMAT(NOW() - INTERVAL 1 WEEK, '%Y-%m-01')
Also, be aware that MySQL is really poor at dealing with date ranges, even when the field is indexed. You're probably going to end up with a full table scan if you aren't careful.
late to the party, but...
there is a way to get truncated date given you know the interval. For example, if the interval is MONTH
, you could get today's date (now()
) truncated to the month using the following:
select date_add('1900-01-01', interval TIMESTAMPDIFF(MONTH, '1900-01-01', now()) MONTH);
Given the above, one could create a function to take care of the other intervals as well:
DELIMITER //
create function date_trunc(vInterval varchar(7), vDate timestamp)
returns timestamp
begin
declare toReturn timestamp;
if vInterval = 'year' then set toReturn = date_add('1900-01-01', interval TIMESTAMPDIFF(YEAR, '1900-01-01', vDate) YEAR);
elseif vInterval = 'quarter' then set toReturn = date_add('1900-01-01', interval TIMESTAMPDIFF(QUARTER, '1900-01-01', vDate) QUARTER);
elseif vInterval = 'month' then set toReturn = date_add('1900-01-01', interval TIMESTAMPDIFF(MONTH, '1900-01-01', vDate) MONTH);
elseif vInterval = 'week' then set toReturn = date_add('1900-01-01', interval TIMESTAMPDIFF(WEEK, '1900-01-01', vDate) WEEK);
elseif vInterval = 'day' then set toReturn = date_add('1900-01-01', interval TIMESTAMPDIFF(DAY, '1900-01-01', vDate) DAY);
elseif vInterval = 'hour' then set toReturn = date_add('1900-01-01', interval TIMESTAMPDIFF(HOUR, '1900-01-01', vDate) HOUR);
elseif vInterval = 'minute' then set toReturn = date_add('1900-01-01', interval TIMESTAMPDIFF(MINUTE, '1900-01-01', vDate) MINUTE);
END IF;
return toReturn;
end//
DELIMITER ;
Use it like so:
select date_trunc('quarter', now())
Here is a function that mimics postgres' DATE_TRUNC
contract using the DATE_FORMAT
mysql function that @Charles has recommended above.
DROP FUNCTION IF EXISTS DATE_TRUNC;
CREATE FUNCTION DATE_TRUNC(
in_granularity ENUM('hour', 'day', 'month', 'year'),
in_datetime datetime(6)
)
RETURNS datetime(6)
DETERMINISTIC
BEGIN
IF (in_granularity = 'hour') THEN
RETURN DATE_FORMAT(in_datetime, '%Y-%m-%d %H:00:00.0000');
END IF;
IF (in_granularity = 'day') THEN
RETURN DATE_FORMAT(in_datetime, '%Y-%m-%d 00:00:00.0000');
END IF;
IF (in_granularity = 'month') THEN
RETURN DATE_FORMAT(in_datetime, '%Y-%m-01 00:00:00.0000');
END IF;
IF (in_granularity = 'year') THEN
RETURN DATE_FORMAT(in_datetime, '%Y-01-01 00:00:00.0000');
END IF;
END;
Here are some MySQL equivalents for the most common Redshift/Postgres date_trunc expressions, using date arithmetic:
select
null
-- RS date_trunc('hour', now()) = '2023-01-13 17:00:00.000 +0100'
,date_add(date(now()), interval hour(now()) hour) as trunc_hour
-- RS date_trunc('week', now()) = '2023-01-09 00:00:00.000 +0100'
,date_sub(cast(date(now()) as datetime), interval weekday(now()) day) as trunc_week
-- RS date_trunc('month', now()) = '2023-01-01 00:00:00.000 +0100'
,date_add(cast(makedate(year(now()), 1) as datetime), interval month(now())-1 month) as trunc_month
-- RS date_trunc('year', now()) = '2023-01-01 00:00:00.000 +0100'
,cast(makedate(year(now()), 1) as datetime) as trunc_year
精彩评论