标签: optimization

使用 RECOMPILE 查询提示时查询之间的执行时间存在巨大差异

我有两个几乎相同的查询在同一个 SQL Server 2005 实例上运行:

  1. 第一个是SELECT由 LINQ 生成的原始查询(我知道,我知道......我不是应用程序开发人员,只是 DBA :)。
  2. 第二个和第一个完全一样,OPTION (RECOMPILE)在最后加了一个。

没有其他任何改变。

第一个每次运行需要 55 秒。
第二个需要 2 秒。

两个结果集是相同的。

为什么这个提示会产生如此显着的性能提升?

在线图书条目RECOMPILE没有提供非常详细的解释:

指示 SQL Server 数据库引擎在执行后放弃为查询生成的计划,强制查询优化器在下次执行相同查询时重新编译查询计划。在不指定 RECOMPILE 的情况下,数据库引擎会缓存查询计划并重用它们。编译查询计划时,RECOMPILE 查询提示使用查询中任何局部变量的当前值,如果查询在存储过程中,则将当前值传递给任何参数。

RECOMPILE 是创建使用 WITH RECOMPILE 子句的存储过程的一种有用的替代方法,当必须重新编译存储过程中的查询子集而不是整个存储过程时。有关详细信息,请参阅重新编译存储过程。RECOMPILE 在您创建计划指南时也很有用。有关更多信息,请参阅使用计划指南优化已部署应用程序中的查询。

由于我的查询有很多局部变量,我的猜测是当我使用OPTION (RECOMPILE)查询提示时,SQL Server 能够(认真地)优化它。

我所看到的每个地方都有人说OPTION (RECOMPILE)应该避免这种情况。对此的解释通常是使用此提示 SQL Server 无法重用此执行计划,因此每次都必须浪费时间重新编译它。
(但是)考虑到巨大的性能优势,我倾向于认为这次使用这个查询提示会是一件好事。

我应该使用它吗?如果没有,有没有一种方法可以强制 SQL Server 使用更好的执行计划而无需此提示且无需更改应用程序?

performance sql-server-2005 sql-server optimization query-performance

16
推荐指数
1
解决办法
2429
查看次数

是否可以提高具有数百万行的窄表的查询性能?

我有一个查询目前平均需要 2500 毫秒才能完成。我的表很窄,但有 4400 万行。我有什么选择可以提高性能,或者这是否已经达到了最好的效果?

查询

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31'; 
Run Code Online (Sandbox Code Playgroud)

桌子

