Der beste Weg, DateTime in SQL in „n Hours Ago“ zu konvertieren
Frage
Ich habe eine SQL-Funktion geschrieben, um einen Datum-Uhrzeit-Wert in SQL in einen benutzerfreundlicheren Nachrichtentyp „n Stunden her“ oder „n Tagen her“ usw. umzuwandeln.Und ich habe mich gefragt, ob es einen besseren Weg gibt, es zu tun.
(Ja, ich weiß: „Machen Sie es nicht in SQL“, aber aus Designgründen muss ich es auf diese Weise tun.)
Hier ist die Funktion, die ich geschrieben habe:
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
Lösung
Wie Sie sagen, würde ich es wahrscheinlich nicht in SQL tun, aber als Gedankenübung habe ich eine MySQL-Implementierung:
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
Basierend auf einer ähnlichen Probe, die auf der gesehen wurde MySQL-Datum und -Uhrzeit Handbuchseiten
Andere Tipps
In 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
Mein Versuch - das ist für MS SQL.Es unterstützt „vor“ und „von jetzt“ sowie die Pluralisierung und verwendet keine Rundung oder Datierung, sondern Kürzung – datierte ergibt einen 1-Monats-Differenz zwischen dem 30.08. und dem 1.09., was wahrscheinlich nicht das ist, was Sie wollen.Durch Rundung ergibt sich eine 1-Monats-Differenz zwischen dem 1. September und dem 16. September.Auch hier wahrscheinlich nicht das, was Sie wollen.
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
Ihr Code sieht funktionsfähig aus.Was einen besseren Weg betrifft, wird das subjektiv.Vielleicht möchten Sie sich das ansehen Seite da es sich um Zeitspannen in SQL handelt.
Wie wäre es damit?Sie könnten dieses Muster erweitern, um „Jahre“-Nachrichten zu erstellen, und Sie könnten ein Häkchen für „1 Tag“ oder „1 Stunde“ setzen, damit dort nicht „vor 1 Tagen“ steht ...
Ich mag die CASE-Anweisung in SQL.
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')
Vielen Dank für die verschiedenen oben geposteten Codes.
Wie Hafthor betonte, gibt es im Originalcode Einschränkungen im Zusammenhang mit der Rundung.Ich habe auch festgestellt, dass einige der Ergebnisse, die sein Code ausgegeben hat, nicht mit meinen Erwartungen übereinstimmten, z. B.Freitagnachmittag -> Montagmorgen würde als „vor 2 Tagen“ angezeigt.Ich denke, wir würden das alle vor drei Tagen nennen, auch wenn noch keine drei vollständigen 24-Stunden-Perioden vergangen sind.
Deshalb habe ich den Code geändert (das ist MS SQL).Haftungsausschluss:Ich bin ein unerfahrener TSQL-Programmierer, also ist das ziemlich knifflig, funktioniert aber!!
Ich habe einige Überschreibungen vorgenommen – z.Alles bis zu 2 Wochen wird in Tagen ausgedrückt.Alles darüber bis zu 2 Monate wird in Wochen ausgedrückt.Alles, was darüber hinausgeht, wird in Monaten usw. angegeben.Es schien einfach die intuitive Art zu sein, es auszudrücken.
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
Die obigen Beiträge haben mir einige gute Ideen gegeben, daher ist hier eine weitere Funktion für alle, die SQL Server 2012 verwenden.
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
Und die Umsetzung:
SELECT TIME_ELAPSED = DBO.FN_TIME_ELAPSED(AUDIT_TIMESTAMP)
FROM SOME_AUDIT_TABLE