1

我有一个包含一个日期时间列的表。我只需要返回最近 6 个月的行。这可以通过

WHERE CloseTime >= DATEADD(Month, DATEDIFF(Month, 0, DATEADD(m, - 6, CURRENT_TIMESTAMP)), 0)

这让我得到了我开始这个脚本的月份的数据 + 上个月的 6 个。因此,例如,如果我今天运行此脚本,我将获得本月 + 之前所有月份的数据,直到 4 月 (04)。

现在我需要修改条件,所以如果我今天运行脚本,将仅获取 03-09 月份的数据,不包括本月 (10) 中的日子。

请问有什么建议吗?

4

4 回答 4

6

如果你想得到前 6 个月,不管今天是第 1 天、第 3 天、第 9 天、第 29 天等等,那么只需减去 7 个月。这是一种方法:将月份的第一天放入变量中,然后在查询中使用开放式范围。

DECLARE @ThisMonth DATETIME;
SET @ThisMonth = DATEADD(MONTH, DATEDIFF(MONTH, '19000101', GETDATE()), '19000101');

SELECT...
WHERE CloseTime >= DATEADD(MONTH, -7, @ThisMonth)
  AND CloseTime <  @ThisMonth;

您也可以使用0代替,'19000101'但我更喜欢显式日期而不是隐式速记(这是一个很难打破的习惯)。

如果您真的不喜欢变量,那么您可以通过重复表达式来计算本月的第一天(并在范围的开头,从月数中减去 7)来使查询更加复杂:

SELECT...
WHERE CloseTime >= DATEADD(MONTH, DATEDIFF(MONTH, '19000101', GETDATE())-7, '19000101')
  AND CloseTime <  DATEADD(MONTH, DATEDIFF(MONTH, '19000101', GETDATE()), '19000101');

呸。变量使这更加整洁。

于 2013-10-10T13:24:47.340 回答
1

创建查询时,您不想在搜索列上使用函数,因为它会导致全表扫描。

该解决方案有效,并且应该获取 CloseTime 上的任何索引。

-- Get me data in months 3 (mar) to 9 (sep) of this year
select 
    *
from 
    my_table 
where 
    CloseTime between 
    DATEADD(d, -1, '03-01-2013') and DATEADD(d, +1, '09-20-2013')

如果表很小并且全表扫描不是问题,一个简单的解决方案是使用 MONTH 函数。

-- Get me data in months 3 (mar) to 9 (sep) of this year
select 
    *
from 
    my_table 
where 
    MONTH(CloseTime) IN (3,4,5,6,7,8,9) and YEAR(CloseTime) = 2013
于 2013-10-10T13:25:12.323 回答
0

我看了亚伦的文章。这是一本非常好的读物。

我想知道自那篇文章以来是否有一些新功能没有经过测试。

如果您使用的是 2012,为什么不使用格式功能?从逻辑上讲,您希望日期变量为 01 天。查询计划仍会进行聚集索引扫描。

让我们看看这个解决方案如何使用 Aaron 的测试数据库进行叠加。

-- Use the sample
use [DateTesting]
go

-- Johns - log by 6 months
CREATE PROCEDURE dbo.Johns_LogBy6Months
   @date SMALLDATETIME
AS
BEGIN
   SET NOCOUNT ON;

   DECLARE @cmp_date SMALLDATETIME = format(@date, 'yyyyMM01');
   DECLARE @c INT;

   SELECT @c = COUNT(*)
        FROM dbo.SomeLogTable
        WHERE DateColumn >= dateadd(m, -7, @cmp_date) 
        AND DateColumn < @cmp_date
END
GO

-- Aarons - log by 6 months
CREATE PROCEDURE dbo.Aarons_LogBy6Months
   @date SMALLDATETIME
AS
BEGIN
   SET NOCOUNT ON;

   DECLARE @c INT;
   DECLARE @cmp_date SMALLDATETIME = DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @date), '19000101');

   SELECT @c = COUNT(*)
        FROM dbo.SomeLogTable
        WHERE 
        DateColumn >=  dateadd(m, -7, @cmp_date) 
        AND DateColumn <  @cmp_date
END
GO

让我们对函数进行 1000 次调用。

-- Sample calls x 1000  
PRINT CONVERT(char(23), GETDATE(), 121);
GO

EXEC dbo.Johns_LogCountByDay @date = '20091005';
GO 1000

PRINT CONVERT(char(23), GETDATE(), 121);
GO

EXEC dbo.Aarons_LogBy6Months @date = '20091005';
GO 1000

PRINT CONVERT(char(23), GETDATE(), 121);
GO

这是执行时间。

2013-10-10 11:58:49.547

Beginning execution loop
Batch execution completed 1000 times.

2013-10-10 11:58:52.837

Beginning execution loop
Batch execution completed 1000 times.

2013-10-10 11:58:55.883

总之,调用新的 format() 函数和隐式转换为小日期时间比使用两个字符串(日期)文字的 dateadd() 和 datediff() 需要更多的时间。

format() 解决方案对我来说似乎更直观或自我记录。时间差为 3.3 秒对 3.0 秒。

我必须让速度测试胜过 Aaron 的解决方案。坚持日期变量的不等式比较。他们更快。

简而言之,我将不得不改掉我的坏习惯。

于 2013-10-10T15:58:52.927 回答
0

我也有类似的要求。我需要过去六个月和接下来六个月的约会。但就像你一样,我需要整整几个月。所以一个简单的 getdate +- 180 不会做。

我采取了更简单的方法。我得到年份和月份并将其转换为 201912 之类的数字。然后执行 between 子句。它充满活力,我得到了整整几个月。

我确信有更复杂的方法,但这就是我想出的。

WHERE Year([ApptDate])*100 + Month([ApptDate]) between Year(getdate()-180)*100 + Month(getdate()-180) and Year(getdate()+180)*100 + Month(getdate()+180)
于 2020-06-08T13:52:37.250 回答