Лучший способ хранить старые даты в SQL Server - PullRequest
10 голосов
/ 04 февраля 2009

Каков наилучший / наиболее эффективный способ хранения старых дат (до 1753 г.) в SQL Server 2005? Я не занимаюсь хранением времени - просто даты. Тип данных datetime в SQL Server может содержать даты только до 1 января 1753 года. В документации MSDN говорится, что существуют типы данных date и datetime2, но SQL Server Management Studio, похоже, их не поддерживает (Ошибка: недопустимый тип данных).

Насколько неэффективно хранить даты в виде строк или целых чисел в форме "ГГГГММДД"? Я делаю много запросов и сортировки по двум полям даты в моей таблице (StartDate и EndDate).

UPDATE:

Ниже приведены некоторые предложения по сохранению года, месяца и даты в отдельных полях. В чем преимущество хранения деталей в разных полях, а не в одном целочисленном поле?

Ответы [ 8 ]

15 голосов
/ 04 февраля 2009

Тип date определенно то, что вы хотите использовать. Его диапазон: «1 января, 1 г. до 31 декабря 9999 г. н.э.» Он также просто хранит информацию о дате, без временной части.

Возможно, вы используете SSMS 2005, а не 2008, или подключены к экземпляру 2005 года? Этот тип был введен в SQL Server 2008 . Если у вас есть возможность использовать базу данных 2008 года, я думаю, что это, безусловно, путь.

4 голосов
/ 04 февраля 2009

Строки, вероятно, будут менее эффективными, чем просто хранение целых чисел для года, месяца и дня. Это немного больше словоблудия в ваших запросах, но они, вероятно, будут работать быстрее, поскольку вы можете индексировать их способами, которые имеют смысл для типов запросов, которые вы делаете.

Так, например:

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)

Конечно, это уродливо, но примерно так же уродливо для запросов диапазона, поэтому, как только вы напишите его в первый раз, вы можете инкапсулировать его в функцию.

4 голосов
/ 04 февраля 2009

Я никогда не делал этого, но, возможно, вы могли бы сохранить дату в виде целого числа, представляющего количество дней, так как любая минимальная дата подходит вам. Затем вы можете либо создать таблицу поиска, которая отображает эти целые числа на год, месяц и день, либо написать пользовательские функции для преобразования целого числа в дату или наоборот.

Это должно быть достаточно эффективно с точки зрения выбора и сортировки.

1 голос
/ 04 февраля 2009

Идея - если у вас есть знания .NET, вы можете создать тип CLR для хранения даты, который по сути будет datetime. Если вы выполняете много вычислений с датой, а не с простыми запросами, возможно, стоит исследовать

1 голос
/ 04 февраля 2009

Использование целочисленных значений, предложенных CodeMonkey1, кажется хорошей идеей и облегчит выполнение "математики дат" (например, некоторые даты + XX дней).

Напишите несколько UDF (также по совету CodeMonkey1) для преобразования int -> YYYYMMDD -> int , и у вас будет гибкость, о которой Ян Варли упоминает в своем ответе.

1 голос
/ 04 февраля 2009

ГГГГММДД = 8 байт. Вы можете сократить его до 4 байтов с помощью SMALLINT и TINYINT, используя 3 столбца.

1 голос
/ 04 февраля 2009

Одна проблема с сохранением дат в формате ГГГГММДД заключается в том, что у вас могут оказаться даты, которых не существует (например, 16000231 - 31 февраля не существует). Вам необходимо выполнить некоторую проверку на стороне клиента, прежде чем вводить ее в базу данных.

То же самое относится и к сохранению даты в целых числах года, месяца и дня, как предложено Ian Varley . Но, кроме этого, мне нравится его ответ, и я просто хотел бы подумать об этом; -)

0 голосов
/ 15 апреля 2019

Один из способов сохранить даты, датированные 1/1/4713 г. до н.э. (и вплоть до современности), - это использовать Юлианский день . Это не то же самое, что юлианская дата, и представляет собой целое число, кодирующее число дней с 01.014713 г. до н.э.

Для простоты здесь приведены переводы из даты в юлианский день и обратно. Обратите внимание, что вы все еще не можете преобразовать юлианский день в 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
...