3

Web アプリケーションで nHibnerate を使用していますが、生成された sp_execute でインデックスを使用すると問題が発生します。私のテーブルには 2 億 1000 万のレコードがあり、クエリが非常に遅いです。

まず、生成された列「kolumna1」タイプに問題がありました。データベースにはvarcharの列がありますが、nHibernateはnvarcharを生成しました。varchar の使用を強制する特別な属性をコードに入れることで、これを回避しました。そのトリックの後、sp_executed はインデックスの使用を開始し、すべてが正しかった。sp_executesql が完了するまでに 10 分かかります。通常のクエリ(sp_executesqlなし)をチェックしたところ、1秒しかかかりませんでした。sp_executesql はインデックスを使用しておらず、通常のクエリはインデックスを使用していました。インデックスを変更せずに、varchar を nvarchar に戻し、sp_execute は 1 秒で終了しました (インデックスを使用)。誰が私がどこで間違いを犯したのか分かりましたか? このような小さな変更に対して実行計画が異なるのはなぜですか? そして、それを修正する方法は?

ここで、さらにコードを添付しました。誰かがそれを必要とする場合に備えて。

sp_executesql と varchar(8000)

exec  sp_executesql N'SELECT count(*) as y0_  FROM tabela1 this_ WHERE ((this_.kolumna2 >= @p0 and this_.kolumna2 <= @p1)) and  
    (this_.kolumna3 in (@p2, @p3) and this_.kolumna1 like @p4)',N'@p0 datetime,@p1 datetime,@p2 int,@p3 int,@p4 varchar(8000)',
    @p0='2013-01-08 14:38:00' ,@p1='2013-02-08 14:38:00',@p2=341,@p3=342,@p4='%501096109%'

varchar を使用した sp_executesql

nvarchar(4000) を使用した sp_executesql

exec  sp_executesql N'SELECT count(*) as y0_  FROM tabela1 this_ WHERE ((this_.kolumna2 >= @p0 and this_.kolumna2 <= @p1)) and  
    (this_.kolumna3 in (@p2, @p3) and this_.kolumna1 like @p4)',N'@p0 datetime,@p1 datetime,@p2 int,@p3 int,@p4 nvarchar(4000)',
    @p0='2013-01-08 14:38:00' ,@p1='2013-02-08 14:38:00',@p2=341,@p3=342,@p4='%501096109%'

nvarchar を使用した sp_executesql

面白いのは、SQL プロファイラーでは、両方のクエリが同じ結果を返すことです。

exec sp_executesql N'SELECT count(*) as y0_ FROM tabela1 this_  
WHERE this_.kolumna3 in (@p2, @p3) and ((this_.kolumna2 >= @p0 and this_.kolumna2 <= @p1))  
and ( this_.kolumna1 like @p4)',N'@p0 datetime,@p1 datetime,@p2 int,@p3 int,@p4 varchar(8000)',  
@p0='2013-01-08 14:38:00' ,@p1='2013-02-08 14:38:00',@p2=341,@p3=342,@p4='%501096109%'  
--Declare @p0 datetime  
--set @p0 = '2013-01-08 14:38:00'  
--Declare @p1 datetime  
--set @p1 = '2013-02-08 14:38:00'  
--Declare @p2 int  
--set @p2 = 341  
--Declare @p3 int  
--set @p3 = 342  
--Declare @p4 varchar(8000)  
--set @p4 = '%501096109%'  
--SELECT count(*) as y0_  
 --FROM tabela1 this_  
 --WHERE ((this_.kolumna2 >= @p0 and  
 --this_.kolumna2 <= @p1)) and  
 --(this_.kolumna3 in (@p2, @p3) and this_.kolumna1 like @p4)

インデックスは次のとおりです。

CREATE TABLE [dbo].[tabela1](
[id] [bigint] NOT NULL,
[kolumna1] [varchar](128) NOT NULL,
[kolumna2] [datetime] NOT NULL,
[kolumna3] [int] NOT NULL,
CONSTRAINT [PK__tabela1__4F7CD00D] 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]

CREATE NONCLUSTERED INDEX [ind_tabela1_ kolumna2] ON [dbo].[tabela1] 
(
    [kolumna2] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [ind_ tabela1_ kolumna3] ON [dbo].[ tabela1] 
(
    [kolumna3] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [IX_ tabela1_ kolumna1] ON [dbo].[ tabela1] 
(
    [kolumna1] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_ tabela1_ kolumna2_ kolumna3] ON [dbo].[ tabela1] 
(
    [kolumna2] ASC,
    [kolumna3] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_ tabela1_ kolumna3_ kolumna2_id_ kolumna1] ON [dbo].[ tabela1] 
(
    [kolumna3] ASC,
    [kolumna2] ASC,
    [id] ASC,
    [kolumna1] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

クエリの実行計画の下: select count(*) from [dbo].[tabela1] where [kolumna1] like N'%501096109%' クエリの実行計画

4

2 に答える 2

2

SQL Serverクエリオプティマイザーは、次の場合にインデックスシークを使用することを選択できます。

  1. LIKE以外にも別のフィルター述語があります。正確な検索または少なくともSARGable述語である必要があります
  2. テーブルが非常に大きい(数百万行)

ただし、明示的な型変換(異なる照合/データ型)が使用されている場合、シーク操作は実行できません。この動作とクエリプランを制御できないもう1つのことは、述語セットごとに異なる可能性があります。これを行うには、ヒントFORCESEEK(バージョン2008+)を使用する必要があります。ここで情報を見つけることができます:http: //msdn.microsoft.com/en-us/library/ms187373%28v=sql.100%29.aspx

于 2013-02-27T07:04:19.160 に答える
2

これを試していただけませんか:

(1)次のSQLを実行します。

select * from sys.dm_exec_cached_plans
cross apply sys.dm_exec_sql_text(plan_handle) t

(2)最後の列を使用して、最初のクエリのSQLを検索します。sp_executesqlは含まれませんが、パラメーターのリストから始まり、最後のリストはvarcharです。を取得しplan_handle、次のステートメントで使用します。

dbcc freeproccache (<your_plan_handle>)

次に、クエリ1を再試行します。

于 2013-02-25T11:31:38.877 に答える