【问题标题】:Query with and without parameters -> totally different execution time带参数和不带参数的查询 -> 完全不同的执行时间
【发布时间】:2019-08-31 23:23:03
【问题描述】:

我确实有一个我不明白的问题。

我有两个条件的查询。这个查询很慢,所以我创建了一个索引。在此之后,我有某种奇怪的行为。 如果我直接运行查询 ... WHERE xxx=1234 结果将在 4 毫秒内送达 当我使用像

这样的参数时
DECLARE @P1 bigint
SET @P1=1234

...WHERE xxx=@P1 

结果将在 80k ms 内交付

我找到了一些关于参数嗅探的信息 - 我将其停用 - 相同的行为。我已经使用

停用了它
ALTER DATABASE SCOPED CONFIGURATION SET PARAMETER_SNIFFING = OFF;

当我运行查询时 OPTION (OPTIMIZE FOR (@P1 = 1234)) 结果将在 4ms 内再次送达。

我的问题是:我没有机会使用 OPTIMIZE FOR 因为 SQL 语句,因为此查询是由程序查询的。

你们能告诉我如何告诉 SQL-Server 使用查询计划,就像他使用不带参数一样?

这是表的 CREATE TABLE:

CREATE TABLE [dbo].[CRM_RO](
    [ID] [bigint] NOT NULL,
    [ID_FI] [bigint] NOT NULL,
    [ID_PE] [bigint] NOT NULL,
    [ID_GENERIC] [bigint] NOT NULL,
    [DateiKurzk] [nchar](4) NOT NULL,
    [RelPosNr] [int] NOT NULL,
    [Partnerrolle] [int] NOT NULL,
    [KopfExtKey] [nvarchar](20) NULL,
    [PosExtKey] [nvarchar](20) NULL,
    [Dokument1] [nvarchar](20) NULL,
    [Dokument2] [nvarchar](20) NULL,
    [SAPAbglStat] [tinyint] NOT NULL,
    [SAPAbglDatum_DT] [bigint] NOT NULL,
    [SAPAbglModus] [tinyint] NOT NULL,
    [FreiK1] [int] NOT NULL,
    [FreiK2] [int] NOT NULL,
    [FreiK3] [int] NOT NULL,
    [FreiK4] [int] NOT NULL,
    [FreiK5] [int] NOT NULL,
    [FreiC1] [nvarchar](40) NULL,
    [FreiC2] [nvarchar](40) NULL,
    [FreiC3] [nvarchar](40) NULL,
    [FreiC4] [nvarchar](40) NULL,
    [FreiC5] [nvarchar](40) NULL,
    [FreiN1] [int] NOT NULL,
    [FreiN2] [int] NOT NULL,
    [FreiN3] [int] NOT NULL,
    [FreiN4] [int] NOT NULL,
    [FreiN5] [int] NOT NULL,
    [FreiD1] [int] NOT NULL,
    [FreiD2] [int] NOT NULL,
    [FreiD3] [int] NOT NULL,
    [FreiD4] [int] NOT NULL,
    [FreiD5] [int] NOT NULL,
    [FreiL1] [bit] NOT NULL,
    [FreiL2] [bit] NOT NULL,
    [FreiL3] [bit] NOT NULL,
    [FreiL4] [bit] NOT NULL,
    [FreiL5] [bit] NOT NULL,
    [FreiDez1] [float] NOT NULL,
    [FreiDez2] [float] NOT NULL,
    [FreiDez3] [float] NOT NULL,
    [FreiDez4] [float] NOT NULL,
    [FreiDez5] [float] NOT NULL,
    [Neu] [bigint] NOT NULL,
    [Upd] [bigint] NOT NULL,
    [UpdL] [bigint] NOT NULL,
    [LosKZ] [bit] NOT NULL,
    [AstNr] [int] NOT NULL,
    [KomKz] [bit] NOT NULL,
    [RKZ] [binary](30) NOT NULL,
    [Inaktiv] [bit] NOT NULL,
    [DatumVon] [int] NOT NULL,
    [DatumBis] [int] NOT NULL,
    [UPD_FIELD] [varbinary](334) NULL,
    [MNO] [int] NOT NULL,
    [F7000] [int] NOT NULL,
    [F7002] [int] NOT NULL,
    [F7004] [nvarchar](35) NULL,
    [F7005] [nvarchar](35) NULL,
    [F7006] [nvarchar](35) NULL,
    [F7007] [nvarchar](35) NULL,
    [F7008] [nvarchar](35) NULL,
    [F7009] [int] NOT NULL,
    [F7010] [nvarchar](35) NULL,
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]

GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [ID]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [ID_FI]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [ID_PE]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [ID_GENERIC]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ('') FOR [DateiKurzk]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [RelPosNr]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [Partnerrolle]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [SAPAbglStat]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [SAPAbglDatum_DT]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [SAPAbglModus]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiK1]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiK2]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiK3]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiK4]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiK5]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiN1]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiN2]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiN3]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiN4]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiN5]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiD1]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiD2]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiD3]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiD4]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiD5]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiL1]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiL2]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiL3]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiL4]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiL5]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiDez1]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiDez2]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiDez3]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiDez4]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [FreiDez5]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [Neu]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [Upd]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [UpdL]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [LosKZ]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [AstNr]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [KomKz]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT (0x) FOR [RKZ]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [Inaktiv]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [DatumVon]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [DatumBis]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [MNO]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [F7000]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [F7002]
GO

ALTER TABLE [dbo].[CRM_RO] ADD  DEFAULT ((0)) FOR [F7009]
GO

这是创建索引代码。请不要对“缺失索引”这个名称感到困惑。这只是因为我使用了语法。我从 ORDER BY 中创建了索引:

CREATE INDEX [QS_missing_index_583420_583419_CRM_RO] ON [CRM].[dbo].[CRM_RO] (ID_FI,ID_PE,DateiKurzk,ID_GENERIC,RelPosNr,Partnerrolle)

【问题讨论】:

  • 您是如何“停用”参数嗅探的?此外,除非 1234 将成为 执行的参数值,否则您不希望将此值与 optimize for 一起使用。通过Paste The Plan 发布您的DDL,包括CREATE TABLECREATE INDEX 语句,以及两个执行计划。对于您的最后一个问题,如果您每次都想要一个新的 计划,那么您会想使用option (recompile),但通常情况下不是
  • 最后但并非最不重要的是我如何停用参数嗅探:ALTER DATABASE SCOPED CONFIGURATION SET PARAMETER_SNIFFING = OFF;
  • 将其作为原始帖子的一部分。
  • 请不要在 cmets 中发布其他信息,而是编辑您的问题并将所有相关信息放在那里。
  • 您的表格设计似乎存在一些规范化问题。你有什么似乎是违反 1NF 的重复组。 en.wikipedia.org/wiki/First_normal_form

标签: sql sql-server performance tsql sqlperformance


【解决方案1】:

这是我们在应用程序中使用存储过程而不是实际查询的原因之一。如果需要调整查询,就像这个一样,更改存储过程比打开应用程序要简单得多。

话虽如此,打开应用程序并将查询交换为存储过程确实是最好的答案。

调整此查询的唯一其他可能方法是通过查询存储。看看这个页面:https://blogs.technet.microsoft.com/dataplatform/2017/01/31/query-store-how-it-works-how-to-use-it/

特别是“1)可以比较计划,在参数嗅探的情况下特别有用”和“2)也可以强制计划”部分。

【讨论】:

    【解决方案2】:

    对于参数嗅探,您不必关闭该选项。您可以为参数设置局部变量并使用它们。如果查看执行计划,您将看到 SQL Server 不再缓存参数值。

    我并没有遇到很多问题,但是遇到过查询需要

    CREATE PROCEDURE dbo.Test ( @var int )
    AS
    DECLARE 
        @_var int
    SELECT 
        @_var = @var
    
    SELECT * 
    FROM dbo.SomeTable 
    WHERE
        Id = @_var
    

    我会试试这个,看看是否有帮助。

    您也可以优化未知数,但我不确定这是否会对您有所帮助。

    由于您创建了索引,您还可以尝试在连接中添加表提示,以确保 SQL Server 在其他调用它时使用该索引(例如SELECT * FROM dbo.TableName WITH (INDEX(ix_Index)))。一般来说,我会尽量避免这样做。

    【讨论】:

      猜你喜欢
      • 2017-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-19
      相关资源
      最近更新 更多