【问题标题】:Deterministic scalar function to get day of week for a date用于获取日期的星期几的确定性标量函数
【发布时间】:2010-12-20 16:29:29
【问题描述】:

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

【问题讨论】:

  • 请展示您的一些代码,以及您打算如何使用它。

标签: sql-server user-defined-functions


【解决方案1】:

好吧,我想通了..

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

【讨论】:

  • 我刚刚发布了你应该让你的实施:) +1 为你
  • 不,@brad 不是...请参阅上面评分较高的答案。
【解决方案2】:

与上述解决方案略有相似的方法,但只是一种可用于函数内部或内联计算列的单行。

假设:

  1. 您之前没有日期 1899-12-31(星期日)
  2. 你想模仿@@datefirst = 7
  3. @dt 是 smalldatetime、datetime、 日期或 datetime2 数据类型

如果您希望它有所不同,请将日期“18991231”更改为您希望等于 1 的工作日日期。convert() 函数是使整个事情正常工作的关键 - 转换不会做这个伎俩:

((datediff(day, convert(datetime, '18991231', 112), @dt) % 7) + 1)

【讨论】:

  • 如果您坚持 1899-12-31 日期,您甚至可以跳过转换 ((datediff(day, -1, @dt) % 7) + 1)
  • 这在函数中工作得很好。在您尝试更改表并设置计算列之前,您会得到“无法持久化,因为该列是非确定性的。”。这是因为 datediff 将字符串转换为 datetime 对象 - 这种转换杀死了在持久列中使用它的确定性能力......不幸的是。改用这个(用@,..替换“at”): SET (at)datediff = (DATEDIFF(dd, -1, convert(datetime, (at)dbdate, 112)) % 7)+1;跨度>
【解决方案3】:

我知道这篇文章已经过时了,但我试图做类似的事情并想出了一个不同的解决方案,并认为我会为后代发帖。另外我做了一些搜索,并没有找到关于这个问题的太多内容。

在我的例子中,我试图使用计算列 PERSISTED,它要求计算是确定性的。我使用的计算是:

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

我们的想法是找出一个已知的星期日,您知道该星期日将在您的表中任何可能的日期之前发生(在本例中为 2010 年 1 月 3 日),然后计算自该星期日以来的天数的模 7 + 1。

问题在于,在函数调用中包含文字日期足以将其标记为非确定性的。您可以通过使用整数 0 来表示纪元来解决这个问题,对于 SQL Server,它是 1900 年 1 月 1 日,星期日。

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

当 datefirst 设置为 7(美国的默认值)时,+1 只是使结果与 datepart(dw,[datecolumn]) 相同,它将星期日设置为 1,星期一设置为 2,等等

我还可以将它与 case [thatComputedColumn] 一起使用,当 1 时为“星期日”,当 2 时为“星期一”……等等。字词更复杂,但具有确定性,这是我的环境中的要求。

【讨论】:

  • 这是一个有趣的想法,但行不通。在我的测试中,我必须让你的0 成为4。我猜这与今年的第一天有关,那是星期三。我不确定你在 2013 年写这篇文章时这是如何工作的,因为那一年是从星期二开始的。 @scottE 的回答说明了这个 stackoverflow.com/a/5109557/444382
  • 根据确定性的定义 (msdn.microsoft.com/en-us/library/ms178091.aspx),这个解决方案是确定性的。您可能得到不同结果的原因是您的数据库设置 firstdate (msdn.microsoft.com/en-us/library/ms178091.aspx)。如果计算列不是确定性的,则无法使其持久化,并且该计算可以持久化
  • 您在解决方案中声明您假设@@datefirst 与我相同,所以我不确定是什么导致这对我不起作用。
  • 在我的 MSSQL 实例上,使用@@datefirst = 7,我必须使用((datediff(dd, 0, datecolumn) + 1) % 7) + 1 才能获得与datepart(weekday, datecolumn) 相同的结果。
  • Ed Avis,这是因为答案错误地指出 1990 年 1 月 1 日是星期日。那是一个星期一。只需执行SELECT DATEPART(dw,'1/1/1900') 并亲自查看。我希望阅读此答案的人不要使用“官方”答案,而是使用您更正的公式。
【解决方案4】:

取自Deterministic scalar function to get week of year for a date

;
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)

【讨论】:

    【解决方案5】:

    sql 中已经有一个内置函数可以做到这一点:

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

    编辑: 如果您确实需要确定性 UDF:

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

    再次编辑:这实际上是错误的,因为DATEPART(weekday) 不是确定性的。

    更新DATEPART(weekday) 是不确定的,因为它依赖于 DATEFIRST (source)。
    您可以使用SET DATEFIRST 更改它,但不能在存储函数中调用它。

    我认为下一步是make your own implementation,在其中使用您首选的 DATEFIRST(根本不考虑它,例如使用 Monday 作为第一天)。

    【讨论】:

    • DatePart 是不确定的,所以这是不可能的
    【解决方案6】:

    建议的解决方案有一个问题 - 它在星期六返回 0。假设我们正在寻找与 DATEPART(WEEKDAY) 兼容的东西,这是一个问题。

    不过,没有什么是简单的CASE 语句解决不了问题的。

    【讨论】:

      【解决方案7】:

      创建一个函数,并将@dbdate varchar(8) 作为输入变量。

      让它返回以下内容:

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

      值 112 是 sql 样式 YYYYMMDD。

      这是确定性的,因为 datediff 不接收字符串输入,如果它要接收字符串,它将不再工作,因为它在内部将其转换为 datetime 对象。这不是确定性的。

      【讨论】:

        【解决方案8】:

        不确定您在寻找什么,但如果这是网站的一部分,请尝试http://php.net/manual/en/function.date.php 中的这个 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;
        }
        

        【讨论】:

        • 谢谢,但我正在寻找 SQL Server 标量函数
        【解决方案9】:

        星期几?为什么不直接使用 DATEPART?

        DATEPART(weekday, YEAR_DATE)
        

        【讨论】:

        • DatePart 是不确定的,所以这是不可能的
        【解决方案10】:

        你不能用类似的方式选择它:

        SELECT DATENAME(dw, GETDATE());
        

        【讨论】:

        • 问题是获取 DayOfWeek 而不是名称。
        猜你喜欢
        • 2017-11-07
        • 2011-03-02
        • 1970-01-01
        • 1970-01-01
        • 2011-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多