3

我有一个MERGE查询要使用通过我的应用程序输入的数据在我的数据库上创建一个 upsert 操作。当我调用它来保存我的大交易数据(> 5000)时,它需要长时间(~20-40 秒)。

这是我的MERGE声明

MERGE TestTable AS target USING (SELECT @Guid) AS source (target.Guid = source.Guid)
WHEN MATCHED THEN
  UPDATE TestTable SET Column1 = @Column1, Column2 = @Column2 WHERE Guid = @Guid
WHEN NOT MATCHED THEN
  INSERT INTO TestTable (Column1, Column2) VALUES (@Column1, @Column2)
OUTPUT $action

我在我的 .NET 代码中一次在一个对象上调用它。

我在 SQL Express 2008 Activity Monitor 的 Activity Monitor 中注意到,由于调用查询时使用的所有不同参数排列,Plan Count 最多可达 900 左右。我还注意到,如果我在不久之后使用稍微不同的参数重复相同的保存,它会保存更快(约 2 秒)。

这是一个潜在的性能问题和长保存时间的原因吗?

我正在使用 SQL Express 2008 R2。

编辑:这是计划:

|--Compute Scalar(DEFINE:([Expr1044]=CASE WHEN [Action1004]=(1) THEN N'UPDATE' ELSE CASE WHEN [Action1004]=(4) THEN N'INSERT' ELSE N'DELETE' END END))
     |--Assert(WHERE:(CASE WHEN NOT [Pass1238] AND [Expr1237] IS NULL THEN (0) ELSE NULL END))
          |--Nested Loops(Left Semi Join, PASSTHRU:([Action1004]=(3) OR [C:\DATABASE.MDF].[dbo].[DoorTable].[CarTable_Guid] as [target].[CarTable_Guid] IS NULL), OUTER REFERENCES:([C:\DATABASE.MDF].[dbo].[DoorTable].[CarTable_Guid]), DEFINE:([Expr1237] = [PROBE VALUE]))
               |--Clustered Index Merge(OBJECT:([C:\DATABASE.MDF].[dbo].[DoorTable].[DoorTable_PK] AS [target]), OBJECT:([C:\DATABASE.MDF].[dbo].[DoorTable].[DoorTable_FI01] AS [target]), SET:(Insert, [C:\DATABASE.MDF].[dbo].[DoorTable].[Column1] as [target].[Column1] = [Expr1005],[C:\DATABASE.MDF].[dbo].[DoorTable].[Column2] as [target].[Column2] = [Expr1006],[C:\DATABASE.MDF].[dbo].[DoorTable].[Column3] as [target].[Column3] = [Expr1007],[C:\DATABASE.MDF].[dbo].[DoorTable].[Column4] as [target].[Column4] = [Expr1008],[C:\DATABASE.MDF].[dbo].[DoorTable].[Column5] as [target].[Column5] = [Expr1009],[C:\DATABASE.MDF].[dbo].[DoorTable].[Column6] as [target].[Column6] = [Expr1010],[C:\DATABASE.MDF].[dbo].[DoorTable].[Column7] as [target].[Column7] = [Expr1011],[C:\DATABASE.MDF].[dbo].[DoorTable].[Column8] as [target].[Column8] = [Expr1012],...
               |    |--Compute Scalar(DEFINE:([Action1004]=[Action1004], [Expr1198]=[Expr1198]))
               |         |--Top(TOP EXPRESSION:((1)))
               |              |--Compute Scalar(DEFINE:([Expr1198]=CASE WHEN [Action1004] = (1) THEN CASE WHEN [Expr1099] THEN (0) ELSE (1) END ELSE [Action1004] END))
               |                   |--Compute Scalar(DEFINE:([Expr1005]=CONVERT_IMPLICIT(nvarchar(64),[@Column1],0), [Expr1006]=CONVERT_IMPLICIT(nvarchar(64),[@Column2],0), [Expr1007]=CONVERT_IMPLICIT(nvarchar(64),[@Column3],0), [Expr1008]=[@Column4], [Expr1009]=CONVERT_IMPLICIT(nvarchar(64),[@Column5],0), [Expr1010]=[@Column6], [Expr1011]=[@Column7], [Expr1012]=CONVERT_IMPLICIT(float(53),[@Column8],0),[Expr1099]=[Action1004]=(1) AND CASE WHEN [C:\DATABASE.MDF].[dbo].[DoorTable].[CarTable_Guid] as [target].[CarTable_Guid] = CONVERT_IMPLICIT(nvarchar(32),[@CarTable_Guid],0) THEN (1) ELSE (0) END))
               |                        |--Compute Scalar(DEFINE:([Action1004]=ForceOrder(CASE WHEN [TrgPrb1002] IS NOT NULL THEN (1) ELSE (4) END)))
               |                             |--Nested Loops(Left Outer Join)
               |                                  |--Constant Scan
               |                                  |--Compute Scalar(DEFINE:([TrgPrb1002]=(1)))
               |                                       |--Clustered Index Seek(OBJECT:([C:\DATABASE.MDF].[dbo].[DoorTable].[DoorTable_PK] AS [target]), SEEK:([target].[Guid]=CONVERT_IMPLICIT(nvarchar(1),[@Guid],0)) ORDERED FORWARD)
               |--Clustered Index Seek(OBJECT:([C:\DATABASE.MDF].[dbo].[CarTable].[CarTable_PK]), SEEK:([C:\DATABASE.MDF].[dbo].[CarTable].[Guid]=[C:\DATABASE.MDF].[dbo].[DoorTable].[CarTable_Guid] as [target].[CarTable_Guid]) ORDERED FORWARD)
4

3 回答 3

1

I've solved the issue I was having. What led me to figure it out was to look at a problem these guys were having: nHibernate Recompiles and Execution Plans

Basically, in the .NET code, it was not defining the DbParameter.Size Property. Since different parameter sizes cause different execution plans to be created, each permutation of all of my parameters were causing different plans to be created and cached.

All I had to do was set the DbParameter.Size to the size of each respective column from my DDL scripts! Wow.

于 2010-09-09T21:27:26.547 回答
1

要验证缓存计划的来源,您可以查询包含所有缓存计划的动态管理视图:

SELECT  text
FROM    sys.dm_exec_cached_plans
CROSS APPLY 
        sys.dm_exec_sql_text(plan_handle)
WHERE   text LIKE ‘%SnippetFromYourQuery%’

尽管看起来您正在正确地参数化查询,但您可以通过打开强制参数化来测试它:

alter database YourDb forced

如果这减少了运行时间,您应该调查查询的哪一部分包含硬编码值而不是参数。SQL Profiler 应该让这一切变得简单。

于 2010-09-08T21:59:24.893 回答
1

与其在循环5000时间内执行此操作,不如将其包装到一个存储过程中,该存储过程将一个TABLE值作为输入,并执行批量更新:

CREATE TYPE paramTable AS TABLE
        (
        guid UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
        column1 INT,
        column2 VARCHAR(100)
        )

CREATE PROCEDURE prcMergeInput(@mytable paramTable)
AS 
        MERGE   TestTable AS target
        USING   (
                SELECT  column1, column2, guid
                FROM    @mytable
                ) AS source
        ON      (target.Guid = source.Guid)
        WHEN MATCHED THEN
                UPDATE  TestTable
                SET     Column1 = source.Column1,
                        Column2 = source.Column2
        WHEN NOT MATCHED THEN
                INSERT
                INTO    TestTable (Column1, Column2)
                VALUES  (source.Column1, source.Column2)
        OUTPUT  INSERTED.guid

还要确保您有一个索引TestTable (guid)或它被声明为PRIMARY KEY.

于 2010-09-08T21:27:57.960 回答