【问题标题】:What is the best query to get the current records in an archive table (SQL Server 2005/2008)获取存档表中当前记录的最佳查询是什么(SQL Server 2005/2008)
【发布时间】:2010-09-22 17:31:25
【问题描述】:

示例

有一个应用程序可以测量世界每个城镇的温度。每 5 分钟进行一次测量并写入测量表。

CREATE TABLE [dbo].[Measurement](
    [MeasurementID] [int] IDENTITY(1,1) NOT NULL,
    [Town] [varchar](50) NOT NULL,
    [Date] [datetime] NOT NULL,
    [Temp] [int] NOT NULL,
CONSTRAINT [PK_Measurement] PRIMARY KEY CLUSTERED 
(
    [MeasurementID] ASC
)) ON [PRIMARY]

问题

获取城镇列表及其当前温度的最有效查询是什么?

假设有 10 万个城镇和 1000 万条记录

注意:我添加了几个可能的答案,但可能还有其他选择。

【问题讨论】:

    标签: sql-server performance tsql sql-server-2008


    【解决方案1】:

    这里有几个应该可行的:

    选择
    m1.Town, m1.Temp
    来自
    测量 AS m1
    左连接
    测量 AS m2
    开启
    m1.Town = m2.Town
    AND m1.Date 在哪里
    m2.MeasurementID 为空
    按 m1.Town 排序


    你需要一个关于城镇和日期的索引。

    这种技术对于 MySQL 的早期版本特别有用,它无法处理更明显的问题

    选择城镇,温度
    FROM 测量 AS m1
    不存在的地方 (
    从测量中选择 1
    WHERE 城镇 = m1.Town
    AND 日期 > m1.date
    )
    按城镇排序

    【讨论】:

    • 有趣的方法。您知道加入获取每个城镇的最大日期的子查询是否更贵/更便宜?
    • 我通常通过测试备选方案来选择我的查询设计,而 NULL 测试似乎总是和任何测试一样快。然后不存在。聚合函数(如 MAX)有时可以让查询优化器有理由读取在这种情况下不需要的多条记录(具有良好的索引)。
    【解决方案2】:
    select *
    from
    (
        select distinct *, --Keyword,Total,CreatedOn,EngineInstanceID,
        Rank() over (PARTITION by Town order by Date DESC) as DateOrder
        from Measurement
        where Town is not null
    ) CurrentMeasurement
    where DateOrder = 1
    

    【讨论】:

      【解决方案3】:

      很高兴看到这么多剥这只猫的方法。这是一个使用 CTE 的示例(您也可以嵌套查询以获取更多 ANSI 主义,但我发现 CTE 很好,可以避免大量缩进和预先声明,使其上下都非常易读):

      WITH LastMeasurements AS (
          SELECT [Town], MAX([Date]) AS LastMeasurementDate
          FROM [Measurement]
          GROUP BY [Town]
      )
      SELECT [Measurement].Town, [Measurement].[Date], [Measurement].Temp
      FROM [Measurement]
      INNER JOIN LastMeasurements
          ON [Measurement].[Town] = LastMeasurements.[Town]
          AND [Measurement].[Date] = LastMeasurements.LastMeasurementDate
      

      我喜欢显式回溯技术的地方在于,它可以让您轻松访问为该组选择的顶行中的所有信息,并且在更改分组方面非常灵活,并且很少重复自己。

      优化器倾向于在 SQL Server 上非常快速地执行这些 - 与大多数解决方案一样,如果您有关于 Town、Date、Temp 的索引,它将覆盖并且运行速度非常快。即使只是在镇上,日期,GROUP BY 中的大部分工作无论如何都可以超快完成。

      【讨论】:

        【解决方案4】:
        select s.*
        from Measurement s
        where exists ( 
           select 1
           from Measurement s1
           where s.Town = s1.Town
           group by s1.Town
           having max( s1.Date )= s.Date)
           order by s.Town
        

        【讨论】:

          【解决方案5】:
          select m.town, m.temperature, m.date
          from Measurement m
          where m.date = (select max(m2.date) from Measurement m2 where m2.town = m.town)
          order by 1
          

          【讨论】:

          • 你少了一个括号。
          • 不是 - 我没有看到卷轴。
          【解决方案6】:

          您可能有一张包含不同城镇列表的表格吗?假设每个城镇有大约 1000 个测量值,窗口函数解决方案(例如 row_number()、rank() 等)的性能可能不如普通聚合或此 APPLY 版本:

          SELECT
             M.*
          FROM
             Towns T
             OUTER APPLY (
                SELECT TOP 1 * -- add 'WITH TIES' to the 'TOP 1' if you have/want ties on date.
                FROM Measurement M
                WHERE T.Town = M.Town
                ORDER BY M.Date DESC
             ) M
          

          如果没有城镇列表,你可以试试这个,虽然我不知道它会如何与普通的香草聚合 + 查找相叠加:

          SELECT
             M.*
          FROM
             (SELECT DISTINCT Town FROM Towns) T
             OUTER APPLY (
                SELECT TOP 1 *
                FROM Measurement M
                WHERE T.Town = M.Town
                ORDER BY M.Date DESC
             ) M
          

          这些查询的性能绝对取决于索引。您至少需要 [Town] 上的一个,而 [Town, Date] 最好。如果其他表使用MeasurementID,但您很少使用MeasurementID 访问Measurement 表,则删除聚集索引,使MeasurementID 成为非聚集PK,并在Town、Date 上添加(非唯一)聚集索引。如果您没有使用 MeasurementID 的其他表,则完全删除该列 - 在这种情况下,它是无用的合成/人工键,无缘无故地使您的表膨胀。

          这些建议的索引更改将有助于此处使用聚合或应用的答案中的所有查询。不确定它们对窗口函数的影响,这取决于优化器如何制定执行计划(如果它足够聪明地意识到它只需要访问最大日期而不触及所有其他行,那么相同的索引将提升它难以置信,虽然我怀疑优化器能做到这一点)。

          另外,为了提高性能,我肯定会建议使用 TownID 的 Town 表,而不是放置整个城镇。如果城市名称改变了怎么办?从每个名称的平均 15 个字节左右切换到一个 int TownID 的仅 4 个字节将有助于提高速度。 (虽然测试是为了确定这一点)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-09-22
            • 2014-01-21
            • 1970-01-01
            • 2010-09-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多