Детерминированная скалярная функция для получения дня недели для даты - PullRequest
5 голосов
/ 26 ноября 2009

SQL Server, пытается получить день недели через детерминированный UDF.

Я уверен, что это возможно, но не могу понять.

ОБНОВЛЕНИЕ: ОБРАЗЕЦ КОДА.

CREATE VIEW V_Stuff WITH SCHEMABINDING AS 
SELECT    
MD.ID, 
MD.[DateTime]
...
        dbo.FN_DayNumeric_DateTime(MD.DateTime) AS [Day], 
        dbo.FN_TimeNumeric_DateTime(MD.DateTime) AS [Time], 
...
FROM       {SOMEWHERE}
GO
CREATE UNIQUE CLUSTERED INDEX V_Stuff_Index ON V_Stuff (ID, [DateTime])
GO

Ответы [ 10 ]

6 голосов
/ 26 ноября 2009

Хорошо, я понял ..

CREATE FUNCTION [dbo].[FN_DayNumeric_DateTime] 
(@DT DateTime)
RETURNS INT WITH SCHEMABINDING
AS 
BEGIN
DECLARE @Result int 
DECLARE  @FIRST_DATE        DATETIME
SELECT @FIRST_DATE = convert(DATETIME,-53690+((7+5)%7),112)
SET  @Result = datediff(dd,dateadd(dd,(datediff(dd,@FIRST_DATE,@DT)/7)*7,@FIRST_DATE), @DT)
RETURN (@Result)
END
GO
6 голосов
/ 24 февраля 2011

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

Предположения:

  1. У вас нет дат раньше 1899-12-31 (воскресенье)
  2. Вы хотите подражать @@ datefirst = 7
  3. @ dt: smalldatetime, datetime, тип данных date или datetime2

Если вы хотите, чтобы это было иначе, замените дату '18991231' на дату с днем ​​недели, который вы хотите равным 1. Функция convert () является ключом к тому, чтобы все это работало - приведение НЕ дает сделать трюк:

((датированный (день, конвертировать (дата и время, '18991231', 112), @dt)% 7) + 1)

1 голос
/ 10 декабря 2013

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

В моем случае я пытался использовать вычисляемый столбец PERSISTED, который требует, чтобы вычисление было детерминированным. Я использовал следующие расчеты:

datediff(dd,'2010-01-03',[DateColumn]) % 7 + 1

Идея состоит в том, чтобы выяснить известное воскресенье, которое, как вы знаете, произойдет до любой возможной даты в вашей таблице (в данном случае, 3 января 2010 г.), а затем вычислить по модулю 7 + 1 числа дней с этого воскресенья.

Проблема в том, что включение буквенной даты в вызов функции достаточно, чтобы пометить ее как недетерминированную. Вы можете обойти это, используя целое число 0 для представления эпохи, которая для SQL Server 1 января 1900 года, воскресенье.

datediff(dd,0,[DateColumn]) % 7 + 1

+1 только заставляет результат работать так же, как datepart (dw, [datecolumn]), когда datefirst установлен на 7 (по умолчанию для США), который устанавливает воскресенье на 1, понедельник на 2 и т. Д.

Я также могу использовать это в сочетании с case [thatComputedColumn], когда 1, затем «воскресенье», когда 2, а затем «понедельник» ... и т. Д. Более сложный, но детерминированный, что было требованием в моей среде.

1 голос
/ 12 июля 2012

Взято из Детерминированная скалярная функция для получения недели года для даты

;
with 
Dates(DateValue) as 
(
    select cast('2000-01-01' as date)
    union all 
    select dateadd(day, 1, DateValue) from Dates where DateValue < '2050-01-01'
)
select 
    year(DateValue) * 10000 + month(DateValue) * 100 + day(DateValue) as DateKey, DateValue,        
    datediff(day, dateadd(week, datediff(week, 0, DateValue), 0), DateValue) + 2 as DayOfWeek,
    datediff(week, dateadd(month, datediff(month, 0, DateValue), 0), DateValue) + 1 as WeekOfMonth,
    datediff(week, dateadd(year, datediff(year, 0, DateValue), 0), DateValue) + 1 as WeekOfYear
    from Dates option (maxrecursion 0)
0 голосов
/ 11 апреля 2019

Создайте функцию и укажите в качестве входной переменной @dbdate varchar (8).

Пусть он вернет следующее:

RETURN (DATEDIFF(dd, -1, convert(datetime, @dbdate, 112)) % 7)+1;

Значение 112 - это стиль sql ГГГГММДД.

Это является детерминированным, потому что датированный символ не получает входные данные строки, если он получит строку, он больше не будет работать, потому что он внутренне преобразует его в объект datetime. Что не является детерминированным.

0 голосов
/ 04 февраля 2011

У предложенного решения есть одна проблема - он возвращает 0 для субботы. Предполагая, что мы ищем что-то совместимое с DATEPART(WEEKDAY), это проблема.

Ничего простого CASE оператор не исправит.

0 голосов
/ 26 ноября 2009

Разве вы не можете просто выбрать что-то вроде:

SELECT DATENAME(dw, GETDATE());
0 голосов
/ 26 ноября 2009

день недели? Почему бы вам просто не использовать DATEPART?

DATEPART(weekday, YEAR_DATE)
0 голосов
/ 26 ноября 2009

В sql уже есть встроенная функция:

SELECT DATEPART(weekday, '2009-11-11')

EDIT : Если вам действительно нужен детерминированный UDF:

CREATE FUNCTION DayOfWeek(@myDate DATETIME ) 
RETURNS int
AS
BEGIN
RETURN DATEPART(weekday, @myDate)
END
GO
SELECT dbo.DayOfWeek('2009-11-11')

Снова отредактируйте : это на самом деле неправильно, поскольку DATEPART(weekday) не является детерминированным.

UPDATE : DATEPART(weekday) является недетерминированным, поскольку опирается на DATEFIRST ( source ).
Вы можете изменить его с помощью SET DATEFIRST, но не можете вызывать его внутри сохраненной функции.

Я думаю, что следующим шагом будет сделать вашу собственную реализацию , используя в ней предпочитаемую DATEFIRST (и вообще не учитывая ее, например, Monday в качестве первого дня).

0 голосов
/ 26 ноября 2009

Не уверен, что вы ищете, но если это часть веб-сайта, попробуйте эту функцию php с http://php.net/manual/en/function.date.php

function weekday($fyear, $fmonth, $fday) //0 is monday
{
  return (((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, 7, 17,   2006))/(60*60*24))+700000) % 7;
}
...