2013-02-20 5 views
2

Я использую nHibnerate в своем веб-приложении, и у меня есть проблема с использованием индексов в сгенерированном sp_execute. Моя таблица имеет 210 миллионов записей, и запрос выполняется очень медленно.sp_executesql не использует индекс?

Во-первых, возникла проблема с созданным типом столбца «kolumna1». В базе данных у меня есть столбец varchar, но nHibernate сгенерирован nvarchar. Я обманул это, добавив специальный атрибут в код, который принудительно использовал varchar. После этого трюк sp_executed начал использовать индексы, и все было правильно. Теперь проблема в том, что sp_executesql занимает 10 минут. Когда я проверил обычный запрос (без sp_executesql), ему потребовалось всего 1 с. Я проверил планы выполнения для обоих: sp_executesql не использовал индекс, а обычный запрос использовал индекс. Без изменения индекса я изменил обратный varchar на nvarchar и sp_execute закончил в 1s (используемый индекс). Кто-нибудь понял, где я совершил ошибку? почему план исполнения отличается для таких небольших изменений? И как это исправить?

Здесь я прилагаю больше кода. На всякий случай, если кому-то это понадобится.

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%' 

sp_executesql with varchar

sp_executesql с NVARCHAR (4000)

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%' 

sp_executesql with nvarchar

Самое смешное, что в SQL Profiler как запрос дает такой же reuslt:

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 (*) из [DBO]. [tabela1], где [kolumna1] как N '% 501096109%' execution plan for query

+1

Это может помочь: http://is.gd/RR0fzH –

+1

Этот запрос не сможет использовать поиск индекса для kolumna1. Причина в том, что у вас есть ведущий% в вашем шаблоне. Можете ли вы предоставить запрос non-count, который использует один из ваших индексов? – muhmud

+0

Jamie Ide: Мы уже пробовали этот подход, и у нас есть как varchar в nhibernate, так и databse. –

ответ

2

Sql Server оптимизатор запросов может выбрать для использования индекса искать, когда:

  1. Есть еще один фильтр предикаты, кроме LIKE. Он должен быть точным поиск или по крайней мере SARGable предикат
  2. Таблица очень велика (миллионы строк)

Но операция поиска не может быть сделано, если используется явное преобразование типа - разные параметры сортировки/тип данных. Другое, что вы не можете контролировать это поведение и планы запросов, может быть различным для разных наборов предикатов. Для этого вам нужно использовать подсказку FORCESEEK (версия 2008+). Вы можете найти информацию здесь: http://msdn.microsoft.com/en-us/library/ms187373%28v=sql.100%29.aspx

+0

Спасибо за ответ. Хорошо, но как заставить NHibernate использовать подсказки? Наши условия запроса имеют одинаковые параметры сопоставления/dataType, такие как включенные параметры. Для тестов я помещаю запрос в sp_executeSQL Option (Recompile) и работает. –

+1

Пожалуйста, смотрите [здесь] (http://stackoverflow.com/questions/10085450/jpa-entity-use-index-hint) – Dalex

2

Не могли бы вы попробовать это:

(1) Запустите FO llowing 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.

+0

Мы уже пробовали прозрачный план в кэше. И это не помогло. –

Смежные вопросы