如何在不使用sql循环的情况下过滤日期之间的行

时间:2015-04-18 10:05:53

标签: sql sql-server tsql sql-server-2014

我想在两个日期时间之间过滤来自单个表的行,并且那些过滤的行应该在一个日期之内,例如

我希望得到之间的所有行 (2015年3月16日6AM)和(2015年3月17日6AM)日期时间(2015年3月17日)日期和 (2015年3月17日6AM)和(2015年3月18日6AM)日期(2015年3月18日)日期等。

这是我的演示表

Id      Name    LogTime
1       mj      2015-03-16 01:28:03.257
2       mj      2015-03-16 05:28:03.257
3       mj      2015-03-16 06:28:03.257
4       mj      2015-03-16 18:28:03.257
5       mj      2015-03-17 01:28:06.677
6       mj      2015-03-17 06:28:06.677
7       mj      2015-03-17 16:28:07.460
8       mj      2015-03-17 07:28:03.257
9       mj      2015-03-18 01:28:08.193
10      mj      2015-03-18 05:28:03.257
11      mj      2015-03-18 06:28:03.257
12      mj      2015-03-18 18:28:03.257
13      mj      2015-03-19 01:28:06.677
14      mj      2015-03-19 06:28:06.677
15      mj      2015-03-19 16:28:07.460
16      mj      2015-03-19 07:28:03.257
17      mj      2015-03-20 01:28:08.193
18      mj      2015-03-20 05:28:03.257
19      mj      2015-03-20 06:28:03.257
20      mj      2015-03-20 18:28:03.257

下面是我正在使用的查询。

DECLARE @i INT = 1

DECLARE @from DATETIME
    ,   @to DATETIME

WHILE (@i <= 5)
BEGIN

    SET @from = CONVERT(DATETIME, CONVERT(VARCHAR(10), DATEADD(D, -@i, '2015-03-20'), 102) + ' 6:00:00')
    SET @to = CONVERT(DATETIME, CONVERT(VARCHAR(10), DATEADD(D, -@i + 1, '2015-03-20'), 102) + ' 6:00:00')

    SELECT  *,  @to AS 'FetchedOn'
    FROM Biometric
    WHERE LogTime BETWEEN @from AND @to
    ORDER BY LogTime

    SET @i = @i + 1

END

生成以下结果。

Id  Name    LogTime             FetchedOn
14  mj  2015-03-19 06:28:06.677 2015-03-20 06:00:00.000
16  mj  2015-03-19 07:28:03.257 2015-03-20 06:00:00.000
15  mj  2015-03-19 16:28:07.460 2015-03-20 06:00:00.000
17  mj  2015-03-20 01:28:08.193 2015-03-20 06:00:00.000
18  mj  2015-03-20 05:28:03.257 2015-03-20 06:00:00.000

Id  Name    LogTime             FetchedOn
11  mj  2015-03-18 06:28:03.257 2015-03-19 06:00:00.000
12  mj  2015-03-18 18:28:03.257 2015-03-19 06:00:00.000
13  mj  2015-03-19 01:28:06.677 2015-03-19 06:00:00.000

Id  Name    LogTime             FetchedOn
6   mj  2015-03-17 06:28:06.677 2015-03-18 06:00:00.000
8   mj  2015-03-17 07:28:03.257 2015-03-18 06:00:00.000
7   mj  2015-03-17 16:28:07.460 2015-03-18 06:00:00.000
9   mj  2015-03-18 01:28:08.193 2015-03-18 06:00:00.000
10  mj  2015-03-18 05:28:03.257 2015-03-18 06:00:00.000

Id  Name    LogTime             FetchedOn
3   mj  2015-03-16 06:28:03.257 2015-03-17 06:00:00.000
4   mj  2015-03-16 18:28:03.257 2015-03-17 06:00:00.000
5   mj  2015-03-17 01:28:06.677 2015-03-17 06:00:00.000

Id  Name    LogTime             FetchedOn
1   mj  2015-03-16 01:28:03.257 2015-03-16 06:00:00.000
2   mj  2015-03-16 05:28:03.257 2015-03-16 06:00:00.000

现在我希望在不使用循环的情况下获得相同的结果。 我正在使用sql 2014,有没有其他替代解决方案?

3 个答案:

答案 0 :(得分:3)

这是一些代码。想法是从测试数据中获得所有可能的不同范围。这就是CTE的回报:

st                      ed
2015-03-15 06:00:00.000 2015-03-16 06:00:00.000
2015-03-16 06:00:00.000 2015-03-17 06:00:00.000
2015-03-17 06:00:00.000 2015-03-18 06:00:00.000
2015-03-18 06:00:00.000 2015-03-19 06:00:00.000
2015-03-19 06:00:00.000 2015-03-20 06:00:00.000

在此之后,它简单地连接数据落在这些范围之间的条件:

DECLARE @t TABLE(ID INT, D DATETIME)

