TSQL ЛИДЕР С ВРЕМЕННЫМИ ИНТЕРВАЛАМИ - PullRequest
1 голос
/ 22 июня 2019

Я пытаюсь улучшить производительность для этого запроса:

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 ]

1 голос
/ 25 июня 2019

Я полагаю, что вы можете избежать корреляционного подзапроса, самостоятельно присоединившись к таблице:

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
1 голос
/ 25 июня 2019

Вы можете попытаться разделить операции.Например:

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, или если всегда у текущего идентификатора поездки дата больше, чем у предыдущей, мы можем добавить TS.TripID > T.TripID, чтобы дополнительно ограничить строки?

Как вы сказали в комментариях, обрабатывается 300 000 строк, но это действительно небольшой объем данных, поэтому его выполнение не должно занимать так много времени.Если вы можете оптимизировать запрос дальше, поделитесь дампом данных.


Вы можете создать представление индекса, чтобы автоматически рассчитывать результаты первого запроса, поэтому никаких дополнительных усилий не требуется.

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 
1 голос
/ 24 июня 2019

На Fiddler это 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...