【问题标题】:Best way to store old dates in SQL Server在 SQL Server 中存储旧日期的最佳方法
【发布时间】:2010-10-05 10:45:01
【问题描述】:

在 SQL Server 2005 中存储旧日期(1753 年之前)的最佳/最有效方法是什么?我不关心存储时间 - 只是日期。 SQL Server 的 datetime 数据类型只能保存追溯到 1753 年 1 月 1 日的日期。MSDN 文档指出有 date 和 datetime2 数据类型,但 SQL Server Management Studio 似乎不支持它们(错误:无效数据类型)。

将日期存储为“YYYYMMDD”形式的字符串或整数会有多低效?我对表中的两个日期字段(StartDate 和 EndDate)进行了大量查询和排序。

更新:

下面有一些建议将年、月和日期存储在单独的字段中。将部分存储在不同的字段而不是单个整数字段有什么好处?

【问题讨论】:

标签: sql sql-server sql-server-2005 datetime date


【解决方案1】:

我从未这样做过,但也许您可以将日期存储为一个整数,表示自适合您的最小日期以来的天数。然后,您可以创建一个将这些整数映射到年、月和日的查找表,或者您可以编写用户定义的函数将整数转换为日期,反之亦然。

这在选择和排序方面应该是相当有效的。

【讨论】:

    【解决方案2】:

    字符串的效率可能不如仅存储年、月和日的整数。这在您的查询中有点冗长,但它们可能会运行得更快,因为您可以以对您正在执行的查询类型有意义的方式对它们进行索引。

    例如:

    CREATE TABLE myOldDates (
      year INT,
      month INT,
      day INT,
      -- otherstuff ...
    )
    

    那么查询都会是这样的:

    -- get records between 5/15/1752 and 3/19/1754
    SELECT * FROM myOldDates
      WHERE 
        (year = 1752 AND ((month = 5 and day >= 15) or month > 5) OR year > 1752)
        AND (year = 1754 AND ((month = 3 and day <= 19) or month < 3) OR year < 1754)
    

    当然,这很丑陋,但对于范围查询来说,这也很丑陋,所以一旦你第一次编写它,你就可以将它封装在一个函数中。

    【讨论】:

      【解决方案3】:

      以 YYYYMMDD 格式存储日期的一个问题是,您最终可能会得到不存在的日期(例如 16000231 - 2 月 31 日不存在)。在将其输入数据库之前,您需要做一些验证客户端。

      正如Ian Varley 所建议的那样,以年、月和日整数形式存储日期当然也是如此。但除此之外,我喜欢他的回答,只是希望我能想到它;-)

      【讨论】:

      • 每个适当的客户端如何处理从任何外部来源接收的数据? :)(提示:验证它)
      • 是的。在处理日期时,它唯一可以忽略的东西,因为内置的日期类型会为您进行验证。
      【解决方案4】:

      date 类型绝对是您想要使用的。它的范围是“公元 1 月 1 日至公元 9999 年 12 月 31 日”。它也只存储日期信息,没有时间部分。

      您是使用 SSMS 2005 而不是 2008,还是连接到 2005 实例?那个类型是introduced in SQL Server 2008。如果你有能力使用 2008 年的数据库,我认为这无疑是一条好路。

      【讨论】:

      • 听起来像是升级的理由!
      【解决方案5】:

      YYYYMMDD = 8 个字节。您可以使用 SMALLINT 和 TINYINT 使用 3 列将其减少到 4 个字节。

      【讨论】:

        【解决方案6】:

        使用 CodeMonkey1 建议的整数似乎是一个好主意,并且可以更容易地进行“日期数学”(例如,某个日期 + XX 天)。

        编写一些 UDF(也按照 CodeMonkey1 的建议)来转换 int --> YYYYMMDD --> int,您将拥有 Ian Varley 在他的回答中提到的灵活性。

        【讨论】:

          【解决方案7】:

          一个想法——如果你有一些 .NET 知识,你可以创建一个 CLR 类型来存储日期,它本质上就是一个日期时间。如果您使用日期而不是简单查询进行大量计算,则可能需要调查

          【讨论】:

            【解决方案8】:

            存储早至公元前 4713 年 1 月 1 日(一直到现代)的日期的一种方法是使用 朱利安日。这与儒略日期不同,它是一个整数,编码自公元前 4713 年 1 月 1 日以来的天数。

            为简单起见,以下是日期与儒略日之间的转换。请注意,如果儒略日超出 DATE 类型的范围,您仍然无法将其转换为 DATE,但正如 bdukes 上面所说,DATE 应该保留为 1/1/0001。

            IF OBJECT_ID (N'dbo.ufn_JulianDayFromDate', N'FN') IS NULL
                exec('CREATE function [dbo].[ufn_JulianDayFromDate] () returns int As begin 
            return 1 end;');
            go
            
            alter function dbo.ufn_JulianDayFromDate(@theDate as date) returns int
            as 
            begin
                declare @JulianDayBase int=693596;
                return @JulianDayBase + datediff(d, 0, @theDate);
            end;
            go
            

            和

            IF OBJECT_ID (N'dbo.ufn_DateFromJulianDay', N'FN') IS NULL
                exec('CREATE function [dbo].[ufn_DateFromJulianDay] () returns int As begin return 1 end;');
            go
            
            alter function dbo.ufn_DateFromJulianDay(@JulianDay as int) returns date
            as 
            begin
                declare @JulianDayBase int=693596;
                return dateadd(d, @JulianDay-@JulianDayBase, '1/1/1900')
            end;
            
            go
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-11-03
              • 2015-07-21
              • 1970-01-01
              • 2014-01-25
              • 2010-09-13
              • 2013-01-25
              • 2011-04-16
              • 1970-01-01
              相关资源
              最近更新 更多