SQL Server: установить дату на SQL-сервере, но оставаться детерминированным

(Это связано с установкой даты на SQL-сервере.)

Существует ли детерминированное выражение для определения DATETIME? Когда я использую это как формулу вычисляемого столбца:

DATEADD(dd, DATEDIFF(dd, 0, [datetime_column]), 0)

я получаю сообщение об ошибке, когда помещаю индекс в этот столбец:

Невозможно создать индекс, так как ключевой столбец «EffectiveDate» является недетерминированным или неточным.

Но и DATEDIFF, и DATEADD по определению являются детерминированными функциями. Где подвох? Является ли это возможным?


person Tomalak    schedule 21.11.2008    source источник


Ответы (6)


Я предполагаю, что это какой-то баг. В SQL 2005 мне удалось без проблем создать такое индексированное представление (код приведен ниже). Когда я попытался запустить его на SQL 2000, я получил ту же ошибку, что и вы.

Следующее, похоже, работает с SQL 2000, но я получаю предупреждение о том, что индекс будет проигнорирован, И вам придется преобразовывать каждый раз, когда вы выбираете из представления.

CONVERT(CHAR(8), datetime_column, 112)

Работает в SQL 2005:

CREATE TABLE dbo.Test_Determinism (
    datetime_column DATETIME    NOT NULL    DEFAULT GETDATE())
GO

CREATE VIEW dbo.Test_Determinism_View
WITH SCHEMABINDING
AS
    SELECT
        DATEADD(dd, DATEDIFF(dd, 0, [datetime_column]), 0) AS EffectiveDate
    FROM
        dbo.Test_Determinism
GO

CREATE UNIQUE CLUSTERED INDEX IDX_Test_Determinism_View ON dbo.Test_Determinism_View (EffectiveDate)
GO
person Tom H    schedule 21.11.2008

В вашем столбце [datetime_column] установлено значение по умолчанию "getDate()"??

Если это так, поскольку функция getdate() не является детерминированной, это вызовет эту ошибку...

Является ли определяемая пользователем функция детерминированной или недетерминированной, зависит от того, как функция закодирована. Пользовательские функции являются детерминированными, если:

  1. Функция привязана к схеме.
  2. Все встроенные или пользовательские функции, вызываемые пользовательской функцией, являются детерминированными.
  3. Тело функции не ссылается на объекты базы данных вне области действия функции. Например, детерминированная функция не может ссылаться на таблицы, отличные от табличных переменных, которые являются локальными для функции.
  4. Функция не вызывает никаких расширенных хранимых процедур.

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

person Charles Bretana    schedule 21.11.2008
comment
getDate не является детерминированным, я считаю, что это было бы причиной - person kristof; 21.11.2008
comment
Но это просто значение по умолчанию. Это не часть формулы. Кстати, я получаю ту же ошибку для столбца NULL по умолчанию. - person Tomalak; 21.11.2008
comment
Я предполагаю, что подпрограмма, которая делает это, не различает ту часть определения столбца, которая представляет формулу, и часть, которая представляет значение по умолчанию... - person Charles Bretana; 21.11.2008
comment
Да, это. Я пробовал предложение Кристофа. - person Tomalak; 21.11.2008

Попробуй это:

CAST(FLOOR(CAST([datetime_column] as FLOAT)) AS DateTime)

Это должно работать намного быстрее, чем опция CONVERT.

person Joel Coehoorn    schedule 21.11.2008
comment
CAST не является детерминированным для значений даты и времени. - person Tomalak; 21.11.2008

Вот мой лучший ответ на исходный вопрос:

Попробуй это:

/* create a deterministic schema bound function */
CREATE FUNCTION FloorDate(@dt datetime)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN 
    RETURN CONVERT(datetime,  FLOOR(CONVERT(float, @dt)))
END
GO

Для проверки попробуйте следующее. Обратите внимание на использование «PERSISTED» для вычисляемого столбца и использование [dbo.] при ссылке на функцию

/*create a test table */
CREATE TABLE [dbo].[TableTestFloorDate](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [TestDate] [datetime] NOT NULL,
    [TestFloorDate]  AS ([dbo].[FloorDate]([TestDate])) PERSISTED,
 CONSTRAINT [PK_TableTestFloorDate] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
) 

Теперь вы должны иметь возможность добавить индекс в вычисляемый столбец (но см. позже)

CREATE INDEX IX_TestFloorDate ON  [dbo].[TableTestFloorDate](TestFloorDate)

Вставляйте случайные данные столько раз, сколько хотите, но больше (1000+) лучше, если вы хотите протестировать планы использования/выполнения индекса.

INSERT INTO TableTestFloorDate (TestDate) VALUES( convert(datetime, RAND()*50000))

Получить результаты

SELECT * FROM TableTestFloorDate WHERE TestFloorDate='2013-2-2'

А вот и ГОТЧА... Индекс, созданный для вычисляемого столбца, не используется! Вместо этого, даже при выборе данных в постоянном поле TestFloorDate, SQLServer (или, по крайней мере, моя версия) предпочитает индекс TestDate.

CREATE INDEX IX_TestFloorDate ON  [dbo].[TableTestFloorDate](TestDate)

Я почти уверен (из памяти), что индексы в вычисляемых, постоянных столбцах приносят пользу с точки зрения производительности - я думаю, вам просто нужно попробовать/проверить свои собственные конкретные варианты использования.

(Надеюсь, я помог!)

person dunxz    schedule 24.07.2013
comment
У меня нет возможности проверить это прямо сейчас. Совместим ли этот SQL Server 2000 (я знаю, что он древний, но теги в вопросе указывали на эту версию)? - person Tomalak; 24.07.2013
comment
Вышеприведенное было протестировано в Sql2012, но по памяти я не понимаю, почему это не работает на Sql2k точно так, как описано выше. - person dunxz; 25.07.2013

Я бы предложил несколько проще:

 cast(cast([datetime_column] as int) as datetime)

но я подозреваю, что вы столкнетесь с той же проблемой.

Теперь, если проблема заключается в возврате к дате и времени, вы можете рассмотреть возможность использования только cast([datetime_column] as int) в качестве отдельного поля только для индекса.

person James Curran    schedule 21.11.2008
comment
Проблема также заключается в приведении from datetime (или, если уж на то пошло, convert()). - person Tomalak; 21.11.2008