INSERT INTO @t VALUES
(1       ,'2015-03-16 01:28:03.257'),
(2       ,'2015-03-16 05:28:03.257'),
(3       ,'2015-03-16 06:28:03.257'),
(4       ,'2015-03-16 18:28:03.257'),
(5       ,'2015-03-17 01:28:06.677'),
(6       ,'2015-03-17 06:28:06.677'),
(7       ,'2015-03-17 16:28:07.460'),
(8       ,'2015-03-17 07:28:03.257'),
(9       ,'2015-03-18 01:28:08.193'),
(10      ,'2015-03-18 05:28:03.257'),
(11      ,'2015-03-18 06:28:03.257'),
(12      ,'2015-03-18 18:28:03.257'),
(13      ,'2015-03-19 01:28:06.677'),
(14      ,'2015-03-19 06:28:06.677'),
(15      ,'2015-03-19 16:28:07.460'),
(16      ,'2015-03-19 07:28:03.257'),
(17      ,'2015-03-20 01:28:08.193'),
(18      ,'2015-03-20 05:28:03.257'),
(19      ,'2015-03-20 06:28:03.257'),
(20      ,'2015-03-20 18:28:03.257')

;
WITH    cte
          AS ( SELECT DISTINCT
                        DATEADD(HOUR, -18, CAST(CAST(D AS DATE) AS DATETIME)) AS st ,
                        DATEADD(HOUR, 6, CAST(CAST(D AS DATE) AS DATETIME)) AS ed
               FROM     @t
             )
    SELECT  t.ID, t.D, c.ed
    FROM    cte c
            JOIN @t t ON t.D BETWEEN c.st AND c.ed

输出:

ID  D                       ed
1   2015-03-16 01:28:03.257 2015-03-16 06:00:00.000
2   2015-03-16 05:28:03.257 2015-03-16 06:00:00.000
3   2015-03-16 06:28:03.257 2015-03-17 06:00:00.000
4   2015-03-16 18:28:03.257 2015-03-17 06:00:00.000
5   2015-03-17 01:28:06.677 2015-03-17 06:00:00.000
6   2015-03-17 06:28:06.677 2015-03-18 06:00:00.000
7   2015-03-17 16:28:07.460 2015-03-18 06:00:00.000
8   2015-03-17 07:28:03.257 2015-03-18 06:00:00.000
9   2015-03-18 01:28:08.193 2015-03-18 06:00:00.000
10  2015-03-18 05:28:03.257 2015-03-18 06:00:00.000
11  2015-03-18 06:28:03.257 2015-03-19 06:00:00.000
12  2015-03-18 18:28:03.257 2015-03-19 06:00:00.000
13  2015-03-19 01:28:06.677 2015-03-19 06:00:00.000
14  2015-03-19 06:28:06.677 2015-03-20 06:00:00.000
15  2015-03-19 16:28:07.460 2015-03-20 06:00:00.000
16  2015-03-19 07:28:03.257 2015-03-20 06:00:00.000
17  2015-03-20 01:28:08.193 2015-03-20 06:00:00.000
18  2015-03-20 05:28:03.257 2015-03-20 06:00:00.000

答案 1 :(得分:2)

您可以根据LogTime字段的时间部分计算FetchedOn:

SELECT B.*,  dateadd(day, (iif(cast(LogTime as time) < '06:00:00', 0, 1)), cast(LogTime as date)) + cast('06:00:00' as datetime) as FetchedOn
from Biometric B
ORDER BY FetchedOn DESC, LogTime

<强>更新

计算FetchedOn的简单公式,以及为SQL2012 +兼容性添加的datetime转换。

SELECT B.*, cast(cast(dateadd(hour, +18, LogTime) as date) as datetime) + cast('06:00:00' as datetime) as FetchedOn
from Biometric B
ORDER BY FetchedOn DESC, LogTime

答案 2 :(得分:1)

这是一个适用于SQL Server 2005+的公式:DATEADD(HOUR, 6, CAST(CAST(DATEADD(HOUR, 18, A.LogTime) AS DATE) AS DATETIME))。我们只是向LogTime添加了18个小时,滥用CAST来截断时间段,然后将6个小时添加到此值。

完整示例(基于原创):

DECLARE @i INT = 5
DECLARE @endDate DATETIME = '2015-03-20 06:00:00.000';
DECLARE @startDate DATETIME = DATEADD(d, -@i, @endDate);

SELECT *
FROM (
    SELECT A.Id
         , A.LogTime
         , FetchedOn = DATEADD(HOUR, 6, CAST(CAST(DATEADD(HOUR, 18, A.LogTime) AS DATE) AS DATETIME))
    FROM(VALUES
        (1, '2015-03-16 01:28:03.257')
      , (2, '2015-03-16 05:28:03.257')
      , (3, '2015-03-16 06:28:03.257')
      , (4, '2015-03-16 18:28:03.257')
      , (5, '2015-03-17 01:28:06.677')
      , (6, '2015-03-17 06:28:06.677')
      , (7, '2015-03-17 16:28:07.460')
      , (8, '2015-03-17 07:28:03.257')
      , (9, '2015-03-18 01:28:08.193')
      , (10, '2015-03-18 05:28:03.257')
      , (11, '2015-03-18 06:28:03.257')
      , (12, '2015-03-18 18:28:03.257')
      , (13, '2015-03-19 01:28:06.677')
      , (14, '2015-03-19 06:28:06.677')
      , (15, '2015-03-19 16:28:07.460')
      , (16, '2015-03-19 07:28:03.257')
      , (17, '2015-03-20 01:28:08.193')
      , (18, '2015-03-20 05:28:03.257')
      , (19, '2015-03-20 06:28:03.257')
      , (20, '2015-03-20 18:28:03.257')
    ) A (Id, LogTime)
    WHERE A.LogTime BETWEEN @startDate AND @endDate
) A
ORDER BY A.FetchedOn DESC, A.LogTime ASC;