【问题标题】:SQL Server: Order By DateDiff Performance issueSQL Server:按 DateDiff 排序性能问题
【发布时间】:2012-07-25 03:48:09
【问题描述】:

我在合理的时间内从包含 2M 行的表中获取前 100 行时遇到问题。 问题是按部分排序,此查询需要 50 多分钟才能得到结果。 这个问题的最佳解决方案是什么?

select top 100 * from THETABLE TT
Inner join SecondTable ST on TT.TypeID = ST.TypeID
ORDER BY DATEDIFF(Day, TT.LastCheckDate, GETDATE()) * ST.SomeParam DESC

非常感谢,

奔腾

编辑: * TheTable 是具有 2M 行的表。 * SomeParam 有 15 个不同的值(或多或少)

【问题讨论】:

  • MySQL ? SQL 服务器?另外,向我们展示结构和数据类型!
  • 哎哟。 ORDER BY 子句有效地阻止了索引的使用。
  • 有多少不同的 ST.SomeParam 值?
  • 哪个表大? TT,ST,或两者兼而有之?另外,SomeParam 多久更改一次?
  • THETABLE TT 是 2M 行的表。 SomeParam 每月更改一次。 SomeParam 有大约 10-20 个不同的值,而且不会超过这个值。

标签: sql-server performance sql-order-by datediff


【解决方案1】:

为了加快这个抓取速度,我想到了两件事:

  1. 如果您需要经常运行此查询,您应该索引列“lastCheckDate”。无论您使用的是哪个 sql db,列上定义明确的索引将允许更快的选择,尤其是在 order by 子句中。

  2. 在执行选择查询之前执行日期数学运算。您将获得行的 checkDate 和当前日期之间的天数差,乘以某个参数。乘法会影响行的顺序吗?这可以简单地由'lastCheckDate desc'订购吗? 探索其他返回相同结果的排序选项

【讨论】:

    【解决方案2】:

    想到两个想法:

    a) 如果 ST.param 不经常更改,也许您可​​以在某处缓存乘法的结果。一天后数字将“关闭”,但相对值将相同 - 即排序顺序不会改变。

    b) 找到减小输入表大小的方法。 LastCheckDate 和/或 SomeParam 的某些值可能永远不会进入前 100 名。例如,

    Select * 
    into #tmp 
    from THETABLE 
    where LastCheckDate between '2012-06-01' and getdate()
    
    
    select top 100 *
    from #tmp join SecondTable ST on #tmp.TypeID = ST.TypeID
    order by DateDiff(day, LastCheckDate, getdate()) * ST.SomeParam desc
    

    搜索小表比搜索大表快得多。

    【讨论】:

    • 使用临时表是尝试解决此问题的一种创造性方法,但我找不到正确的方法(到目前为止......)。 “在'2012-06-01'之间”不是我可以使用的,因为我不知道提前的原始日期
    • 也许只是select top 1000 * into #tmp from THETABLE order by lastcheckdate desc?我们的想法是从 THETABLE 中获取健康的一把,但省略 90% 或更多
    【解决方案3】:

    DATEDIFF(Day, TT.LastCheckDate, GETDATE()) 是自“上次检查”以来的天数。

    如果您只是通过TT.LastCheckDate 订购,您会得到类似的订单。

    编辑 也许您可以计算出您不希望返回的日期并对其进行过滤。当然,您还需要该 LastDateCheck 列上的索引。如果一切顺利,您至少可以将要检查的记录列表从 2M 缩短到可管理的数量。

    【讨论】:

    • 但他乘以 St.SomeParam,不一定是常数。
    • 我需要将日期乘以“* ST.SomeParam” 据我所知,仅按 TT.LastCheckDate 字段排序是无法做到的...
    • @user1199838:虽然您必须将来自两个单独表的值相乘以产生排序值,但您不会从该查询中获得良好的性能。很少有技术可以加快速度。即使是功能索引之类的东西也无济于事。他们一次被限制在一张桌子上,AFAIK。由于最旧的行最有可能出现,您也许可以在主查询中编写一个过滤条件来限制必须排序的行数,但这就是我能想到的全部。
    【解决方案4】:

    这很复杂。您真的需要查询中的所有列吗?您可以在这里尝试一件事。首先只需获取前 100 行 typeid

    如下所示

    select top 100 typeid
    ,TT.lastcheckdate,st.someparam --do not use these if the typeid is unqiue in both tables..
    --or just the PK columns of both tables and typeid so that these can be joined on PK
    into #temptable
    from st inner join tt on st.typeid = tt.typeid 
    ORDER BY DATEDIFF(Day, TT.LastCheckDate, GETDATE()) * ST.SomeParam DESC 
    

    上面将对非常少的数据进行排序,因此应该更快。根据表和索引中有多少列,这应该会更快(如果两个表中都有很多列,它会很快,但是这个查询将使用只是 3.Also,也许这些列(st.typeid、st.someparam 和 tt.typeid 和 tt.lastcheckdate)被一些索引覆盖,因此不需要读取底层表,从而也减少了 IO)而不是实际的。 .然后将此数据连接回两个表。

    如果这不按您期望的方式工作。那么您可以通过将表达式的顺序添加为列来使用上面的选择来索引视图。然后使用这个索引视图获得前 100 名并与主表连接。这肯定会减少工作量,从而提高性能。但是索引视图会有开销,这取决于表 TT 中数据更改的频率。

    【讨论】:

      【解决方案5】:

      为了减少行数,您可能会为 LastCheckDate 排序的每条 SecondTable 记录检索前 (100) 行,然后通过临时表或动态 sql 生成的查询将所有它们合并并最终选择前 (100)。

      此解决方案使用游标获取 SecondTable 中每个值的前 100 条记录。使用 TheTable 上的 (TypeID, LastCheckDate) 索引,它会立即运行(在我的系统上使用包含 700,000 条记录和 50 个 SecondTable 条目的表进行测试)。

      declare @SomeParam varchar(3)
      declare @TypeID int
      
      declare @tbl table (TheTableID int, LastCheckDate datetime, SomeParam float)
      
      declare rstX cursor local fast_forward for
            select TypeID, SomeParam
              from SecondTable
      
      open rstX
      while 1 = 1
      begin
          fetch next from rstX into @TypeID, @SomeParam
          if @@fetch_status <> 0
             break
          insert into @tbl
          select top 100 ID, LastCheckDate, @SomeParam
            from TheTable
           where TypeID = @TypeID
           order by LastCheckDate
      end
      close rstX
      deallocate rstX
      
      select top 100 *
      from @tbl
      order by DATEDIFF(Day, LastCheckDate, GETDATE()) * SomeParam
      

      显然,此解决方案仅获取 ID。您可能希望使用其他列来扩展临时表。

      【讨论】:

        猜你喜欢
        • 2016-03-02
        • 1970-01-01
        • 2011-11-23
        • 1970-01-01
        • 2019-04-13
        • 1970-01-01
        • 2017-01-01
        • 1970-01-01
        相关资源
        最近更新 更多