CREATE TABLE [dbo].[Heartbeats](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [DeviceID] [int] NOT NULL,
    [IsPUp] [bit] NOT NULL,
    [IsWebUp] [bit] NOT NULL,
    [IsPingUp] [bit] NOT NULL,
    [DateEntered] [datetime] NOT NULL,
 CONSTRAINT [PK_Heartbeats] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

指数

CREATE NONCLUSTERED INDEX …
Run Code Online (Sandbox Code Playgroud)

performance sql-server optimization query-performance

16
推荐指数
2
解决办法
7807
查看次数

好、坏或无所谓:哪里 1=1

鉴于reddit 上的这个问题,我清理了查询以指出问题在查询中的位置。我首先使用逗号WHERE 1=1并使修改查询更容易,所以我的查询通常是这样结束的:

SELECT 
     C.CompanyName
    ,O.ShippedDate
    ,OD.UnitPrice
    ,P.ProductName
FROM 
               Customers       as C
    INNER JOIN Orders          as O  ON C.CustomerID = O.CustomerID
    INNER JOIN [Order Details] as OD ON O.OrderID    = OD.OrderID
    INNER JOIN Products        as P  ON P.ProductID  = OD.ProductID
Where 1=1
--  AND O.ShippedDate Between '4/1/2008' And '4/30/2008'
    And P.productname = 'TOFU'
Order By C.CompanyName
Run Code Online (Sandbox Code Playgroud)

有人基本上说1=1 通常是懒惰的,而且对性能不利

鉴于我不想“过早优化” - 我确实想遵循良好的做法。我以前看过查询计划,但通常只是为了找出我可以添加(或调整)哪些索引以使我的查询运行得更快。

那么问题真的……会Where 1=1导致不好的事情发生吗?如果是这样,我怎么知道?

次要编辑:我也一直“假设”1=1会被优化,或者在最坏的情况下可以忽略不计。质疑一句口头禅永远不会有什么坏处,比如“Goto's are Evil”或“过早优化......”或其他假设的事实。不确定是否1=1 AND会实际影响查询计划。在子查询中呢?CTE的?手续?

除非需要,否则我不是要优化的人……但如果我正在做一些实际上“不好”的事情,我想尽量减少影响或在适用的情况下进行更改。

performance optimization

16
推荐指数
3
解决办法
1万
查看次数

如何使用执行计划优化 T-SQL 查询

我有一个 SQL 查询,我花了两天时间尝试使用试错法和执行计划进行优化,但无济于事。请原谅我这样做,但我会在这里发布整个执行计划。我已经努力使查询和执行计划中的表名和列名通用,既为了简洁又为了保护我公司的 IP。可以使用SQL Sentry Plan Explorer打开执行计划。

我已经完成了大量的 T-SQL,但是使用执行计划来优化我的查询对我来说是一个新领域,我真的试图了解如何去做。所以,如果有人能帮我解决这个问题并解释如何破译这个执行计划以在查询中找到优化它的方法,我将永远感激不尽。我还有更多的查询需要优化——我只需要一个跳板来帮助我完成第一个查询。

这是查询:

DECLARE @Param0 DATETIME     = '2013-07-29';
DECLARE @Param1 INT          = CONVERT(INT, CONVERT(VARCHAR, @Param0, 112))
DECLARE @Param2 VARCHAR(50)  = 'ABC';
DECLARE @Param3 VARCHAR(100) = 'DEF';
DECLARE @Param4 VARCHAR(50)  = 'XYZ';
DECLARE @Param5 VARCHAR(100) = NULL;
DECLARE @Param6 VARCHAR(50)  = 'Text3';

SET NOCOUNT ON

DECLARE @MyTableVar TABLE
(
    B_Var1_PK int,
    Job_Var1 varchar(512),
    Job_Var2 varchar(50)
)

INSERT INTO @MyTableVar (B_Var1_PK, Job_Var1, Job_Var2) 
SELECT B_Var1_PK, Job_Var1, Job_Var2 FROM [fn_GetJobs] (@Param1, @Param2, @Param3, @Param4, …
Run Code Online (Sandbox Code Playgroud)

sql-server-2008 sql-server optimization t-sql execution-plan

16
推荐指数
1
解决办法
2万
查看次数

Oracle 没有为长键使用唯一索引

我的测试数据库中有一个包含 250K 行的表。(生产中有几亿个,我们可以在那里观察到同样的问题。)该表有一个 nvarchar2(50) 字符串标识符,不为空,上面有一个唯一索引(不是 PK)。

标识符由第一部分组成,它在我的测试数据库中有 8 个不同的值(生产中大约有 1000 个),然后是一个 @ 符号,最后是一个 1 到 6 位长的数字。例如,可能有 5 万行以“ABCD_BGX1741F_2006_13_20110808.xml@”开头,后面跟着 5 万个不同的数字。

当我根据其标识符查询单行时,基数估计为1,成本很低,工作正常。当我在 IN 表达式或 OR 表达式中查询多个具有多个标识符的行时,对索引的估计是完全错误的,因此使用了全表扫描。如果我用提示强制索引,它会非常快,全表扫描实际上执行的速度要慢一个数量级(并且在生产中要慢得多)。所以这是一个优化器问题。

作为测试,我使用完全相同的 DDL 和完全相同的内容复制了表(在相同的模式+表空间中)。我在第一个表上重新创建了唯一索引以获得良好的度量,并在克隆表上创建了完全相同的索引。我做了一个DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);. 您甚至可以看到索引名称是连续的。所以现在这两个表唯一的区别是第一个是在很长一段时间内以随机顺序加载的,块分散在磁盘上(与其他几个大表一起在一个表空间中),第二个是作为一个批处理加载的插入-选择。除此之外,我想不出有什么不同。(自上次大删除以来,原始表已缩小,此后没有进行过一次删除。)

这里是sick和clone表的查询计划(黑色笔刷下的字符串全图相同,灰色笔刷下也是如此。):

查询计划

(在这个例子中,有1867行以刷黑的标识符开始。2行查询产生1867*2的基数,3行查询产生1867*3的基数,等等。不能巧合的是,Oracle 似乎并不关心标识符的结尾。)

什么可能导致这种行为?显然,在生产中重新创建表会非常昂贵。

USER_TABLES:http : //i.stack.imgur.com/nDWze.jpg USER_INDEXES:http : //i.stack.imgur.com/DG9um.jpg我只更改了架构和表空间名称。您可以看到表和索引名称与查询计划屏幕截图中的相同。

oracle optimization oracle-11g-r2

16
推荐指数
3
解决办法
2232
查看次数

对 PostgreSQL 的 GEQO(遗传查询优化)的修改

我需要实现一个符合 PostgreSQL 的 GEQO 功能的功能。我知道 GEQO 方法是将查询计划编码为整数字符串,而 GEQO 会随机生成这些可能的连接序列。来源:http : //www.postgresql.org/docs/9.3/static/geqo-pg-intro.html

我的问题:如果我明确知道正确的连接序列,如何修改 GEQO 函数,这样我就不必搜索不同的连接序列。例如,如果我知道加入 4 个关系的最佳方式是 4-1-3-2,我就不必检查其他排列。

关于如何在 PostgreSQL 中实现 GEQO 没有任何好的材料。PostgreSQL 只给出了 GEQO 功能的整体视图,但并没有做太多解释。

或者我可以在不使用 GEQO 的情况下在 standard_join_search() 本身中实现这个功能吗?

postgresql optimization

16
推荐指数
1
解决办法
862
查看次数

为什么在连接谓词中引用变量会强制嵌套循环?

最近遇到了这个问题,在网上找不到任何讨论。

下面的查询

DECLARE @S VARCHAR(1) = '';

WITH T
     AS (SELECT name + @S AS name2,
                *
         FROM   master..spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2
         ON T1.name2 = T2.name2;
Run Code Online (Sandbox Code Playgroud)

总是得到一个嵌套循环计划

在此处输入图片说明

尝试使用INNER HASH JOININNER MERGE JOIN提示强制问题会产生以下错误。

由于此查询中定义的提示,查询处理器无法生成查询计划。在不指定任何提示且不使用 SET FORCEPLAN 的情况下重新提交查询。

我找到了一种允许使用散列或合并连接的解决方法 - 将变量包装在聚合中。生成的计划成本显着降低(19.2025 与 0.261987)

DECLARE @S2 VARCHAR(1) = '';

WITH T
     AS (SELECT name + (SELECT MAX(@S2)) AS name2,
                *
         FROM   spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2 …
Run Code Online (Sandbox Code Playgroud)

performance sql-server optimization query-performance

16
推荐指数
1
解决办法
787
查看次数

谓词:在 WHERE 子句还是 JOIN 子句中?

查看Kalen Delaney 撰写的“SQL Server 2008 Internals” 1,第 13 页,它说明了以下内容:

“生成这样一个计划的第一步是规范化每个查询,这可能会将单个查询分解为多个细粒度的查询。查询优化器在规范化一个查询之后对其进行优化,这意味着它确定了一个计划执行该查询。”

另一位 DBA 向我建议,可以通过将WHERE子句谓词移动到FROM子句中来提高某些查询的性能,例如:

SELECT *
FROM dbo.table1 t1
    INNER JOIN dbo.table3 t3 ON t1.ID = t3.ID
    LEFT OUTER JOIN dbo.table2 t2 ON t1.ID = t2.ID
WHERE t1.CreateDate >= '2015-07-31 00:00:00';
Run Code Online (Sandbox Code Playgroud)

会成为:

SELECT *
FROM dbo.table1 t1
    INNER JOIN dbo.table3 t3 ON t1.ID = t3.ID
        AND t1.CreateDate >= '2015-07-31 00:00:00'
    LEFT OUTER JOIN dbo.table2 t2 ON t1.ID = t2.ID;
Run Code Online (Sandbox Code Playgroud)

显然,第一个示例的含义是查询优化器将执行第JOIN一个, …

sql-server optimization

16
推荐指数
1
解决办法
5930
查看次数

当我添加连接提示时,为什么 SQL Server 行估计会发生变化?

我有一个查询,它连接了几个表并且执行得非常糟糕 - 行估计偏离了(1000 次)并且选择了嵌套循环连接,从而导致多个表扫描。查询的形状相当简单,看起来像这样:

SELECT t1.id
FROM t1
INNER JOIN t2 ON t1.id = t2.t1_id
LEFT OUTER JOIN t3 ON t2.id = t3.t2_id
LEFT OUTER JOIN t4 ON t3.t4_id = t4.id 
WHERE t4.id = some_GUID
Run Code Online (Sandbox Code Playgroud)

玩弄查询时,我注意到当我提示它对其中一个连接使用合并连接时,它的运行速度要快很多倍。这我可以理解 - 合并连接是连接数据的更好选择,但 SQL Server 只是没有正确选择嵌套循环。

我不完全理解的是为什么这个连接提示会改变所有计划运营商的所有估计?通过阅读不同的文章和书籍,我假设基数估计是在构建计划之前执行的,因此使用提示不会改变估计,而是明确告诉 SQL Server 使用特定的物理连接实现。

然而,我看到的是合并提示使所有估计变得非常完美。为什么会发生这种情况,是否有任何通用技术可以使查询优化器在没有提示的情况下做出更好的估计 - 考虑到统计数据显然允许这样做?

UPD:匿名执行计划可以在这里找到:https ://www.dropbox.com/s/hchfuru35qqj89s/merge_join.sqlplan?dl =0 https://www.dropbox.com/s/38sjtv0t7vjjfdp/no_hints_join.sqlplan?dl =0

我使用 TF 3604、9292 和 9204 检查了这两个查询使用的统计数据,它们是相同的。然而,被扫描/搜索的索引在查询之间是不同的。

除此之外,我尝试运行查询OPTION (FORCE ORDER)- 它比使用合并联接运行得更快,为每个联接选择 HASH MATCH。

performance sql-server optimization t-sql performance-tuning

16
推荐指数
1
解决办法
584
查看次数

为什么当我索引列时这个 sqlite 查询要慢得多?

我有一个带有两个表的 sqlite 数据库,每个表有 50,000 行,包含(假)人的名字。我构建了一个简单的查询来找出有多少个名字(名字、中间名首字母、姓氏)是两个表共有的:

select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;
Run Code Online (Sandbox Code Playgroud)

当除了主键上没有索引(与此查询无关)时,它运行得很快:

[james@marlon Downloads] $ time sqlite3 generic_data_no_indexes.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    0m0.115s
user    0m0.111s
sys     0m0.004s
Run Code Online (Sandbox Code Playgroud)

但是如果我为每个表的三列添加索引(总共六个索引):

CREATE INDEX `idx_uk_givenname` ON `fakenames_uk` (`givenname` )
//etc.
Run Code Online (Sandbox Code Playgroud)

然后它运行得很慢:

[james@marlon Downloads] $ time sqlite3 generic_data.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    1m43.102s
user    0m52.397s
sys …
Run Code Online (Sandbox Code Playgroud)

sqlite performance index optimization count

16
推荐指数
1
解决办法
2万
查看次数