SQL Server 2008 R2:使用多个 CTE 获取日期明智的记录

Posted

技术标签:

【中文标题】SQL Server 2008 R2:使用多个 CTE 获取日期明智的记录【英文标题】:SQL Server 2008 R2: Get date wise records using multiple CTE 【发布时间】:2018-05-25 11:07:25 【问题描述】:

我有下表:

表:TblLocation

CREATE TABLE TblLocation
(
    ColDate DATE,
    ColTime time,
    Colvalue VARCHAR(50)
);

INSERT INTO TblLocation VALUES('2018-01-01','01:10:11','Location1');
INSERT INTO TblLocation VALUES('2018-01-01','02:10:11','Location2');
INSERT INTO TblLocation VALUES('2018-01-01','03:10:11','Location3');
INSERT INTO TblLocation VALUES('2018-01-01','11:10:11','Location4');

INSERT INTO TblLocation VALUES('2018-01-02','01:10:11','Location1');
INSERT INTO TblLocation VALUES('2018-01-02','02:10:11','Location2');
INSERT INTO TblLocation VALUES('2018-01-02','03:10:11','Location2');
INSERT INTO TblLocation VALUES('2018-01-02','01:10:11','Location3');
INSERT INTO TblLocation VALUES('2018-01-02','03:15:11','Location4');

INSERT INTO TblLocation VALUES('2018-01-03','02:10:11','Location1');
INSERT INTO TblLocation VALUES('2018-01-03','02:50:11','Location1');
INSERT INTO TblLocation VALUES('2018-01-03','03:10:11','Location1');

注意:我想找到每个日期的第一个、中间和最后一个位置。

预期输出

ColDate         FirstLocation   MidLocation     LastLocation
-------------------------------------------------------------
2018-01-01      Location1       Location3       Location4
2018-01-02      Location1       Location2       Location4
2018-01-03      Location1       NULL            Location1

输出说明

    FirstLocation - 这应该基于 ORDER by date 和 time,并且将是该日期的 FIRST time。

    MidLocation - 这不应该等于 FirstLocation 和 LastLocation 但它是 存在于数量更多或任何平局的位置之间。 如果两者之间没有任何内容,则为 NULL,如第 3 行所示。

    LastLocation - 这应该基于 ORDER 按日期和时间,并且将是该日期的 LAST 时间。

我的尝试

WITH CTEMain AS
(
    SELECT ColDate,ColTime, ColValue, ROW_NUMBER() OVER(PARTITION BY ColDate ORDER BY ColDate,ColTime ASC) Rn 
    FROM dbo.TblLocation
),
CTEMinMax AS
(
    SELECT MIN(Rn) AS MinRn,MAX(Rn) AS MaxRn
    FROM CTEMain
),
CX AS 
(
    SELECT c1.ColDate,c1.Colvalue AS FirstLocation,
       c2.Colvalue AS LastLocation
    FROM CTEMain c1 
    INNER JOIN CTEMinMax cmin ON c1.Rn = cmin.MinRn
    INNER JOIN CTEMain c2 ON 1 = 1
    INNER JOIN CTEMinMax cmax ON c2.Rn = cmax.MaxRn
), 
CXX AS
( 
    SELECT TOP 1 ROW_NUMBER() OVER(PARTITION BY Colvalue ORDER BY Rn DESC) Rnk,ColValue,Rn
    FROM CTEMain c1
    INNER JOIN CX x ON c1.colvalue <> x.FirstLocation AND c1.colvalue <> x.LastLocation
    GROUP BY ColValue,Rn
    ORDER BY Rnk DESC
)
SELECT ColDate,CX.FirstLocation,(SELECT ColValue FROM CXX) AS MidName, cx.LastLocation
FROM CX

输出

ColDate     FirstLocation   MidName     LastLocation
-----------------------------------------------------
2018-01-01  Location1       Location2   Location4
2018-01-02  Location1       Location2   Location4
2018-01-03  Location1       Location2   Location4

【问题讨论】:

【参考方案1】:

关闭

declare @t TABLE (ColDate DATE, ColTime time, Colvalue VARCHAR(50));
INSERT INTO @t VALUES
('2018-01-01','01:10:11','Location1'),
('2018-01-01','02:10:11','Location2'),
('2018-01-01','03:10:11','Location3'),
('2018-01-01','11:10:11','Location4'),

('2018-01-02','01:10:11','Location1'),
('2018-01-02','02:10:11','Location2'),
('2018-01-02','03:10:11','Location2'),
('2018-01-02','01:10:11','Location3'),
('2018-01-02','03:15:11','Location4'),

('2018-01-03','02:10:11','Location1'),
('2018-01-03','02:50:11','Location1'),
('2018-01-03','03:10:11','Location1');

--select * from @t order by ColDate, ColTime

with cte as 
( select * 
       , row_number() over (partition by ColDate order by ColTime asc)  as tasc
       , row_number() over (partition by ColDate order by ColTime desc) as tdsc
  from @t 
) 

select distinct
       cteMin.ColDate, cteMin.ColTime
     , cteMin.Colvalue as [cteMin.Colvalue]
     , cteMid.Colvalue as [cteMid.Colvalue]
     , cteMax.Colvalue as [cteMax.Colvalue]
from cte cteMin 
join cte cteMax
  on cteMin.ColDate = cteMax.ColDate 
 and cteMin.tasc = 1 
 and cteMax.tdsc = 1
left join cte cteMid 
  on cteMid.ColDate = cteMin.ColDate 
 and cteMid.tasc > 1
 and cteMid.tdsc = 2
 and cteMid.Colvalue <> cteMin.Colvalue 
 and cteMid.Colvalue <> cteMax.Colvalue

【讨论】:

【参考方案2】:

我会使用 correlated subquery with apply 运算符:

select distinct t.coldate, t1.*, 
       ( select top 1 ColValue
         from TblLocation
         where (coldate = t.coldate and 
                ColValue <> t1.FirstLocation and ColValue <> t2.LastLocation)
         order by count(*) over() desc
       ) as MidName, t2.*
from TblLocation t cross apply 
     (select top 1 ColValue as FirstLocation 
      from TblLocation
      where coldate = t.coldate
      order by coltime
     ) t1 cross apply 
     (select top 1 ColValue as LastLocation
      from TblLocation
      where coldate = t.coldate
      order by coltime desc
     ) t2;

【讨论】:

以上是关于SQL Server 2008 R2:使用多个 CTE 获取日期明智的记录的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2008 R2 计算字段问题

windows server 2008 r2 C盘空间不断减少!怎么清理

在 SQL Server 2008 R2 和 SQL Server Compact 4 中存储日期/时间数据的最佳方式

SQL server 2008 R2

将 EF 模型从 SSCE 4.0 切换到 SQL Server 2008 R2

windows 2012 r2怎么安装sql server 2008 r2