具有时间间隔的TSQL领导

时间:2019-06-22 05:20:14

标签: sql sql-server tsql subquery

我正在尝试提高此查询的性能:

SQL FIDDLE

    SELECT Ts.[TripID]
      ,Ts.[RDate]
      ,Ts.[RTime]
      ,Ts.[Passengers]
      ,Ts.[Other]
      ,Ts.[RecordStatusID]
      ,IIF(Ts.RecordStatusID = 1, '', CONVERT(VARCHAR(10),
                             (SELECT        SUM((IIF(Passengers + Other < 8, 1, CEILING((Passengers + Other) / 7.0))))
                               FROM            dbo.tblTrips T
                               WHERE        ISNULL(T.ActualDateTime, CAST(T.RDate AS SMALLDATETIME) + CAST(T.RTime AS SMALLDATETIME)) BETWEEN ISNULL(Ts.ActualDateTime, CAST(Ts.RDate AS SMALLDATETIME) + CAST(Ts.RTime AS SMALLDATETIME)) AND DATEADD(MINUTE, 35, 
                                                         ISNULL(Ts.ActualDateTime, CAST(Ts.RDate AS SMALLDATETIME) + CAST(Ts.RTime AS SMALLDATETIME))) AND T.RecordStatusID > 1)) + ' Trips') AS Trips
  FROM tblTrips Ts
  ORDER BY Ts.RDate, Ts.RTime

我真的不确定如何以其他方式执行此操作,我们将不胜感激。

这里的目标是计算每行35分钟的间隔,显示从当前行程时间(rdate + rtime)+ 35分钟开始的行程,我尝试进行分组,但结果不是我想要的。我想知道是否有时间间隔的线索可以指定条件并对此进行计数?

enter image description here

3 个答案:

答案 0 :(得分:1)

在提琴手上这是9毫秒,而您的解决方案是11毫秒 这个想法是将时间和日期字段合并一次,然后使用它。 对于其他部分,我并不确定您正在做什么。

  ;with cte as (
    select *,ISNULL(ActualDateTime, CAST(RDate AS SMALLDATETIME) + CAST(RTime AS SMALLDATETIME)) TripsTime
    ,isnull(nullif(CEILING((Passengers + Other)/7.0),0),1) ICPO FROM tblTrips 
  )
  select  [TripID],[RDate],[RTime],[Passengers],[Other],[RecordStatusID]
 , IIF(Ts.RecordStatusID = 1, '',
    (select CONVERT(VARCHAR(10),SUM(ICPO))+ ' Trips' from cte T where 
        T.TripsTime between Ts.TripsTime and DATEADD(MINUTE, 35,Ts.TripsTime)
        AND T.RecordStatusID > 1))
     AS Trips
   from cte Ts ORDER BY Ts.RDate, Ts.RTime

答案 1 :(得分:1)

我想您可以通过自我加入表格来避免相关子查询:

SELECT
         Ts.[TripID]
       ,Ts.[RDate]
       ,Ts.[RTime]
       ,Ts.[Passengers]
       ,Ts.[Other]
       ,Ts.[RecordStatusID]
       , IIF(Ts.RecordStatusID = 1, '',
         cast(     
                       SUM(IIF(tl.Passengers + tl.Other < 8, 1, CEILING((tl.Passengers + tl.Other) / 7.0)))
        as varchar(10)
         ) + ' Trips') AS Trips
FROM
         tblTrips Ts
         inner join tblTrips Tl
         on 
          Tl.RecordStatusID > 1
          and (
         (ts.tripid=tl.tripid and Ts.RecordStatusID = 1) or
         (  Ts.RecordStatusID > 1 and
        ISNULL(Tl.ActualDateTime, CAST(Tl.RDate AS SMALLDATETIME) + CAST(Tl.RTime AS SMALLDATETIME)) BETWEEN 
            ISNULL(Ts.ActualDateTime, CAST(Ts.RDate AS SMALLDATETIME) + CAST(Ts.RTime AS SMALLDATETIME)) 
                AND DATEADD(MINUTE, 35, ISNULL(Ts.ActualDateTime, CAST(Ts.RDate AS SMALLDATETIME) + CAST(Ts.RTime AS SMALLDATETIME)))
            ))
