mySQL 中 PostgreSQL 的 date_trunc

发布于 2024-10-29 06:14:54 字数 625 浏览 1 评论 0原文

最近,我开始熟悉 PostgreSQL(使用 8.2),发现 date_trunc 函数对于轻松匹配某些天/月/等之间的时间戳非常有用。 我相信该函数的真正有用之处在于它以时间戳的格式保存输出。

我不得不切换到 mySQL(5.0) 并发现一些日期函数相当缺乏比较。 extract 函数似乎很有用,而且我发现的 date 函数解决了我的一些问题,但是有什么方法可以复制 PostgreSQL 的 date_trunc 吗?

以下是我如何使用 date_trunc 将查询的时间戳仅与最近 4 个月(包括当月)匹配的示例,但前提是本月已经过去了一周:

WHERE date_trunc('month', QUERY_DATE) BETWEEN 
    date_trunc('month', now()) - INTERVAL '4 MONTH' AND 
    date_trunc('month', now() - INTERVAL '1 WEEK')

我不知道如何在 mySQL 中重新创建这样的规定。所以,我最终的问题是,这种类型的查询是否可以通过尝试复制 date_trunc(以及如何)在 mySQL 中完成,或者我是否需要开始以不同的方式查看这些类型的查询以使它们工作在 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 stipulation 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)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

私野 2024-11-05 06:14:54

extract 函数看起来很有用,而且我发现的 date 函数解决了我的一些问题,但是有没有办法复制 PostgreSQL 的 date_trunc?

事实上,EXTRACT 看起来这将是该特定案例最接近的匹配。

您在 PG 中的原始代码:

WHERE date_trunc('month', QUERY_DATE) BETWEEN 
    date_trunc('month', now()) - INTERVAL '4 MONTH' AND 
    date_trunc('month', now() - INTERVAL '1 WEEK')

使用 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)

虽然它在功能上应该是相同的,但这实际上是在进行比较之前将日期转换为 YYYYMM 字符串。

另一种选择是使用 DATE_FORMAT 重建日期字符串并强制其为月初:

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')

另外,请注意,MySQL 在处理日期范围方面确实很差,即使该字段已建立索引。如果不小心,您可能会进行全表扫描。

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.

旧情勿念 2024-11-05 06:14:54

聚会迟到了,但是...

如果您知道时间间隔,有一种方法可以截断日期。例如,如果间隔为 MONTH,您可以使用以下命令将今天的日期 (now()) 截断为月份:

select date_add('1900-01-01', interval TIMESTAMPDIFF(MONTH, '1900-01-01', now()) MONTH);

根据上述内容,可以创建一个函数也照顾其他间隔:

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 ;

像这样使用它:

select date_trunc('quarter', now())

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())
你是我的挚爱i 2024-11-05 06:14:54

以下是使用日期算术的最常见 Redshift/Postgres date_trunc 表达式的一些 MySQL 等效项:

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

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
水溶 2024-11-05 06:14:54

这是一个使用 @Charles 上面推荐的 DATE_FORMAT mysql 函数模仿 postgres 的 DATE_TRUNC 合约的函数。

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 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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文