在 SQL 中将 DateTime 转换为“n Hours Ago”的最佳方法

Posted

技术标签:

【中文标题】在 SQL 中将 DateTime 转换为“n Hours Ago”的最佳方法【英文标题】:Best way to convert DateTime to "n Hours Ago" in SQL 【发布时间】:2010-09-08 04:29:28 【问题描述】:

我编写了一个 SQL 函数来将 SQL 中的日期时间值转换为更友好的“n Hours Ago”或“n Days Ago”等类型的消息。我想知道是否有更好的方法来做到这一点。

(是的,我知道“不要在 SQL 中这样做”,但出于设计原因,我必须这样做)。

这是我写的函数:

CREATE FUNCTION dbo.GetFriendlyDateTimeValue
(
    @CompareDate DateTime
)
RETURNS nvarchar(48)
AS
BEGIN
    DECLARE @Now DateTime
    DECLARE @Hours int
        DECLARE @Suff nvarchar(256)
    DECLARE @Found bit

    SET @Found = 0
    SET @Now = getDate()
    SET @Hours = DATEDIFF(MI, @CompareDate, @Now)/60

    IF @Hours <= 1
    BEGIN
        SET @Suff = 'Just Now'
        SET @Found = 1
        RETURN @Suff
    END

    IF @Hours < 24
    BEGIN
        SET @Suff = ' Hours Ago'
        SET @Found = 1
    END

    IF @Hours >= 8760 AND @Found = 0
    BEGIN
        SET @Hours = @Hours / 8760
        SET @Suff = ' Years Ago'
        SET @Found = 1
    END

    IF @Hours >= 720 AND @Found = 0
    BEGIN
        SET @Hours = @Hours / 720
        SET @Suff = ' Months Ago'
        SET @Found = 1
    END

    IF @Hours >= 168 AND @Found = 0
    BEGIN
        SET @Hours = @Hours / 168
        SET @Suff = ' Weeks Ago'
        SET @Found = 1
    END

    IF @Hours >= 24 AND @Found = 0
    BEGIN
        SET @Hours = @Hours / 24
        SET @Suff = ' Days Ago'
        SET @Found = 1
    END

    RETURN Convert(nvarchar, @Hours) + @Suff
END

【问题讨论】:

【参考方案1】:
CASE WHEN datediff(SECOND,OM.OrderDate,GETDATE()) < 60 THEN
  CONVERT(NVARCHAR(MAX),datediff(SECOND,OM.OrderDate,GETDATE())) +' seconds ago'
WHEN datediff(MINUTE,OM.OrderDate,GETDATE()) < 60 THEN
  CONVERT(NVARCHAR(MAX),datediff(MINUTE,OM.OrderDate,GETDATE())) +' minutes ago'
WHEN datediff(HOUR,OM.OrderDate,GETDATE()) < 24 THEN
  CONVERT(NVARCHAR(MAX),datediff(HOUR,OM.OrderDate,GETDATE())) +' hours ago'
WHEN datediff(DAY,OM.OrderDate,GETDATE()) < 8 THEN
  CONVERT(NVARCHAR(MAX),datediff(DAY,OM.OrderDate,GETDATE())) +' Days ago'
ELSE FORMAT(OM.OrderDate,'dd/MM/yyyy hh:mm tt') END AS TimeStamp

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案2】:

上面的帖子给了我一些好主意,所以这里有另一个功能供任何使用 SQL Server 2012 的人使用。

    CREATE FUNCTION [dbo].[FN_TIME_ELAPSED] 
    (
        @TIMESTAMP DATETIME
    )
    RETURNS VARCHAR(50)
    AS
    BEGIN

    RETURN 
    (
        SELECT TIME_ELAPSED = 
            CASE
                WHEN @TIMESTAMP IS NULL THEN NULL
                WHEN MINUTES_AGO < 60 THEN CONCAT(MINUTES_AGO, ' minutes ago')
                WHEN HOURS_AGO < 24 THEN CONCAT(HOURS_AGO, ' hours ago')
                WHEN DAYS_AGO < 365 THEN CONCAT(DAYS_AGO, ' days ago')
                ELSE CONCAT(YEARS_AGO, ' years ago') END
        FROM ( SELECT MINUTES_AGO = DATEDIFF(MINUTE, @TIMESTAMP, GETDATE()) ) TIMESPAN_MIN
        CROSS APPLY ( SELECT HOURS_AGO = DATEDIFF(HOUR, @TIMESTAMP, GETDATE()) ) TIMESPAN_HOUR
        CROSS APPLY ( SELECT DAYS_AGO = DATEDIFF(DAY, @TIMESTAMP, GETDATE()) ) TIMESPAN_DAY
        CROSS APPLY ( SELECT YEARS_AGO = DATEDIFF(YEAR, @TIMESTAMP, GETDATE()) ) TIMESPAN_YEAR
    )
    END
    GO

