简体   繁体   English

SQL Server 2005查询DateDate

[英]SQL Server 2005 Query DateDate

WHERE
   StartDate BETWEEN 
       CAST(FLOOR(CAST(DATEADD("month", - 12, ISNULL(RunDate.testdate, GETDATE()) - DAY(ISNULL(RunDate.testdate, GETDATE())) + 1) AS FLOAT)) AS DATETIME)
       AND 
       CAST(FLOOR(CAST(ISNULL(RunDate.testdate, GETDATE()) - DAY(ISNULL(RunDate.testdate, GETDATE())) + 1 AS FLOAT)) AS DATETIME)

/************************************************* 
SQL Server 2005 (9.0 SP4)
StartDate is of TYPE DATETIME
StartDate date looks like 2012-07-05 12:45:10.227
RunDate is a TABLE with no records
RunDate.testdate is always NULL
**************************************************/

This WHERE clause works and produces the expected results for what the business expects but seems excessively untidy. WHERE子句可以正常工作并产生业务期望的预期结果,但看起来过于混乱。

All that this is attempting to achieve is to bring across all records for the last 12 months, when run in July of any year. 试图实现的所有目的就是将任何一年的7月运行的过去12个月的所有记录汇总在一起。 So records from 1 July 2013 to 30 June 2014. 因此记录了2013年7月1日至2014年6月30日。

Here is another expression: 这是另一个表达式:

where startdate > cast(dateadd(year, -1, getdate() - day(getdate()) as date) and
      startdate <= cast(getdate() - day(getdate()) as date)

When you subtract a number from a datetime it is treated as a number of day, which can be easier to read. 当您从datetime减去数字时,将其视为天数,这更易于阅读。

EDIT: 编辑:

Oh, that's 2005, not 2008. The date logic goes like this . 哦,那是2005年,而不是2008年。日期逻辑是这样的。 . . :

where startdate > cast(dateadd(year, -1, dateadd(day, - day(getdate()), getdate()) as date) and
      startdate <= cast(dateadd(day, - day(getdate()), getdate())

But you will have times on the date. 但是您会在约会上有时间。 There actually isn't an easy way to convert this to a date. 实际上,没有简单的方法可以将其转换为日期。 You can do this more readily with a subquery: 您可以使用子查询更轻松地执行此操作:

from (select dateadd(day, 0, datediff(day, 0, getdate())) as today) const
where startdate > dateadd(year, -1, dateadd(day, - day(today), today) and
      startdate <= cast(dateadd(day, - day(today), today)

My first question would be: what do you expect to see if someone executes this at any time other than July? 我的第一个问题是:您希望看到有人在七月以外的任何时间执行此操作吗?

Never assume something outside your control. 切勿承担超出您控制范围的事情。 You have no control over when this will be executed so write it so it will always execute during the correct time period no matter what. 您无法控制何时执行此操作,因此请对其进行编写,以使其始终在正确的时间段内执行,无论如何。

Because there are a number of ways to do this based on DBMS and even version of DBMS (SS 2005 vs 2008, for example) here it is in human-speak: 因为有许多方法可以基于DBMS甚至是DBMS版本(例如,SS 2005 vs 2008)来完成此任务,所以这里是有人说的:

 -- Get the difference, in years, between sysdate and the base date
 -- Add that to the base date. You now have Jan 1 of whatever year it is.
 -- Add 6 months. You now have Jul 1 of that year.
 -- Add 1 year and subtract 1 day. You now have Jun 30 of the following year.

Your query will now operate on the correct range no matter when it is executed. 现在,无论何时执行查询,查询都将在正确的范围内运行。 Of course, if it is executed in, say, February, the results will not be complete because some of the range will be in the future. 当然,如果在2月执行,则结果将不完整,因为某些范围会在将来出现。 But the results will always be correct for the time it is executed. 但是结果在执行时始终是正确的。

One adjustment you could make is that, if executed before Jul 1 of any year, it generates a range for the most recent complete year. 您可以进行的一种调整是,如果在任何一年的7月1日之前执行该调整,则会生成最近一个完整年份的范围。 So Jul 1 2012 to Jun 30 2013 if executed anytime before Jul 1 2014. 因此,如果2014年7月1日之前的任何时间执行,则为2012年7月1日至2013年6月30日。

[Edit: I suppose since I brought it up, I should specify how that could be done. [编辑:我想自从提出以来,我应该指定如何做到这一点。 In the first step above, where it says sysdate , change it to (sysdate minus 6 months) .] 在上面的第一步中,将其显示为sysdate ,将其更改为(sysdate minus 6 months)

It really doesn't matter which way you design it, as long as your users know what to expect. 只要您的用户知道期望什么,实际上用哪种方式设计都没关系。 But whatever you do, don't let your query depend on users' ability to execute it at the right time. 但是无论您做什么,都不要让查询取决于用户在正确的时间执行查询的能力。

If you abandon the x BETWEEN a AND b pattern in favour of one that could roughly be expressed as 如果您放弃了x BETWEEN a AND b模式之间 ,而将其大致表示为

x BEGINS WITH a
  ENDS BEFORE b

(which, of course, is not valid SQL syntax but x >= a AND x < b would be a decent replacement), you will end up with both a and b having same granularity (specifically, months, because each of the two points would be the beginning of a month). (当然,这不是有效的SQL语法,但x >= a AND x < b将是一个不错的替代),您将最终得到ab具有相同的粒度(具体来说是几个月),因为两点各将是一个月的开始)。

In this case it is very opportune because it will allow you to factorise calculation of both a and b nicely. 在这种情况下,这是非常合适的,因为它将允许您很好地分解ab计算。 But before I get there, let me tell (or remind) you about this date/time truncation technique in SQL Server : 但是在到达那里之前,让我告诉(或提醒)您有关SQL Server中这种日期/时间截断技术的信息

DATEADD(unit, DATEDIFF(unit, SomeFixedDate, YourDateTime), SomeFixedDate)

In your case, the unit would be MONTH . 在您的情况下, unitMONTH As for SomeFixedDate , there is an established practice of using 0 there for brevity, 0 representing the date of 1900-01-01 1 . 对于SomeFixedDate ,有一种简便的做法是使用0表示简洁,0表示1900-01-01 1的日期。

So, using this technique, the beginning of the current month would be calculated as 因此,使用此技术,本月初将计算为

DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)

That would be the end of your interval. 那将是您间隔的尽头。 The beginning of the interval would be same except you would subtract 12 from the DATEDIFF's result, and so the complete condition to match the range would look like this: 间隔的开始将是相同的,除了您将从DATEDIFF的结果中减去12,因此匹配范围的完整条件如下所示:

WHERE StartDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - 12, 0)
  AND StartDate <  DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)

If that still looks a mouthful to you, you could try getting rid of the repetition of DATEDIFF(MONTH, 0, GETDATE()) . 如果您仍然觉得这很麻烦,则可以尝试摆脱DATEDIFF(MONTH, 0, GETDATE())的重复。 You could view it as some abstract month number and reference it as such in both DATEADD calls. 您可以将其视为某个抽象的月份号,并在两个DATEADD调用中均将其引用。 The CROSS APPLY syntax would help you with that: CROSS APPLY语法将帮助您:

…
CROSS APPLY (
  SELECT DATEDIFF(MONTH, 0, GETDATE())
) AS x (MonthNo)
WHERE StartDate >= DATEADD(MONTH, x.MonthNo-12, 0)
  AND StartDate <  DATEADD(MONTH, x.MonthNo   , 0)

Yes, that seems factorised all right. 是的,这似乎可以分解。 However, it might not lend itself well to readability. 但是,它可能无法很好地提高可读性。 Someone (even yourself, at some later point) might look at it and say: "Huh, MonthNo ? What in the world is that? Ah, that number…" If I were to factorise your interval calculation, I might consider this instead: 有人(甚至是您自己,在以后的某个时间)可能会看着它说:“ MonthNoMonthNo ?那到底是什么?啊, 那个数字……”如果我要考虑间隔计算,我可能会考虑以下几点:

…
CROSS APPLY (
  SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)
) AS x (ThisMonth)
WHERE StartDate >= DATEADD(YEAR, -1, x.ThisMonth)
  AND StartDate <  x.ThisMonth

I admit, though, that readability is not a perfectly objective criterion and sometimes one chooses to write one way or the other based on one's personal tastes/preferences. 不过,我承认,可读性并不是一个完美的客观标准,有时人们会根据自己的个人喜好/偏好选择一种书写方式。


1 Although hard-coding a date as an integer value is not a normal practice, I would argue that this truncation technique is a special case where using 0 in place of a datetime value should be okay: that usage appears to be so widespread that it may by now have become a "fixed expression" among those using it and is, therefore, unlikely to cause much confusion. 1尽管将日期硬编码为整数值不是正常做法,但我认为这种截断技术是一种特殊情况,其中使用0代替日期时间值应该可以:这种用法似乎非常普遍,以至于可能到现在已经成为使用它的人中的“固定表达”,因此,不太可能引起混乱。

In SQL Server 2005, you can use DATEADD and DATEDIFF to remove the time component and compute exact month boundaries: 在SQL Server 2005中,可以使用DATEADDDATEDIFF删除时间部分并计算确切的月边界:

WHERE 
    startdate >= DATEADD(year, -1, DATEADD(month, DATEDIFF(month, '', GETDATE()), ''))
    AND startdate > DATEADD(month, DATEDIFF(month, '', GETDATE()), '')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM