在 SQL Server 2008 中从月份和年份创建日期

Posted

技术标签:

【中文标题】在 SQL Server 2008 中从月份和年份创建日期【英文标题】:Create Date From Month and Year in SQL Server 2008 【发布时间】:2017-11-27 20:29:41 【问题描述】:

该表有月份和年份作为单独的列,我想创建一个函数,它将以DD-MM-YYYY 格式返回日期。

我需要为每个组合创建一个开始日期和结束日期,不幸的是架构无法更改,所以我可以使用它。

我已经粘贴了代码:

CREATE FUNCTION fn.GetDateFromMonthYear
    (@isStartDate BIT,
     @month INT,
     @year INT)
RETURNS DATE
AS 
BEGIN
    DECLARE @finalDate AS DATE

    IF (isStartDate)
    BEGIN
        SELECT @finalDate = CAST('01' + '-' + @month + @year AS DATE)
    END
    ELSE
    BEGIN
        SELECT @finalDate = CAST(CAST('01' + '-' + CAST(CAST(@month AS INT) + 1 AS VARCHAR(20)) + @year AS DATETIME) - 1 AS DATE
    END

    RETURN @startDate
END 

有两件事这个功能的性能如何才能有任何改进。

我们如何处理闰年条件。

示例输入和输出

年 = 2017 月 = 11 isStartDate = true

输出:2011 年 1 月 11 日

年= 2017 月 = 11 isStartDate = 假

输出:30-11-2011

年= 2017 月 = 12 isStartDate = 假

输出:2011 年 31 月 12 日

【问题讨论】:

为什么要将 int 月份转换为 int?请记住,您要在此处使用字符数据,而不是数字。所以添加月份和年份意味着你得到了加法,而不是字符串连接。您需要将这些转换为 varchar,然后将它们加在一起。正确的格式是 YYYYMMDD。任何其他格式,有时您会得到不正确的日期。 您要返回该月的第一天还是最后一天? @SeanLange 我想根据标志 isStartDate 返回第一个日期(01-11-2017)和最后一个日期(31-11-2017)。年份和月份在数据库中保存为 int,因此我正在尝试获取这些值。您能否更正并发布您认为最好的答案。对我来说,格式应该是 dd-mm-yyyy 【参考方案1】:

我会使用内联表值函数而不是标量函数。它们更灵活,性能更好。此外,日期没有格式。该格式用于前端。 ANSI 批准的日期表示格式是 YYYYMMDD。无论您的本地设置是什么,这都会起作用。

这是一个使用内联表值函数执行此操作的完整示例。

create function GetDateFromMonthYear
(
    @month int
    , @year int
    , @isStartDate bit
) returns table as return

select MyDate = case @isStartDate 
when 1 
    then dateadd(month, datediff(month, 0, CONVERT(char(4), @year) + right('0' + CONVERT(varchar(2), @month), 2) + '01'), 0)
    else dateadd(day, -1, dateadd(month, datediff(month, 0, CONVERT(char(4), @year) + right('0' + CONVERT(varchar(2), @month), 2) + '01') + 1, 0)) 
end

GO

declare @SomeDates table
(
    MyYear int
    , MyMonth int
    , IsStartDate bit
)

insert @SomeDates
(
    MyYear
    , MyMonth
    , IsStartDate
) values
(2017, 11, 1)
,(2017, 11, 0)
,(2017, 12, 1)
,(2017, 12, 0)

select *
from @SomeDates s
cross apply dbo.GetDateFromMonthYear(s.MyMonth, s.MyYear, s.isStartDate) x

【讨论】:

您的函数在检查不到 10 个月时会为我抛出错误。 @Alexey 对,你是……忘记了那些讨厌的个位数月份。没什么大不了的。现已修复。【参考方案2】:

一个带日期的数学小技巧可以解决这个问题:

select dateadd(month, (YEARCOLUMN-1900)*12 + MONTHCOLUMN - ISSTARTDATE), ISSTARTDATE - 1)

日期在 SQLS 中以数字形式表示,0 表示 1900-01-01 00:00:00,每个整数增量为一天后

