在SQL Server中存储旧日期的最佳方法

时间:2009-02-04 15:59:08

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

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

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

更新:

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

8 个答案:

答案 0 :(得分:15)

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

您是使用SSMS 2005而不是2008,还是连接到2005实例?那种类型是introduced in SQL Server 2008。如果你有能力使用2008数据库,我认为这无疑是最佳选择。

答案 1 :(得分:4)

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

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

答案 2 :(得分:4)

字符串可能效率低于仅存储年,月,日的整数。这在您的查询中更为明显,但它们可能会运行得更快,因为您可以使用对您正在进行的查询类型有意义的方式对其进行索引。

例如:

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 :(得分:1)

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

按照Ian Varley的建议,以年,月,日整数存储日期也是如此。但是,我喜欢他的回答,只是希望我能想到它; - )

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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

编写一些UDF(也是CodeMonkey1建议的)来转换 int - &gt; YYYYMMDD - &gt; int ,你将拥有Ian Varley在答案中提到的灵活性。

答案 6 :(得分:1)

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

答案 7 :(得分:0)

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

为简单起见,以下是从日期到儒略日的转换。请注意,如果儒略日超出了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