【问题标题】:DATEPART: specifying first day of week when SET DATEFIRST cannot be usedDATEPART:指定不能使用 SET DATEFIRST 时的第一天
【发布时间】:2024-04-24 19:35:01
【问题描述】:

我正在 ReportServer 中创建一些报告(动态列表类型),其中之一是从 SQL Server 检索过去一周事件的信息。

在 SSMS 中,这很简单,我得到所需的信息(按时间)

... WHERE DATEPART(week, event_date) = DATEPART(week, DATEADD(d, -5, GETDATE()))

前面有一个

SET DATEFIRST 1;

声明,以便将星期一设置为一周的第一天。

很遗憾,ReportServer 不接受SET DATEFIRST 语句作为查询的一部分,因此在将其省略后,返回的数据范围是周日到周六,而不是周一到周日。我无法对数据库(或服务器,就此而言)进行全局更改。

我怎样才能解决这个问题?

【问题讨论】:

  • 简单的答案是部署包含SET DATEFIRST 更改的存储过程。你能说服你的 DBA 允许这样做吗?请注意,SET DATEFIRST 的范围仅限于当前会话,因此您可能还希望在更改之前保存 @@DATEFIRST 的值并在查询后恢复该值 - 否则您可能会影响其他查询重用相同的连接。
  • 感谢@AlwaysLearning,但这是我的问题的一部分:除非它们真的与流程相关,否则我无法对数据库进行任何更改;恐怕报告不属于该类别
  • 最好的办法可能是引入物理数字/日期表,在很多地方都很帅。 this example

标签: sql-server tsql reportserver


【解决方案1】:

计算所需的一周的第一天,然后在 where 子句中使用周日期范围,例如

declare @FirstDayOfWeek date, @Now date = getdate();

select @FirstDayOfWeek = dateadd(day, -1*(case when datepart(weekday, @Now) > 1 then datepart(weekday, @Now)-2 else 6 end), @Now);

select *
from dbo.MyTable
where event_date >= @FirstDayOfWeek and event_date < dateadd(week, 1, @FirstDayOfWeek);

当然你也可以在查询中嵌入计算,例如

select *
from dbo.MyTable
cross join (
  select dateadd(day, -1*(case when datepart(weekday, getdate()) > 1 then datepart(weekday, getdate())-2 else 6 end), getdate()) FirstDayOfWeek
) D
where event_date >= D.FirstDayOfWeek and event_date < dateadd(week, 1, D.FirstDayOfWeek);

注意:您可以将日期计算复制到 where 子句中并避免使用 join,但我不喜欢重复计算。

【讨论】:

  • 谢谢@Dale Burrell,但我什至不尝试,我可以告诉你,Reportserver 不允许我声明任何内容,仅允许我设置任何内容
  • @ChrisBE 你确定吗? declare 在权限方面与set 非常不同。我会试一试。它适用于 SSRS。
  • @ChrisBE 当然你不需要声明它,你可以计算它作为查询的一部分。
  • @ChrisBE,顺便提一下:您可以使用 CTE 模拟已声明的变量:WITH myVariables(VariableA) AS(SELECT 'names') SELECT * FROM myVariables v CROSS JOIN sys.objects o WHERE o.[name] like '%' + v.VariableA + '%'。这将使用类似于变量的'names'...
【解决方案2】:

您可以使用@@DATEFIRST 尝试一下。这是一个系统变量,您无需在内联/即席查询中进行任何声明或设置即可使用:

--今天是星期天

DECLARE @OneSunday DATE='20190929';

--你的陈述在不同文化中的回报不同

SET LANGUAGE ENGLISH;
SELECT DATEPART(week, @OneSunday); --<-- returns 40
SELECT @@DATEFIRST;                --<-- returns 7  

SET LANGUAGE GERMAN;
SELECT DATEPART(week, @OneSunday); --<-- returns 39
SELECT @@DATEFIRST;                --<-- returns 1

如您所见,系统变量@@DATEFIRST 反映了当天的索引,该索引由文化设置。你可以用它来做一些修正运算。

SET LANGUAGE ENGLISH;
SELECT DATEPART(week, DATEADD(DAY,(@@DATEFIRST % 7),@OneSunday));

SET LANGUAGE GERMAN;
SELECT DATEPART(week, DATEADD(DAY,(@@DATEFIRST % 7),@OneSunday));

现在两者都返回 40。

我使用% 7 获取0,其中@@DATEFIRST 返回7

【讨论】:

  • 谢谢!你让我走上了正确的道路。我修改了您的建议以适合我的查询,并且以下工作完美:DATEPART(week, DATEADD(d, CASE WHEN @@DATEFIRST % 7 = 0 THEN -1 ELSE 0 END , event_date)) = DATEPART(week, DATEADD(d, -5, GETDATE()))
  • @ChrisBE 很高兴为您提供帮助。只是一个小小的提示:在您的构造中,您不需要%7。您可以测试@@DATEFIRST=7% 的技巧使计算变得更容易......
【解决方案3】:

由于您想要以Monday 开头的星期,因此您使用Monday 的参考日期(如1900-01-01)并计算一周的开始和结束日期

begin_of_week = dateadd(day, datediff(day, '1900-01-01', [date]) / 7 * 7, '1900-01-01')

并获取周末日期,

end_of_week = dateadd(day, datediff(day, '1900-01-01', [date]) / 7 * 7 + 7, '1989-12-31')

所以把它放到你的查询中(我假设你的 event_date 可能包含时间组件,因此,上限条件是less than&lt;

WHERE event_date >= dateadd(day, datediff(day, '1900-01-01', getdate()) / 7 * 7, '1900-01-01')
AND   event_date <  dateadd(day, datediff(day, '1900-01-01', getdate()) / 7 * 7 + 7, '1900-01-01')

【讨论】:

最近更新 更多