有没有更好的方法将 SQL 日期时间从 hh:mm:ss 转换为 hhmmss?

发布于 2024-07-21 12:49:03 字数 1117 浏览 13 评论 0原文

我必须编写一个 SQL 视图,以 hhmmss 格式的字符串形式返回日期时间列的时间部分(显然 SAP BW 不理解 hh:mm:ss)。

此代码是 SAP 推荐的方法来执行此操作,但我认为必须有一种更好、更优雅的方法来完成此操作

TIME = case len(convert(varchar(2), datepart(hh, timecolumn)))
             when 1 then       /* Hour Part of TIMES */
               case convert(varchar(2), datepart(hh, timecolumn))
                 when '0' then '24'    /* Map 00 to 24 ( TIMES ) */
                 else '0' + convert(varchar(1), datepart(hh, timecolumn))
               end
            else convert(varchar(2), datepart(hh, timecolumn))
            end
         + case len(convert(varchar(2), datepart(mi, timecolumn)))
              when 1 then '0' + convert(varchar(1), datepart(mi, timecolumn))
              else convert(varchar(2), datepart(mi, timecolumn))
           end
         + case len(convert(varchar(2), datepart(ss, timecolumn)))
              when 1 then '0' + convert(varchar(1), datepart(ss, timecolumn))
              else convert(varchar(2), datepart(ss, timecolumn))
           end

这实现了所需的结果,21:10:45 显示为 211045。

我喜欢更紧凑的东西并且易于阅读,但到目前为止我还没有想出任何有效的方法。

I have to write an SQL view that returns the time part of a datetime column as a string in the format hhmmss (apparently SAP BW doesn't understand hh:mm:ss).

This code is the SAP recommended way to do this, but I think there must be a better, more elegant way to accomplish this

TIME = case len(convert(varchar(2), datepart(hh, timecolumn)))
             when 1 then       /* Hour Part of TIMES */
               case convert(varchar(2), datepart(hh, timecolumn))
                 when '0' then '24'    /* Map 00 to 24 ( TIMES ) */
                 else '0' + convert(varchar(1), datepart(hh, timecolumn))
               end
            else convert(varchar(2), datepart(hh, timecolumn))
            end
         + case len(convert(varchar(2), datepart(mi, timecolumn)))
              when 1 then '0' + convert(varchar(1), datepart(mi, timecolumn))
              else convert(varchar(2), datepart(mi, timecolumn))
           end
         + case len(convert(varchar(2), datepart(ss, timecolumn)))
              when 1 then '0' + convert(varchar(1), datepart(ss, timecolumn))
              else convert(varchar(2), datepart(ss, timecolumn))
           end

This accomplishes the desired result, 21:10:45 is displayed as 211045.

I'd love for something more compact and easily readable but so far I've come up with nothing that works.

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

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

发布评论

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

评论(7

孤云独去闲 2024-07-28 12:49:03

注意:

问题表明该列的数据类型为DATETIME,而不是较新的(SQL Server 2008)TIME 数据类型。

答案:

  REPLACE(CONVERT(VARCHAR(8),timecolumn,8),':','')

让我们来解压一下。

CONVERT 将日期时间的时间部分格式化为 varchar,格式为“hh:mi:ss”(24 小时制),由格式样式值 8 指定。

首先 REPLACE 函数删除冒号,以获取 'hhmiss' 格式的 varchar。

这应该足以获得您需要的格式的可用字符串。


后续问题

(OP问题提出)

内联表达式是否比用户定义的函数更快/更少的服务器密集度?

快速回答是肯定的。 更长的答案是:这取决于几个因素,并且您确实需要测量性能以确定这是否属实。

我创建并执行了一个基本测试用例:(

    -- sample table
    create table tmp.dummy_datetimes (c1 datetime)

    -- populate with a row for every minute between two dates
    insert into tmp.dummy_datetimes
    select * from udfDateTimes('2007-01-01','2009-01-01',1,'minute')

    (1052641 row(s) affected)

    -- verify table contents
    select min(c1) as _max
         , max(c1) as _min
         , count(1) as _cnt
      from tmp.dummy_datetimes

    _cnt    _min                    _max
    ------- ----------------------- -----------------------
    1052641 2007-01-01 00:00:00.000 2009-01-01 00:00:00.000

注意,udfDateTimes 函数返回指定间隔内两个日期时间值之间的所有日期时间值的集合。在本例中,我用整整两年内每分钟的行填充了虚拟表。 。

现在,用户定义的函数使用相同的语法执行与内联表达式相同的转换:

    CREATE FUNCTION [tmp].[udfStrippedTime] (@ad DATETIME)
    RETURNS VARCHAR(6)
    BEGIN
    -- Purpose: format time portion of datetime argument to 'hhmiss'
    -- (for performance comparison to equivalent inline expression)
    -- Modified:
    -- 28-MAY-2009 spencer7593

       RETURN replace(convert(varchar(8),@ad,8),':','')

    END

注意:我知道该函数未定义为确定性的 (我认为这需要使用模式绑定和其他一些声明来声明该函数,例如 Oracle 所需的 PRAGMA。)但是由于每个日期时间值在表中都是唯一的,因此这并不重要。函数必须对每个不同的值执行,即使它被正确地声明为DETERMINISTIC,

我不是这里的SQL Server“用户定义函数”专家,所以可能还有其他东西。我错过了这会无意中且不必要地减慢该功能。

好的。

因此,在测试中,我交替运行这些查询中的每一个,先是一个,然后是另一个,一遍又一遍地连续运行。 第一次运行的时间与后续运行的时间一致。 (通常情况并非如此,我们希望忽略第一次运行的时间。)SQL Server Management Studio 将查询运行时间报告为最接近的秒数,格式为 hh:mi:ss,这就是我在这里报告的内容。

    -- elapsed times for inline expression
    select replace(convert(varchar(8),c1,8),':','') from tmp.dummy_datetimes

    00:00:10
    00:00:11
    00:00:10

    -- elapsed times for equivalent user defined function
    select tmp.udfStrippedTime(c1) from tmp.dummy_datetimes

    00:00:15
    00:00:15
    00:00:15

对于此测试用例,我们观察到用户定义的函数比等效的内联表达式慢大约 45%。

华泰

NOTE:

The question says that the column is of datatype DATETIME, not the newer (SQL Server 2008) TIME datatype.

ANSWER:

  REPLACE(CONVERT(VARCHAR(8),timecolumn,8),':','')

Let's unpack that.

First, CONVERT formats the time portion of the datetime into a varchar, in format 'hh:mi:ss' (24-hour clock), as specified by the format style value of 8.

Next, the REPLACE function removes the colons, to get varchar in format 'hhmiss'.

That should be sufficient to get a usable string in the format you'd need.


FOLLOW-UP QUESTION

(asked by the OP question)

Is an inline expression faster/less server intensive than a user defined function?

The quick answer is yes. The longer answer is: it depends on several factors, and you really need to measure the performance to determine if that's actually true or not.

I created and executed a rudimentary test case:

    -- sample table
    create table tmp.dummy_datetimes (c1 datetime)

    -- populate with a row for every minute between two dates
    insert into tmp.dummy_datetimes
    select * from udfDateTimes('2007-01-01','2009-01-01',1,'minute')

    (1052641 row(s) affected)

    -- verify table contents
    select min(c1) as _max
         , max(c1) as _min
         , count(1) as _cnt
      from tmp.dummy_datetimes

    _cnt    _min                    _max
    ------- ----------------------- -----------------------
    1052641 2007-01-01 00:00:00.000 2009-01-01 00:00:00.000

(Note, the udfDateTimes function returns the set of all datetime values between two datetime values at the specified interval. In this case, I populated the dummy table with rows for each minute for two entire years. That's on the order of a million ( 2x365x24x60 ) rows.

Now, user defined function that performs the same conversion as the inline expression, using identical syntax:

    CREATE FUNCTION [tmp].[udfStrippedTime] (@ad DATETIME)
    RETURNS VARCHAR(6)
    BEGIN
    -- Purpose: format time portion of datetime argument to 'hhmiss'
    -- (for performance comparison to equivalent inline expression)
    -- Modified:
    -- 28-MAY-2009 spencer7593

       RETURN replace(convert(varchar(8),@ad,8),':','')

    END

NOTE: I know the function is not defined to be DETERMINISTIC. (I think that requires the function be declared with schema binding and some other declaration, like the PRAGMA required Oracle.) But since every datetime value is unique in the table, that shouldn't matter. The function is going to have to executed for each distinct value, even if it were properly declared to be DETERMINISTIC.

I'm not a SQL Server 'user defined function' guru here, so there may be something else I missed that will inadvertently and unnecessarily slow down the function.

Okay.

So for the test, I ran each of these queries alternately, first one, then the other, over and over in succession. The elapsed time of the first run was right in line with the subsequent runs. (Often that's not the case, and we want to throw out the time for first run.) SQL Server Management Studio reports query elapsed times to the nearest second, in format hh:mi:ss, so that's what I've reported here.

    -- elapsed times for inline expression
    select replace(convert(varchar(8),c1,8),':','') from tmp.dummy_datetimes

    00:00:10
    00:00:11
    00:00:10

    -- elapsed times for equivalent user defined function
    select tmp.udfStrippedTime(c1) from tmp.dummy_datetimes

    00:00:15
    00:00:15
    00:00:15

For this test case, we observe that the user defined function is on the order of 45% slower than an equivalent inline expression.

HTH

辞取 2024-07-28 12:49:03

您可以使用用户定义的函数,例如:

create FUNCTION [dbo].[udfStrippedTime]
(
    @dt datetime
)
RETURNS varchar(32)
AS
BEGIN
    declare @t varchar(32)
    set @t = convert( varchar(32), @dt, 108 )
    set @t = left(@t,2) + substring(@t,4,2)

    RETURN @t
END

然后将

select dbo.udfStrippedTime(GETDATE())

秒的逻辑留给读者作为练习

you could use a user-defined function like:

create FUNCTION [dbo].[udfStrippedTime]
(
    @dt datetime
)
RETURNS varchar(32)
AS
BEGIN
    declare @t varchar(32)
    set @t = convert( varchar(32), @dt, 108 )
    set @t = left(@t,2) + substring(@t,4,2)

    RETURN @t
END

then

select dbo.udfStrippedTime(GETDATE())

the logic for the seconds is left as an exercise for the reader

稳稳的幸福 2024-07-28 12:49:03

这里有一个问题。 格式化是否需要在数据库服务器上进行? 服务器本身实际上只关心数据的存储,并且针对数据的存储进行了优化。 查看数据通常是 Db 之上的 hte 层的责任(从严格的学术意义上来说,现实世界有点混乱)

例如,如果您将结果输出到绑定到 GridControl 的 ASP.NET 页面中,您将只需在绑定到列时指定 DataFormattingString 即可。 如果您使用 C# 将其写入文本文件,那么当您提取数据时,您只需将格式字符串传递给 .ToString() 函数。

如果您需要它专门位于 DbServer 上,那么几乎每个解决方案都会很混乱,因为您需要的时间格式虽然紧凑且符合逻辑,但不是服务器能够识别的时间格式,因此您需要对其进行操作作为字符串。

Here's a question. Does the formatting need to happen on the Db Server? The server itself really only care about, and is optimized for storing the data. Viewing the data is usually the responsibility of hte layer above the Db (in a strictly academic sense, the real world is a bit more messy)

For instance, if you are outputting the result into an ASP.NET page bound to a GridControl you would just specify your DataFormattingString when you bind to the column. If you were using c# to write it to a text file, when you pull the data you would just pass the format string to the .ToString() function.

If you need it to be on the DbServer specifically, then yeah pretty much every solution is going to be messy because the time format you need, while compact and logical, is not a time format the server will recognize so you will need to manipulate it as a string.

狂之美人 2024-07-28 12:49:03

这处理 00 -> 24 转换

SELECT CASE WHEN DATEPART(hh,timecolumn) = 0 
    THEN  '24' + SUBSTRING(REPLACE(CONVERT(varchar(8),timecolumn, 108),':',''),3,4)
    ELSE REPLACE(CONVERT(varchar(8),timecolumn, 108),':','') END

This handles the 00 - > 24 conversion

SELECT CASE WHEN DATEPART(hh,timecolumn) = 0 
    THEN  '24' + SUBSTRING(REPLACE(CONVERT(varchar(8),timecolumn, 108),':',''),3,4)
    ELSE REPLACE(CONVERT(varchar(8),timecolumn, 108),':','') END
若沐 2024-07-28 12:49:03

编辑2:更新为句柄0 --> 24 转换,以及较短的版本:

select replace(left(replace(convert(char,getdate(),8),':',''),2),'00','24') + right(replace(convert(char,getdate(),8),':',''),4)

回到稍长的版本:)

Edit 2: updated to handle 0 --> 24 conversion, and a shorter version:

select replace(left(replace(convert(char,getdate(),8),':',''),2),'00','24') + right(replace(convert(char,getdate(),8),':',''),4)

Back to the slightly longer version :)

拥醉 2024-07-28 12:49:03

选择替换(转换(varchar(15),日期时间字段,108),':','')
从表

SELECT replace(convert(varchar(15),datetimefield, 108), ':','')
from Table

大姐,你呐 2024-07-28 12:49:03

选择替换('2009-05-27 12:49:19', ':', '')
2009-05-27 124919

SELECT REPLACE('2009-05-27 12:49:19', ':', '')
2009-05-27 124919

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