基于度量子集的最小/最大日期的SQL性能

时间:2019-07-12 08:33:36

标签: sql sql-server

我编写了一个SQL查询,该查询返回所有“度量值组”以及在其中看到度量值的第一个和最后一个日期。这些是从子“ Measurement”表中获得的,但是在此特定查询中,我只想将最后100个测量值(这将成为用户可配置的)用于第一个/最后一个日期计算。我附加的查询返回了正确的结果,但是随着数据集的增长(Measurements表中的数百万行),性能变得很差,大约需要1分钟才能返回。

希望您能了解我在做什么,有什么想法可以改善此查询的性能吗?

查询

SELECT  mg.MeasurementGroupId,
        mg.[Name],
(
    SELECT  MIN(m1.MeasurementDateTime)
    FROM    dbo.Measurement m1
    WHERE   m1.MeasurementId IN
    (
        SELECT TOP 100 m2.MeasurementId
        FROM    dbo.Measurement m2
        WHERE   m2.MeasurementGroupId = mg.MeasurementGroupId
        ORDER
            BY  m2.MeasurementDateTime DESC
    )
) AS FirstMeasurement,
(
    SELECT  MAX(m1.MeasurementDateTime)
    FROM    dbo.Measurement m1
    WHERE   m1.MeasurementId IN
    (
        SELECT TOP 100 m2.MeasurementId
        FROM    dbo.Measurement m2
        WHERE   m2.MeasurementGroupId = mg.MeasurementGroupId
        ORDER
            BY  m2.MeasurementDateTime DESC
    )
) AS LastMeasurement
FROM    dbo.MeasurementGroup mg

DDL

CREATE TABLE [dbo].[MeasurementGroup](
    [MeasurementGroupId] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_MeasurementGroup] PRIMARY KEY CLUSTERED 
(
    [MeasurementGroupId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Measurement](
    [MeasurementId] [int] IDENTITY(1,1) NOT NULL,
    [MeasurementGroupId] [int] NOT NULL,
    [MeasurementDateTime] [datetime] NOT NULL,
    [Value1] [decimal](18, 0) NULL,
    [Value2] [decimal](18, 0) NULL,
    [Value3] [decimal](18, 0) NULL,
 CONSTRAINT [PK_Measurement] PRIMARY KEY CLUSTERED 
(
    [MeasurementId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Measurement]  WITH CHECK ADD  CONSTRAINT [FK_Measurement_MeasurementGroup] FOREIGN KEY([MeasurementGroupId])
REFERENCES [dbo].[MeasurementGroup] ([MeasurementGroupId])
GO

ALTER TABLE [dbo].[Measurement] CHECK CONSTRAINT [FK_Measurement_MeasurementGroup]
GO

DML

WITH
  L0   AS (SELECT c FROM (SELECT 1 UNION ALL SELECT 1) AS D(c)), -- 2^1
  L1   AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),       -- 2^2
  L2   AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),       -- 2^4
  Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS k FROM L2)

INSERT INTO [dbo].[MeasurementGroup] ([Name])
SELECT  'Measurement ' + CAST(k AS VARCHAR)
FROM    Nums
WHERE   k <= 10
WITH
  L0   AS (SELECT c FROM (SELECT 1 UNION ALL SELECT 1) AS D(c)), -- 2^1
  L1   AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),       -- 2^2
  L2   AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),       -- 2^4
  L3   AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),       -- 2^8
  L4   AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),       -- 2^16
  L5   AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),       -- 2^32
  Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS k FROM L5)

INSERT INTO [dbo].[Measurement] ([MeasurementGroupId], [MeasurementDateTime], [Value1], [Value2], [Value3])
SELECT  mg.MeasurementGroupId, DATEADD(MINUTE, k, GETDATE()), 13.3, NULL, NULL
FROM    Nums n
CROSS JOIN  dbo.MeasurementGroup mg
WHERE   k < 1000000

生成的执行计划:https://www.brentozar.com/pastetheplan/?id=r1zs_JIWH

1 个答案:

答案 0 :(得分:0)

尝试使用apply。至少,您只需要提取100条测量行,而不是两次:

select mg.*, m.*
from dbo.MeasurementGroup mg cross apply
     (select min(MeasurementDateTime) as firstMeasurement,
             max(MeasurementDateTime) as lastMeasurement
      from (select top (100) m.*
            from measurement m
            where m.MeasurementGroupId = mg.MeasurementGroupId
            order by m.MeasurementDateTime desc
           ) m
     ) m;

这应该能够利用measurement(MeasurementGroupId, MeasurementDateTime desc)上的索引。

相关问题