Лучший способ хранения старых дат в SQL Server

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

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

UPDATE:

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

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

Итак, например:

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

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

Один из способов хранения дат 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, @[email protected], '1/1/1900')
end;

go