【问题标题】:Recursive SQL query to speed up non-indexed query递归 SQL 查询加速非索引查询
【发布时间】:2011-04-24 10:24:39
【问题描述】:

这个问题很大程度上是出于好奇,因为我确实有一个有效的查询(只是比我想要的要长一点)。

我有一个有 400 万行的表。此表上唯一的索引是一个自动递增的 BigInt ID。该查询正在其中一列中查找不同的值,但仅返回 1 天。不幸的是,被评估的 ReportDate 列不是 DateTime 类型,甚至不是 BigInt,而是 YYYYMMDD 格式的 char(8)。所以查询有点慢。

  SELECT Category 
    FROM Reports 
   where ReportDate = CONVERT(VARCHAR(8), GETDATE(), 112) 
GROUP BY Category

请注意,上述语句中的日期转换只是将其转换为 YYYYMMDD 格式进行比较。

我想知道是否有一种方法可以优化此查询,因为我知道我感兴趣的唯一数据位于表的“底部”。我在考虑某种递归 SELECT 函数,它逐渐增长了一个可用于最终查询的临时表。

例如在psuedo-sql中:

N = 128
TemporaryTable = SELECT TOP {N} * 
                   FROM Reports 
               ORDER BY ID DESC 

/* Once we hit a date < Today, we can stop */
if(TemporaryTable does not contain ReportDate < Today) 
  N = N**2
  Repeat Select

/* We now have a smallish table to do our query */
  SELECT Category 
    FROM TemproaryTable 
   where ReportDate = CONVERT(VARCHAR(8), GETDATE(), 112) 
GROUP BY Category

这有意义吗?有这样的可能吗?

这是在 MS SQL Server 2008 上。

【问题讨论】:

  • @Martin 我不是数据所有者,而且我真的很好奇上述是否可行。理想情况下,我们会在报告日期上编制索引(并使其为非字符类型,例如 bigint 或实际日期类型)
  • SELECT MAX(ID) FROM YourTable WHERE ReportDate &lt; CONVERT(VARCHAR(8), GETDATE(), 112) 是否会找到感兴趣范围的限制?如果是这样,那么扫描可能会在索引结束时开始反向工作,并且它可以在找到前一天的第一条记录后立即停止。
  • 是的,您尝试做的事情是可能的 - 但如果没有 ReportDate 上的索引,它永远不会像它可能的那样快......

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


【解决方案1】:

为什么不直接创建你需要的索引呢?

create index idx_Reports_ReportDate 
    on Reports(ReportDate, Category)

【讨论】:

  • 我不是数据所有者 - 致力于更新索引,但正在寻找替代解决方案(更不用说对一般替代方案感到好奇)
【解决方案2】:

我可能建议您不需要转换以 YYYYMMDD 格式存储为 char 数据的 Date;该格式本身就是可排序的。我会将您的日期转换为该格式的输出。

此外,您编写转换的方式是为每一行转换当前 DateTime,因此即使为整个查询存储该值可以加快速度......但我认为只需将您正在搜索的日期转换为该格式的 char 会有所帮助。

当然,我还建议创建您需要的索引...但这不是您问的问题:P

【讨论】:

    【解决方案3】:

    不,这没有意义。优化此查询的唯一方法是为其设置一个覆盖索引:

    CREATE INDEX ndxReportDateCategory ON Reports (ReportDate, Category);
    

    更新

    考虑到您不能修改架构的评论,那么您应该修改架构。如果你仍然不能,那么答案仍然适用:解决方案是有一个索引。

    最后,为了更直接地回答您的问题,如果您在 ID 和 ReportData 之间有很强的相关性:您寻找的 ID 是 ReportDate 小于您之后的日期的最大 ID:

    SELECT MAX(Id) 
    FROM Reports
    WHERE ReportDate < 'YYYYMMDD';
    

    这将对 ID 索引进行反向扫描,并在您想要的日期之前的第一个 ID 处停止(即,不会扫描整个表)。然后,您可以在找到的最大 ID 上过滤您的报告库。

    【讨论】:

    • 有趣 - 这是反向扫描,因为它正在查看 Max(ID)?
    • 是的。可以通过快速查看索引的任一端来满足在 Column 上有索引的 MAX(Column) 。如果您还有一个过滤器,那么扫描操作员可以通过从适当的一端开始,然后逐个检查条件来快速找到 MAX(或 MIN)。通过过滤器的第一个记录是所需的值。
    • 请注意,这假设 Id 和 ReportDate 之间存在确定性关系,即。没有日期按 ID 顺序颠倒。
    • 我很乐意给你绿色检查 - 但你的答案是矛盾的“优化查询的唯一方法是为其设置一个覆盖索引”,然后你有一个答案,它在不创建索引的情况下优化查询(查询从 20 秒以上变为零)。哦,我明白了 - 创建索引 == 很好,但是你的言辞妨碍了你的回答。
    • 其他人会谷歌并登陆此页面。我更喜欢花言巧语,并强调解决关系表中性能问题的方法几乎总是以您的模式(即覆盖索引)的形式出现,而几乎从不在查询文本中。跨度>
    【解决方案4】:

    我偶尔用来进入具有与您的索引相似的索引的日志表的查询模式之一是通过子查询进行限制:

    DECLARE @ReportDate varchar(8)
    SET @ReportDate = Convert(varchar(8), GetDate(), 112)
    
    SELECT *
    FROM
    (
    SELECT top 20000 *
    FROM Reports
    ORDER BY ID desc
    ) sub
    WHERE sub.ReportDate = @ReportDate
    

    20k/4M = 0.5% 的表被读取。


    这是一个循环解决方案。注意:可能希望在临时表中建立 ID 主键和 Reportdate 索引。

    DECLARE @ReportDate varchar(8)
    SET @ReportDate = Convert(varchar(8), GetDate(), 112)
    
    DECLARE @CurrentDate varchar(8), MinKey bigint
    
    
    SELECT top 2000 * INTO #MyTable
    FROM Reports ORDER BY ID desc
    
    SELECT @CurrentDate = MIN(ReportDate), @MinKey = MIN(ID)
    FROM #MyTable
    
    WHILE @ReportDate <= @CurrentDate
    BEGIN
    
      SELECT top 2000 * INTO #MyTable
      FROM Reports WHERE ID < @MinKey ORDER BY ID desc
    
      SELECT @CurrentDate = MIN(ReportDate), @MinKey = MIN(ID)
      FROM #MyTable
    
    END
    
    SELECT * FROM #MyTable
    WHERE ReportDate = @ReportDate
    
    
    DROP TABLE #MyTable
    

    【讨论】:

    • 这就是我的想法——但不一定提前知道子查询的大小。
    【解决方案5】:

    我认为您会发现 Rob Farley 的博客上关于 SARGability 的讨论对于您的帖子主题非常有趣。

    http://blogs.lobsterpot.com.au/2010/01/22/sargable-functions-in-sql-server/

    一种不需要您修改现有列数据类型的有趣替代方法是利用计算列。

    alter table REPORTS
    add castAsDate as CAST(ReportDate as date)
    
    create index rf_so2 on REPORTS(castAsDate) include (ReportDate)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-31
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 2014-02-13
      • 1970-01-01
      • 1970-01-01
      • 2014-03-17
      相关资源
      最近更新 更多