以及实现:

    SELECT TIME_ELAPSED = DBO.FN_TIME_ELAPSED(AUDIT_TIMESTAMP)
    FROM SOME_AUDIT_TABLE

【讨论】:

【参考方案3】:

感谢上面发布的各种代码。

正如 Hafthor 指出的,原始代码在舍入方面存在局限性。我还发现他的代码踢出的一些结果与我的预期不符,例如周五下午 -> 周一早上将显示为“2 天前”。我想我们都会在 3 天前调用它,即使 3 个完整的 24 小时周期还没有过去。

所以我修改了代码(这是 MS SQL)。免责声明:我是一名新手 TSQL 编码器,所以这很 hacky,但有效!!

我已经做了一些覆盖 - 例如最多 2 周的任何时间都以天数表示。超过 2 个月的任何时间都以周表示。超过这个时间的任何事情都需要几个月等。这似乎是一种直观的表达方式。

CREATE FUNCTION [dbo].[GetFriendlyDateTimeValue]( @CompareDate DATETIME ) RETURNS NVARCHAR(48) AS BEGIN
declare @s nvarchar(48)

set @s='Now'
select top 1 @s=convert(nvarchar,abs(n))+' '+s+case when abs(n)>1 then 's' else '' end+case when n>0 then ' ago' else ' from now' end from (
 select convert(int,(convert(float,(getdate()-@comparedate))*n)) as n, s from (
  select 1/365 as n, 'year' as s union all
  select 1/30, 'month' union all
  select 1/7, 'week' union all
  select 1, 'day' union all
  select 24, 'hour' union all
  select 24*60, 'minute' union all
  select 24*60*60, 'second'
 ) k
) j where abs(n)>0 order by abs(n)

if @s like '%days%'
BEGIN
 -- if over 2 months ago then express in months
 IF convert(nvarchar,DATEDIFF(MM, @CompareDate, GETDATE())) >= 2
 BEGIN
  select @s = convert(nvarchar,DATEDIFF(MM, @CompareDate, GETDATE())) + ' months ago'
 END

 -- if over 2 weeks ago then express in weeks, otherwise express as days
 ELSE IF convert(nvarchar,DATEDIFF(DD, @CompareDate, GETDATE())) >= 14
 BEGIN
  select @s = convert(nvarchar,DATEDIFF(WK, @CompareDate, GETDATE())) + ' weeks ago'
 END

 ELSE
  select @s = convert(nvarchar,DATEDIFF(DD, @CompareDate, GETDATE())) + ' days ago'
END

return @s
END

【讨论】:

【参考方案4】:

我的尝试 - 这是针对 MS SQL 的。它支持 'ago' 和 'from now' 复数形式,它不使用舍入或 datediff,但截断 - datediff 在 8/30 和 9/1 之间给出 1 个月的差异,这可能不是你想要的。四舍五入给出了 9/1 和 9/16 之间的 1 个月差异。同样,可能不是您想要的。

CREATE FUNCTION dbo.GetFriendlyDateTimeValue( @CompareDate DATETIME ) RETURNS NVARCHAR(48) AS BEGIN
declare @s nvarchar(48)
set @s='Now'
select top 1 @s=convert(nvarchar,abs(n))+' '+s+case when abs(n)>1 then 's' else '' end+case when n>0 then ' ago' else ' from now' end from (
 select convert(int,(convert(float,(getdate()-@comparedate))*n)) as n, s from (
  select 1/365 as n, 'Year' as s union all
  select 1/30, 'Month' union all
  select 1, 'Day' union all
  select 7, 'Week' union all
  select 24, 'Hour' union all
  select 24*60, 'Minute' union all
  select 24*60*60, 'Second'
 ) k
) j where abs(n)>0 order by abs(n)
return @s
END

