如何优化取决于执行时间的 T-SQL 查询?

How to optimize T-SQL query which depends on execution time?

我有一个很大的(几百万行)table 系统事件。我必须获取最近的事件计数,但我不知道如何以正确的方式进行。

我创建了这个视图:

CREATE VIEW [dbo].[EventCounts] 
AS
  SELECT  
      (SELECT COUNT(1)
       FROM dbo.SystemEvents r
       WHERE r.Timestamp > DATEADD(MINUTE, -15, GETDATE())
         AND r.Timestamp <= GETDATE()) AS last15minEventCount,
      (SELECT COUNT(1)
       FROM dbo.SystemEvents r
       WHERE r.Timestamp > DATEADD(MINUTE, -30, GETDATE())
         AND r.Timestamp <= DATEADD(MINUTE, -15, GETDATE())) AS from15to30EventCount,
      (SELECT COUNT(1)
       FROM dbo.SystemEvents r
       WHERE r.Timestamp > DATEADD(MINUTE, -60, GETDATE())
         AND r.Timestamp <= DATEADD(MINUTE, -30, GETDATE())) AS from30to60EventCount,
     (SELECT COUNT(1)
      FROM dbo.SystemEvents r
      WHERE r.Timestamp <= DATEADD(MINUTE, -60, GETDATE())) AS olderThan60minEventCount

这个观点returns:

  1. < 15 分钟事件计数;
  2. 15 - 30 分钟的事件计数;
  3. 30 - 60 分钟的事件计数;
  4. 超过 60 分钟的事件计数。

现在我的代码在整个 table 上运行了 4 次,我想优化它。我不能使用预聚合,因为这取决于执行时间。由于同样的原因,我无法使用索引视图。

我想我可以在 Timestamp 列上添加非聚集索引,但代码仍然需要读取整个 table 得到这个计数,对吗?

你能建议如何优化我的查询吗?

P.S。 last15minEventCount、from15to30EventCount、from30to60EventCount - 所有事件的很小一部分。

一个建议,你可以改变方法,像下面这样写你的查询。

CREATE VIEW [dbo].[EventCounts] 
AS 
  WITH Boundary AS (
           SELECT Dateadd(MINUTE, -15, Getdate()) AS LOW, 
                  Getdate()                       AS HIGH, 
                  'last15minEventCount'           AS Label 
           UNION 
           SELECT Dateadd(MINUTE, -30, Getdate()) AS LOW, 
                  Dateadd(MINUTE, -15, Getdate()) AS HIGH, 
                  'from15to30EventCount'          as Label 
          --Additional Conditions 
          ) 
  SELECT Count(*), 
         B.Label 
  FROM   SystemEvents R 
         INNER JOIN Boundary B 
                 ON R.TimeStamp BETWEEN LOW AND HIGH 
  GROUP  BY B.Label 

这样你就可以将输出变成行,你需要转换成列,我觉得这应该是直截了当的。

这应该很快,因为查询现在是 SARGable,您需要在 TimpeStamp 列上放置非聚集索引。

尝试这样的事情:

 SELECT COUNT(1),
        SUM(case when r.Timestamp > DATEADD(MINUTE, -15, GETDATE()) AND r.Timestamp <= GETDATE() 
                 then 1
                 else 0
            end) as last15minEventCount,
        sum(case when r.Timestamp > DATEADD(MINUTE, -30, GETDATE())AND r.Timestamp <= DATEADD(MINUTE, -15, GETDATE())
                 then 1
                 else 0 
                 end) as from15to30EventCount,
        sum(case when  r.Timestamp > DATEADD(MINUTE, -60, GETDATE()) AND r.Timestamp <= DATEADD(MINUTE, -30, GETDATE())
                 then 1
                 else 0 
                 end) as from30to60EventCount,
        sum(case when r.Timestamp <= DATEADD(MINUTE, -60, GETDATE()
                 then 1
                 else 0 
                 end) as olderThan60minEventCount,

 FROM dbo.SystemEvents r

为避免每个间隔进行一次扫描,您可以使用 SUM(CASE ... END)。但是要使查询真正快速,您应该维护一个 table 和总计数并在 TimeStamp 列上创建一个索引:

CREATE TABLE SystemEvents
(
    [TimeStamp] datetime
)
GO

CREATE INDEX IX_SystemEvents_TimeStamp
ON dbo.SystemEvents(TimeStamp)

CREATE TABLE SystemEventsTotalCount
(
    TotalCount int NOT NULL
)
GO

INSERT INTO SystemEventsTotalCount VALUES ((SELECT COUNT(*) FROM SystemEvents))
GO

CREATE TRIGGER SystemEvents_TotalCount
ON SystemEvents
FOR INSERT, DELETE
AS
    DECLARE @InsertedRows int = (SELECT COUNT(*) FROM inserted);
    DECLARE @DeletedRows int = (SELECT COUNT(*) FROM deleted);
    UPDATE SystemEventsTotalCount
    SET TotalCount = TotalCount + @InsertedRows - @DeletedRows
GO
GO
CREATE VIEW EventCounts
AS
WITH b AS
(
    SELECT 
        SUM(CASE WHEN r.Timestamp > DATEADD(MINUTE, -15, GETDATE()) AND r.Timestamp <= GETDATE() THEN 1 ELSE 0 END) AS Last15MinEventCount,
        SUM(CASE WHEN r.Timestamp > DATEADD(MINUTE, -30, GETDATE()) AND r.Timestamp <= DATEADD(MINUTE, -15, GETDATE()) THEN 1 ELSE 0 END) AS From15To30EventCount,
        SUM(CASE WHEN r.Timestamp > DATEADD(MINUTE, -60, GETDATE()) AND r.Timestamp <= DATEADD(MINUTE, -30, GETDATE()) THEN 1 ELSE 0 END) AS From30to60EventCount,
        (SELECT TotalCount FROM dbo.SystemEventsTotalCount) AS TotalEventCount
    FROM 
        dbo.SystemEvents r
    WHERE
        r.Timestamp > DATEADD(MINUTE, -60, GETDATE())
)
SELECT
    b.Last15MinEventCount, b.From15To30EventCount, b.From30to60EventCount,
    (b.TotalEventCount - b.Last15MinEventCount - b.From15To30EventCount - b.From30to60EventCount) AS OlderThan60EventCount

FROM b
GO

SELECT * FROM EventCounts