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

问题描述 投票:10回答:8

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

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

更新:

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

sql sql-server sql-server-2005 datetime date
8个回答
15
投票

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

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


4
投票

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

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


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)

当然,这很难看,但是这与范围查询一样难看,所以一旦你第一次写它,就可以将它封装在一个函数中。


1
投票

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

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


1
投票

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


1
投票

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

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


1
投票

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


0
投票

存储日期与公元前1/13/4713(以及直到现代)的一种方法是使用朱利安日。这与Julian日期不同,是一个整数,编码自公元前1/43/4713以来的天数。

为简单起见,这里是从日期到朱利安日和之后的转换。请注意,如果它超出DATE类型的范围,您仍然无法将Julian Day转换为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
© www.soinside.com 2019 - 2024. All rights reserved.