SQL选择daterange的第一个值和最后一个值

时间:2016-05-09 14:50:44

标签: sql sql-server tsql

我是SQL查询的新手,我需要从这个查询开始进行连接:

SELECT b.Name, a.*
FROM a INNER JOIN b ON a.VId = b.Id
WHERE  b.SId = 100
       AND a.Day >= '2016-05-05'
       AND a.Day < '2016-05-09';

并向第一个选定数据添加2个列(表c中的SCap和ECap)。 根据我的尝试,我的代码如下所示:

    SELECT b.Name,a.*,
       c.MaxDay,
       c.Cap,
FROM   a INNER JOIN b
       ON a.VId = b.Id
       INNER JOIN
       (SELECT   VId,
                 MAX(TimestampLocal) AS MaxDay,
                 CAST (TimestampLocal AS DATE) AS Day,
                 Cap,
        FROM Info
        GROUP BY VId, 
                 CAST (TimestampLocal AS DATE), 
                     Cap) AS c
       ON a.VId = c.VId
          AND a.Day = c.Day
WHERE  b.SId = 33
       AND a.Day >= '2016-05-05'
       AND a.Day < '2016-05-09';

但是我得到的行多于所需的行数。

我需要在日期范围内为给定车辆提供最早和最新的TimestampLocal。这将来自Info中的两个记录,每个记录都有Cap的正确值。 例如: 我有两个名称,表格信息在表格信息中2天(名称1),名称2只有一天:

- Day 1 2016-05-07:
    - Name 1: Values at 2:45, 10:10 and 3.10
    - Name 2: Values at 5:13 and 8:22
- Day 2 2016-05-09:
    - Name 1: Values at 4:13, 6:15 and 9:20

我需要显示(如果我选择日期范围:2016-05-052016-05-09),名称1为SCap,2016-05-07中的值为2:45,而ECap则为2016年的值 - 05-09从9:20开始,名称2为SCap,2016-05-07从5:13开始,而ECap从2016-05-07开始从8:22开始。这应该以2行显示。

有没有办法可以将这两列添加到我的查询中,而不会为同一个名称添加更多行?

EDIT! 表a我有:

VId   | Day
5251  | 05/09/2016
5382  | 05/09/2016

表b:

Id    | Name
5251  | N1
5382  | N2

表格信息:

VId   | TimestampLocal                      |  Cap
5251  | 2016-05-09 11:33:46.2000000 +03:00  |   0
5251  | 2016-05-09 11:37:11.4000000 +03:00  |   7
5251  | 2016-05-09 11:38:11.4000000 +03:00  |   4
5251  | 2016-05-09 11:39:11.7000000 +03:00  |   2
5382  | 2016-05-09 09:30:56.6000000 -04:00  |   5
5382  | 2016-05-09 09:31:56.6000000 -04:00  |   3

我需要显示 - 如果我选择从2016-05-032016-05-10的日期范围:

Id    | Name | SCap | ECap
5251  | N1   | 0    | 2
5382  | N2   | 5    | 3

3 个答案:

答案 0 :(得分:0)

您可以CTE使用ROW_NUMBER来检索这两条记录:

;WITH Info_CTE AS (
   SELECT   VId,            
            CAST (TimestampLocal AS DATE) AS Day,
            Cap,
            ROW_NUMBER() OVER (PARTITION BY VId 
                               ORDER BY TimestampLocal) AS rn1,
            ROW_NUMBER() OVER (PARTITION BY VId 
                               ORDER BY TimestampLocal DESC) AS rn2
   FROM Info
)
SELECT b.Name,
       a.*,
       c.Day,
       c.Cap
FROM  a 
INNER JOIN b ON a.VId = b.Id
INNER JOIN Info_CTE AS c ON a.VId = c.VId AND 
                            a.Day = c.Day AND
                            1 IN (c.rn1, c.rn2)
WHERE  b.SId = 33
       AND a.Day >= '2016-05-05'
       AND a.Day < '2016-05-09';

如果您希望将ECapSCap作为单独的列,而不是

c.Cap

你可以使用:

CASE WHEN c.rn1 = 1 THEN c.Cap END AS ECap,
CASE WHEN c.rn2 = 1 THEN c.Cap END AS SCap

答案 1 :(得分:0)

首先,获取给定日期的每个id的最大和最小时间戳。然后连接两次,一次使用最大时间戳值,然后使用每个id到tblinfo的最小时间戳值,以获取这些行的开始和结束值。

添加带有日期过滤器的where子句,以过滤特定日期。

Sample demo

with maxandmin as 
(select vid
        ,max(timestamplocal) maxtime
        ,min(timestamplocal) mintime
 from tblinfo
 group by vid,cast(timestamplocal as date))
select m.vid,b.name,i1.cap as Scap,i2.cap as Ecap
from maxandmin m 
join tblinfo i1 on m.vid=i1.vid and m.mintime = i1.timestamplocal
join tblinfo i2 on m.vid=i2.vid and m.maxtime = i2.timestamplocal
join tableb b on b.id=m.vid

答案 2 :(得分:0)

经过一些进一步的调查后,我找到了一个解决方案,这就是我设法做的事情:

SELECT      b.Id,
            b.Name,
            c.S_Time,
            c.E_Time,
            s.Cap AS S_Cap,
            e.Capa AS E_Cap,
FROM        b
INNER JOIN   (SELECT  VId,
                     MIN(TimestampLocal) AS S_Time,
                     MAX(TimestampLocal) AS E_Time
            FROM     Info
            where CAST (TimestampLocal AS DATE) >='2016-04-05'
            and CAST (TimestampLocal AS DATE) <'2016-05-10'
            GROUP BY VId
            ) AS c
        ON  b.Id = c.VId
INNER JOIN   Info AS s
        ON  s.VId = c.VId
        AND s.TimestampLocal = c.S_Time
INNER JOIN   Info AS e
        ON  e.Vd = c.VId
        AND e.TimestampLocal = c.E_Time
WHERE   b.SId = 100