AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 165626
Accepted
Vojtěch Dohnal
Vojtěch Dohnal
Asked: 2017-02-28 05:17:36 +0800 CST2017-02-28 05:17:36 +0800 CST 2017-02-28 05:17:36 +0800 CST

使用表变量而不是临时表会使查询执行速度变慢

  • 772

我有一个表,其中包含有关AutoData具有组合聚集键Cas(DateTime) + GCom(Car ID) 的汽车的历史数据。一条记录包含各种指标,如燃油油位、车辆状态等。

表中一辆车的各个记录之间的间隔AutoData是不规则的,有时是 120 秒,有时是几秒,有时是几小时等。我需要对记录进行规范化查看,以便每 30 秒显示一条记录。

我有以下脚本:

DECLARE @GCom int = 2563,
    @Od DateTime2(0) = '20170210', 
    @Do DateTime2(0) = '20170224'    

--Create a table with intervals by 30 seconds
declare @temp Table ([cas] datetime2(0))
INSERT @temp([cas])
SELECT d
FROM
(
  SELECT
      d = DATEADD(SECOND, (rn - 1)*30, @Od)
  FROM 
  (
      SELECT TOP (DATEDIFF(MINUTE, @Od, @Do)*2) 
          rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
      FROM
          sys.all_objects AS s1
      CROSS JOIN
          sys.all_objects AS s2
      ORDER BY
          s1.[object_id]
  ) AS x
) AS y;

--Create temp table
CREATE TABLE #AutoData (
    [Cas] [datetime2](0) NOT NULL PRIMARY KEY,
    [IDProvozniRezim] [tinyint] NOT NULL,
    [IDRidic] [smallint] NULL,
    [Stav] [tinyint] NOT NULL,
    [Klicek] [bit] NOT NULL,
    [Alarm] [bit] NOT NULL,
    [MAlarm] [tinyint] NOT NULL,
    [DAlarm] [bit] NOT NULL,
    [Bypass] [bit] NOT NULL,
    [Lat] [real] NULL,
    [Lon] [real] NULL,
    [ObjemAktualni] [real] NOT NULL,
    [RychlostMaxV1] [real] NOT NULL,
    [RychlostV2] [real] NOT NULL,
    [Otacky] [smallint] NOT NULL,
    [Nadspotreba] [real] NOT NULL,
    [Vzdalenost] [real] NOT NULL,
    [Motor] [smallint] NOT NULL
)

--Populate the temp table selecting only relevant AutoData records
INSERT INTO #AutoData
SELECT [Cas]
      ,[IDProvozniRezim]
      ,[IDRidic]
      ,[Stav]
      ,[Klicek]
      ,[Alarm]
      ,[MAlarm]
      ,[DAlarm]
      ,[Bypass]
      ,[Lat]
      ,[Lon]
      ,[ObjemAktualni]
      ,[RychlostMaxV1]
      ,[RychlostV2]
      ,[Otacky]
      ,[Nadspotreba]
      ,[Vzdalenost]
      ,[Motor]
FROM AutoData a 
WHERE a.GCom = @GCom AND a.cas BETWEEN @Od AND @do

--Select final data
SELECT t.cas, ad.malarm, ad.IDProvoznirezim, ad.Otacky, ad.motor, ad.objemAktualni, ad.Nadspotreba 
FROM @temp t
OUTER APPLY (
SELECT TOP 1 stav, malarm, otacky,motor, objemAktualni, Nadspotreba, IDProvoznirezim  FROM #AutoData a
                     WHERE DATEDIFF(SECOND, a.cas, t.cas)<=CASE WHEN Motor>120 THEN Motor ELSE 120 END 
                     AND DATEDIFF(SECOND,  a.cas, t.cas)>-30 
                     ORDER BY CASE WHEN DATEDIFF(SECOND, a.cas, t.cas)>0 THEN DATEDIFF(SECOND, a.cas, t.cas) ELSE (DATEDIFF(SECOND, a.cas, t.cas)*-1) +120 END
) ad

DROP TABLE #AutoData

起初,我尝试使用只有一个表变量@temp 将条件WHERE a.GCom = @GCom AND a.cas BETWEEN @Od AND @do放在最后一个选择中来编写脚本。该脚本需要 39 秒才能执行。

当我使用#AutoData临时表在临时表中预加载数据子集时,如上面的脚本所示,它下降到 5 秒。

然后我尝试使用表变量@AutoData而不是#AutoData- 但它又花了更长的时间 - 22秒。

@temp对于这个例子,表有 40320 条记录,#AutoData表有 1904 条记录。但令人惊讶的是,仅使用#temp表而不是@temp变量会使执行再次变慢。

