【问题标题】:Building a timestamp from date, time, and meridian fields从日期、时间和经络字段构建时间戳
【发布时间】:2015-10-24 03:17:49
【问题描述】:

我有一个旧系统,它使用四个离散字段来捕获日期时间:一个用于日期(例如 2015 年 10 月 23 日),一个用于小时(例如,10),一个用于分钟(例如,45 ),一个用于子午线(A 或 P)。

我正在尝试在 MSSQL 2008 R2 中构建一个标量函数,该函数将上述所有字段作为参数并返回使用所有提供的值创建的日期时间值。

这是我目前所拥有的:

CREATE FUNCTION f_getTimeStamp (@date Date, @hour Varchar, @minute Varchar, @meridian Varchar)
    RETURNS Datetime
    AS
    BEGIN
        DECLARE @createdDateTime datetime
        DECLARE @createdTime varchar

        SELECT @createdTime = @hour + ':' + @minute +
            CASE
                WHEN @meridian = 'A' Then 'am'
                WHEN @meridian = 'P' Then 'pm'
                Else 'pm'
            END

        SET @createdDateTime = @date + CAST(@createdTime AS DATETIME)

        RETURN @createdDateTime
    END

我们的想法是获取创建时间所需的字段并将它们组合成一个看起来像时间值的字符串,然后将其添加到已经存在的日期字段中。

我目前在查询中收到以下错误:

从字符串转换日期和/或时间时转换失败。

我应该注意,在使用此函数的查询中,我只发送非空值。

非常感谢任何帮助。

谢谢。

【问题讨论】:

  • 您作为参数传递的字段是否与您在函数中声明的数据类型相同?

标签: sql-server datetime


【解决方案1】:

您的代码中只有一处尝试从字符串转换日期/时间:

CAST(@createdTime AS DATETIME)

您在某处的值不会导致可以转换的字符串。

您可以在WHERE 子句中使用您的时间构建逻辑和ISDATE() 找到违规值:

SELECT *
FROM YourTable
WHERE ISDATE(CAST(Hour AS VARCHAR(10)+':'+CAST(Minute AS VARCHAR(10)) = 0

'2:4am' 之类的内容将毫无问题地转换为日期时间,因此您的格式本身没有问题。

修复此问题后,您应该会遇到另一个关于不兼容数据类型的错误,因为您无法添加 DATE 和 DATETIME,因此您需要进行更改:

SET @createdDateTime = @date + CAST(@createdTime AS DATETIME)

收件人:

SET @createdDateTime = CAST(@date AS DATETIME) + CAST(@createdTime AS DATETIME)

【讨论】:

    【解决方案2】:

    我带来了这个解决方案。我认为使用 DATEADD 和 DATEPART 这样的内置函数可以很好地解决这个问题。这是我使用的脚本:

    添加日期:

    返回具有指定数字间隔的指定日期(有符号 integer) 添加到该日期的指定日期部分。

    日期:

    返回一个整数,表示指定日期部分 指定日期。

    实际查询:

    DECLARE @date DATETIME = '10/23/2015'
      , @hour VARCHAR(20) = 10
      , @minute VARCHAR(20) = 45
      , @meridian VARCHAR(20) = 'P'
      , @time DATETIME;
      
    SELECT @time = @hour + ':' + @minute + CASE @meridian
        WHEN 'A' THEN 'AM'
        WHEN 'P' THEN 'PM'
        ELSE 'PM'
      END
    
    
    SELECT DATEADD(MINUTE, DATEPART(MINUTE, @time), DATEADD(HOUR, DATEPART(HOUR, @time), @date));
    

    我在Stack Exchange Data Explorer 上构建了一个查询。它为我的场景带来了这个结果:

    2015-10-23 22:45:00
    

    它有什么作用?

    1. 它创建一个时间字符串,然后将其转换为日期时间
    2. 然后它使用 DATEPART 从创建的字符串中提取分钟和小时(已处理子午线),并使用 DATEADD 将值添加到给定日期

    P.S. 始终为您的 VARCHAR 声明长度。

    如果您没有定义长度,SQL Server 会为您分配一个长度,并且 它可能没有您期望的那么宽。在某些情况下,它将是 1。

    来源:Bad habits to kick : declaring VARCHAR without (length)

    【讨论】:

    • @JoshuaBrewster 很高兴我提供了帮助。 :)
    【解决方案3】:

    那里正在进行大量的类型转换。这个表达式@date + CAST(@createdTime AS DATETIME) 很可能是给你错误的行。输出可能是 `2012-01-01 00:00:002012-01-01 10:45:00'

    更好的方法是简单地将时间添加到原始日期。请注意,我更改了函数中的数据类型,以使函数中没有发生类型转换。

    CREATE FUNCTION f_getTimeStamp (@date Date, @hour INT, @minute INT, @meridian CHAR(1))
    RETURNS Datetime
    AS
    BEGIN
        DECLARE @TEMP DATETIME
        SET @TEMP = @date
        IF @hour < 12
            SET @TEMP = DATEADD(HOUR, @date, @hour)
        SET @TEMP = DATEADD(MINUTE, @date, @minute)
    
        IF @meridian = 'P' AND @hour < 13 -- the hour must already be PM
            SET @TEMP = DATEADD(HOUR, @date, 12)
    
        RETURN @TEMP
    END
    

    注意:我没有在 SQL Server 中测试过这个。我可能有一两个错字。但是,这个概念应该是合理的。

    【讨论】:

    • 谢谢。我应该注意,小时字段不是 24 小时时间格式字段。也就是说,1PM 在小时字段中为 1,在子午线字段中为 P。
    • 而且,这段代码应该适应这一点。当我编写一个函数或一些我以外的随机人可能使用的东西时(不是指你,只是浏览数据库中可用的函数并说“嘿,我可以将它用于我不相关的人目的”)。
    猜你喜欢
    • 2017-08-08
    • 2020-03-23
    • 2013-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-16
    • 2019-09-23
    • 2019-07-21
    相关资源
    最近更新 更多