GROUP BY
     Ts.[TripID]
       ,Ts.[RDate]
       ,Ts.[RTime]
       ,Ts.[Passengers]
       ,Ts.[Other]
       ,Ts.[RecordStatusID]             
ORDER BY
         Ts.RDate
       , Ts.RTime

答案 2 :(得分:1)

您可以尝试拆分操作。例如:

DROP TABLE IF EXISTS #tblTrips;
DROP TABLE IF EXISTS #tblTripsAgg;


CREATE TABLE #tblTrips
(
     [TripID] [int] NOT NULL 
    ,[Record] INT 
    ,[ActualDateTime] [datetime] 
    ,PRIMARY KEY ([TripID])
);

CREATE TABLE #tblTripsAgg
(
     [TripID] [int] NOT NULL PRIMARY KEY
    ,Trips INT
);

INSERT INTO #tblTrips ([TripID], [Record], [ActualDateTime])
SELECT TripID
      ,IIF(RecordStatusID = 1, 0, IIF(Passengers + Other < 8, 1, CEILING((Passengers + Other) / 7.0)))
      ,CAST(RDate AS SMALLDATETIME) + CAST(RTime AS SMALLDATETIME)
FROM tblTrips

INSERT INTO #tblTripsAgg
SELECT TS.[TripID]
        ,SUM(T.[Record])
FROM #tblTrips TS
LEFT JOIN #tblTrips T
    ON T.[ActualDateTime] >= TS.[ActualDateTime]
    AND T.[ActualDateTime] <= DATEADD(MINUTE, 35, TS.[ActualDateTime])
GROUP BY TS.[TripID]

SELECT A.[TripID]
      ,A.[RDate]
      ,A.[RTime]
      ,A.[Passengers]
      ,A.[Other]
      ,A.[RecordStatusID]
      ,IIF(A.RecordStatusID = 1, '', CONCAT(Trips, ' Trips')) AS Trips
FROM tblTrips A
INNER JOIN #tblTripsAgg DS
    ON A.TripID = ds.TripID;

为什么?首先,因为有时复杂的查询无法通过SQL引擎正确优化,其次-您可以轻松地看到过程中哪一部分最慢。

例如,在上面的示例中,第一部分只是将我们的日期和时间放在一个日期中。另外,我们正在那里计算其他一些内容。因此,如果查询的这一部分花费大量时间,则只需使用触发器,使用计算的预计算列或应用程序触摸表时对其进行预计算即可。

然后在第二个查询中,我们计算每个trip id的总和。如果这部分很慢,也许我们可以在包含ActualDateTime的表上创建一个不同的索引,或者如果当前旅程ID的日期总是大于前一个,那么我们可以添加TS.TripID > T.TripID来限制再行吗?

正如您在评论中所说,处理了30万行,但这确实是少量数据,因此执行它实际上不需要花费太多时间。如果您可以进一步优化查询,请共享数据转储。


您可以创建一个索引视图,以自动预先计算第一个查询的结果,因此不需要其他工作。

DROP VIEW IF EXISTS  dbo.vw_tblTrips;
GO

CREATE VIEW dbo.vw_tblTrips WITH SCHEMABINDING
AS 
SELECT TripID AS [TripID]
      ,IIF(RecordStatusID = 1, 0, IIF(Passengers + Other < 8, 1, CEILING((Passengers + Other) / 7.0))) AS [Record]
      ,CAST(RDate AS SMALLDATETIME) + CAST(RTime AS SMALLDATETIME) AS [ActualDateTime]
FROM dbo.tblTrips;

GO

CREATE UNIQUE CLUSTERED INDEX INX_vw_tblTrips   
    ON dbo.vw_tblTrips ([ActualDateTime], [TripID]);  
GO