将您的年份减去 1900,然后乘以 12,得出我们必须 DATEADD 到开始日期 0(1900 年 1 月 1 日)才能到达您的年份的月数。

然后,如果它是开始日期,我们将比您的月份少一个(您的月份是 11,我们将 10 个月添加到一月以得到第 11 个月的开始),或者如果它是结束日期,我们添加月份 num (即我们再增加一个月,我们将在一天后使用另一个技巧回来)。

isstartdate 在开始日期时为 1,在结束日期时为 0,因此我们本质上添加 (monthnum - isstartdate) 来改变它是开始还是结束。请注意,目前该算法将为 2017、11 和 0 的组件生成 2017-12-01(即是结束日期),因此在这种情况下我们需要有一天回来,所以...

我们要做的唯一其他数学运算是从第 0 天开始计数,如果它是开始日期,则从 -1 开始计数,如果它是结束日期。这很容易通过从 isstartdate 中减去 1 来实现(这意味着当它是开始日期时我们从 0 开始计数,当它是结束日期时我们从 -1 开始计数。换句话说,当它是结束日期时,我们的月份被添加到 1899 -12-31)

http://www.sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1e1f4fbf1/14236

注意:我已经使用“YEARCOLUMN”等占位符完成了这种 sql 样式;您只需将相关变量名替换为上述查询中的所有内容,即大写

【讨论】:

【参考方案3】:

我知道您用“sql-server-2008”标记了您的问题,但是对于以后遇到此问题的任何人,SQL Server 2012 中添加了EOMONTH()DATEFROMPARTS() 函数。这两者都使您的问题成为更容易解决。

CREATE FUNCTION GetDateFromMonthYear
(
    @month INT
    , @year INT
    , @isStartDate BIT
) RETURNS TABLE AS RETURN
SELECT MyDate = CASE @isStartDate 
    WHEN 1 
        THEN DATEFROMPARTS(@year, @month, 1)
        ELSE EOMONTH(DATEFROMPARTS(@year, @month, 1))
    END

【讨论】:

【参考方案4】:

我会这样写,但想法基本相同:

create function dbo.GetDateFromMonthYear(
    @isStartDate bit,
    @month int,
    @year int)
returns date
as
begin
return (
    case when @isStartDate = 1 
    then convert(date, cast(@year as varchar(max)) + '-' + cast(@month as varchar(max)) + '-1', 121)
    else dateadd(day, -1, dateadd(month, 1, convert(date, cast(@year as varchar(max)) + '-' + cast(@month as varchar(max)) + '-1', 121)))
    end)
end

go

select 
    dbo.GetDateFromMonthYear(isStartDate, Month, Year) 
from (values
    (0, 2, 2016),
    (0, 3, 2017),
    (1, 4, 2017),
    (1, 12, 2017)
)t(isStartDate, Month, Year)

【讨论】:

我仍然不建议在这里使用标量函数。它们对性能来说太痛苦了。【参考方案5】:

这是一个使用 dateadd 的示例。如果该位为真,则取该月的第一天。如果该位为假,则加一个月减一天。

DECLARE @sample TABLE ([year] int, [month] int, isStartDate bit)
INSERT INTO @sample VALUES (2017, 11, 1), (2017, 11, 0), (2017, 12, 0)

SELECT CASE WHEN isStartDate = 1
             THEN CONVERT(varchar, CAST(CAST(year as varchar) + CAST(month as varchar) + '01' as date), 105)
             ELSE CONVERT(varchar, DATEADD(DAY, -1, DATEADD(MONTH, 1, CAST(CAST(year as varchar) + CAST(month as varchar) + '01' as date))), 105)
       END
  FROM @sample

生产:

01-11-2017
30-11-2017
31-12-2017

【讨论】:

以上是关于在 SQL Server 2008 中从月份和年份创建日期的主要内容,如果未能解决你的问题,请参考以下文章

如何在sql中从给定的月份和年份获取月份的第一天和最后一天

在 SQL Server 中获取特定年份中的列作为月份名称

如何在 SQL Server 2014 中从 SQL Server 2008 R2 恢复备份?

sql server获取连续年份月份日

SQL取系统时间的前一个月的月份和年份

如何仅从 sql server 2008 中的日期中提取年份?