【讨论】:

你试过了吗?我不相信你可以从 t-sql 中的函数调用 'getDate()' 我试过了。刚刚在 SQL 2005、2008 和 2008R2 CTP 上再次尝试。作品。 GETDATE 使函数具有不确定性,这稍微限制了它的使用。来自 SQL2000 BOL:“在 Microsoft SQL Server 2000 中,不能在两种类型的 Transact-SQL 表达式中指定非确定性函数:* 如果 computed_column_expression 引用任何非确定性函数,则无法在计算列上创建索引。* 无法创建聚集索引如果视图引用任何非确定性函数,则在视图上。" 很确定这在 SQL 2000 上不起作用,因为非确定性 UDF 在 2005 年是新的,但即使是这样,也有一些破解方法。最坏的情况是,您可以修改 UDF 以添加第二个日期时间参数,前提是您将传入 GETDATE()。【参考方案5】:

这个怎么样?您可以将此模式扩展为发送“年”消息,并且可以检查“1 天”或“1 小时”,这样它就不会显示“1 天前”...

我喜欢 SQL 中的 CASE 语句。

drop function dbo.time_diff_message    
GO

create function dbo.time_diff_message (
    @input_date datetime
)
returns varchar(200)    
as    
begin    
declare @msg varchar(200)    
declare @hourdiff int

set @hourdiff = datediff(hour, @input_date, getdate())    
set @msg = case when @hourdiff < 0 then ' from now' else ' ago' end    
set @hourdiff = abs(@hourdiff)    
set @msg = case when @hourdiff > 24 then convert(varchar, @hourdiff/24) + ' days' + @msg
                else convert(varchar, @hourdiff) + ' hours' + @msg
            end

return @msg
end

GO    
select dbo.time_diff_message('Dec 7 1941')

【讨论】:

【参考方案6】:

在甲骨文中:

select
  CC.MOD_DATETIME,
  'Last modified ' ||
  case when (sysdate - cc.mod_datetime) < 1
       then round((sysdate - CC.MOD_DATETIME)*24) || ' hours ago'
       when (sysdate - CC.MOD_DATETIME) between 1 and 7
       then round(sysdate-CC.MOD_DATETIME) || ' days ago'
       when (sysdate - CC.MOD_DATETIME) between 8 and 365
       then round((sysdate - CC.MOD_DATETIME) / 7) || ' weeks ago'
       when (sysdate - CC.MOD_DATETIME) > 365   
       then round((sysdate - CC.MOD_DATETIME) / 365) || ' years ago'
       end
from 
  customer_catalog CC

【讨论】:

【参考方案7】:

正如你所说,我可能不会在 SQL 中这样做,但作为一个思考练习,有一个 mysql 实现:

CASE
    WHEN compare_date between date_sub(now(), INTERVAL 60 minute) and now() 
        THEN concat(minute(TIMEDIFF(now(), compare_date)), ' minutes ago')

    WHEN datediff(now(), compare_date) = 1 
        THEN 'Yesterday'

    WHEN compare_date between date_sub(now(), INTERVAL 24 hour) and now() 
        THEN concat(hour(TIMEDIFF(NOW(), compare_date)), ' hours ago')

    ELSE concat(datediff(now(), compare_date),' days ago')
END

基于在MySQL Date and Time 手册页上看到的类似示例

【讨论】:

【参考方案8】:

您的代码看起来很实用。至于更好的方法,那将变得主观。您可能想查看此page,因为它处理 SQL 中的时间跨度。

【讨论】:

以上是关于在 SQL 中将 DateTime 转换为“n Hours Ago”的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL Server 中将 varchar 转换为 datetime [重复]

在 SQL 中将 DateTime 转换为“n Hours Ago”的最佳方法

如何在SQL Server中将datetime值转换为yyyymmddhhmmss?

在 SQL Server 中将 varchar 'hh:mm:ss' 转换为 datetime hh:mm:ss

在 sql server 中将 varchar yyyy-mm-dd T hh:mm 转换为 datetime2

在SQL中将dd / mm / yyyy VARCHAR转换为yyyy-dd-mm DATETIME