在SQL中将DateTime转换为" n Hours Ago"的最佳方法
时间:2020-03-05 18:49:29 来源:igfitidea点击:
我编写了一个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
解决方案
回答
代码看起来正常。至于更好的方法,那将变得主观。我们可能想签出此页面,因为它处理SQL中的时间跨度。
回答
如我们所说,我可能不会在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日期和时间手册页上看到的类似示例
回答
在Oracle中:
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
回答
这个怎么样?我们可以扩展此模式以发送"年"消息,并且可以放入" 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')
回答
我的尝试是针对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
回答
感谢我们上面发布的各种代码。
正如Hafthor指出的那样,原始代码在取整方面存在局限性。我还发现他的代码踢出的一些结果与我期望的结果不符,例如星期五下午->星期一早晨将显示为" 2天前"。我想我们三天前都会打电话给我,尽管还没有过去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