我很惊讶地看到使用或不使用临时表/变量的这种差异。显然,SQL Server 本身无法优化 OUTER APPLY 子句的内部。

但是为什么使用表变量和临时表会有这么大的区别呢?有没有其他方法可以知道,使用什么而不只是尝试它?


带有临时表#AutoData的执行计划: 临时表

https://www.brentozar.com/pastetheplan/?id=B1y2x2Zcg

带有变量@AutoData 的执行计划: 多变的

https://www.brentozar.com/pastetheplan/?id=r1rAZnbqx

sql-server sql-server-2008
  • 2 2 个回答
  • 4436 Views

2 个回答

  • Voted
  1. Best Answer
    Brent Ozar
    2017-02-28T05:47:42+08:002017-02-28T05:47:42+08:00

    关键在于您问题的这一部分:

    @temp 表有 40320 条记录

    在执行计划中,将鼠标悬停在@temp 表的扫描上。比较估计的行数和实际的行数。(如果您想在http://PasteThePlan.com上发布该计划,我们可以为您提供更具体的细节。免责声明:这是我公司的网站。)

    您将看到估计的行数非常低。

    SQL Server 估计 1-3 行将从表变量中返回(取决于您的 SQL Server 版本、基数估计器、跟踪标志等)。这反过来给您一个非常糟糕的执行计划,因为 SQL Server 低估了多少工作它需要从其他表中提取多少内存,等等。

    以下是获得更准确估计的两种最流行的方法:

    • 尝试使用临时表(并查看计划中的估计行与实际行)
    • 在查询中使用 OPTION (RECOMPILE) - 这将使您获得更精确的估计,但在计划缓存可见性和 CPU 使用率方面存在一些非常大的缺点

    要看到我现场直播,请观看 1 小时Watch Brent Tune Queries(免责声明:就是我,链接到我的视频),我在其中使用表变量进行 Stack Overflow 查询,并在前面进行现场调整SQL Rally 挪威的观众。

    • 4
  2. paparazzo
    2017-02-28T05:32:58+08:002017-02-28T05:32:58+08:00

    查询计划器使用#temp 更有效。在表变量上,它只考虑前几行。

    您的表变量(以及 #temp 如果您使用一个)可能会受益于声明主键。

    在#AutoData 上放一个键,只填充必要的行。

    在添加行时按键排序。

    我怀疑下面可以用 row_number() 进行优化

    SELECT t.cas
         , ad.malarm, ad.IDProvoznirezim, ad.Otacky
         , ad.motor, ad.objemAktualni, ad.Nadspotreba 
    FROM @temp t
    OUTER APPLY ( SELECT TOP 1 malarm, IDProvoznirezim, Otacky
                             , motor, objemAktualni, Nadspotreba   
                   FROM #AutoData a
                  WHERE DATEDIFF(SECOND, a.cas, t.cas) <= CASE WHEN Motor > 120 THEN Motor ELSE 120 END 
                    AND DATEDIFF(SECOND, a.cas, t.cas)  > -30 
                  ORDER BY CASE WHEN DATEDIFF(SECOND, a.cas, t.cas) > 0 THEN DATEDIFF(SECOND, a.cas, t.cas) 
                                ELSE DATEDIFF(SECOND, t.cas, a.cas) + 120 END
                ) ad  
    

    这是作为 row_number() 的尝试

    select * from 
    ( SELECT t.cas
           , a.malarm, a.IDProvoznirezim, a.Otacky
           , a.motor, a.objemAktualni, a.Nadspotreba 
           , row_nunber() over (partition by t.cas 
                                ORDER BY CASE WHEN DATEDIFF(SECOND, a.cas, t.cas) > 0 THEN DATEDIFF(SECOND, a.cas, t.cas) 
                                ELSE DATEDIFF(SECOND, t.cas, a.cas) + 120 END) rn
        FROM @temp t  -- with primay key t.cas order by
        join AutoData a
          on a.GCom = @GCom 
         AND a.cas BETWEEN @Od AND @do
         AND DATEDIFF(SECOND, a.cas, t.cas) <= CASE WHEN Motor > 120 THEN Motor ELSE 120 END 
         AND DATEDIFF(SECOND, a.cas, t.cas)  > -30 
    ) ad 
    where ad.rn = 1  
    
    • 1

相关问题

  • 死锁的主要原因是什么,可以预防吗?

  • 我在索引上放了多少“填充”?

  • 是否有开发人员遵循数据库更改的“最佳实践”类型流程?

  • 如何确定是否需要或需要索引

  • 从 SQL Server 2008 降级到 2005

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve