Помогите оптимизировать запрос.
От: ppvrt  
Дата: 16.07.09 09:19
Оценка:
Есть база данных MS SQL 2008.
В таблицах содержится много записей (в некоторых таблицах более 5 млн записей).

Имеется запрос вида:

if exists (select top 1 Column1 from Table1 where Column2 = @value)
begin
...
end
else
begin
...
end


Суть запроса — найти хоть одну строку, в которой значение в ячейке имеет искомое значение.
Так вот, Top 1 сначала находит все строки, удовлетворяющие условию поиска, а потом усекает результаты запроса и выводит клиенту первую строку из результата запроса.
Мне же нужно сделать так, что если нашлась первая строка, удовлетворяющая условию поиска, то дальнейший поиск прекратить. Какой оператор для этого нужно использовать, применительно к MS SQL 2008.
Данный вопрос очень актуален по причине того, что таких запросов на проверку exists (select top 1 ...) в моих хранимых процедурах достаточно много. Кроме того, в процессе выполнения запроса, находится много (порядка 20-30 тысяч) записей, удовлетворяющих условиям поиска.
Re: Помогите оптимизировать запрос.
От: ilya.buchkin США http://engineering.meta-comm.com/
Дата: 16.07.09 09:27
Оценка:
Здравствуйте, ppvrt, Вы писали:

P>Имеется запрос вида:

P>
P>if exists (select top 1 Column1 from Table1 where Column2 = @value)
P>...

P>Так вот, Top 1 сначала находит все строки, удовлетворяющие условию поиска, а потом усекает результаты запроса и выводит клиенту первую строку из результата запроса.
P>Мне же нужно сделать так, что если нашлась первая строка, удовлетворяющая условию поиска, то дальнейший поиск прекратить. Какой оператор для этого нужно использовать, применительно к MS SQL 2008.

если есть индекс по "Column2" (или хотя-бы такой где "Column2" на первом месте), то должно работать как ты просишь — должен использоваться INDEX SEEK, и стоп на первой же записи.
--
Ilya Buchkin
MetaCommunications Engineering, Iowa City — Санкт-Петербург
Re: Помогите оптимизировать запрос.
От: Ellin Россия www.rsdn.ru
Дата: 16.07.09 09:28
Оценка:
Здравствуйте, ppvrt, Вы писали:

P>Есть база данных MS SQL 2008.

P>В таблицах содержится много записей (в некоторых таблицах более 5 млн записей).

P>Имеется запрос вида:


P>
P>if exists (select top 1 Column1 from Table1 where Column2 = @value)
P>begin
P>...
P>end
P>else
P>begin
P>...
P>end
P>


P>Суть запроса — найти хоть одну строку, в которой значение в ячейке имеет искомое значение.

P>Так вот, Top 1 сначала находит все строки, удовлетворяющие условию поиска, а потом усекает результаты запроса и выводит клиенту первую строку из результата запроса.
P>Мне же нужно сделать так, что если нашлась первая строка, удовлетворяющая условию поиска, то дальнейший поиск прекратить. Какой оператор для этого нужно использовать, применительно к MS SQL 2008.
P>Данный вопрос очень актуален по причине того, что таких запросов на проверку exists (select top 1 ...) в моих хранимых процедурах достаточно много. Кроме того, в процессе выполнения запроса, находится много (порядка 20-30 тысяч) записей, удовлетворяющих условиям поиска.
Я, конечно, не такой уж большой эксперт (кои встречаются здесь на форуме), однакож ИМХО достаточно сделать
if exists (select Column1 from Table1 where Column2 = @value)

Т.е. убрать top... то что ты спрашиваешь именно так поступает exists. При первом совпадении прекращает работу.
Re[2]: Помогите оптимизировать запрос.
От: ppvrt  
Дата: 16.07.09 10:08
Оценка:
Здравствуйте, Ellin, Вы писали:

E>Здравствуйте, ppvrt, Вы писали:


P>>Есть база данных MS SQL 2008.

P>>В таблицах содержится много записей (в некоторых таблицах более 5 млн записей).

P>>Имеется запрос вида:


P>>
P>>if exists (select top 1 Column1 from Table1 where Column2 = @value)
P>>begin
P>>...
P>>end
P>>else
P>>begin
P>>...
P>>end
P>>


P>>Суть запроса — найти хоть одну строку, в которой значение в ячейке имеет искомое значение.

P>>Так вот, Top 1 сначала находит все строки, удовлетворяющие условию поиска, а потом усекает результаты запроса и выводит клиенту первую строку из результата запроса.
P>>Мне же нужно сделать так, что если нашлась первая строка, удовлетворяющая условию поиска, то дальнейший поиск прекратить. Какой оператор для этого нужно использовать, применительно к MS SQL 2008.
P>>Данный вопрос очень актуален по причине того, что таких запросов на проверку exists (select top 1 ...) в моих хранимых процедурах достаточно много. Кроме того, в процессе выполнения запроса, находится много (порядка 20-30 тысяч) записей, удовлетворяющих условиям поиска.
E>Я, конечно, не такой уж большой эксперт (кои встречаются здесь на форуме), однакож ИМХО достаточно сделать
E>
E>if exists (select Column1 from Table1 where Column2 = @value)
E>

E>Т.е. убрать top... то что ты спрашиваешь именно так поступает exists. При первом совпадении прекращает работу.

Убрал top 1.
Как же так? А почему тогда на плане запроса не меняется значение 'Estimated I/O cost' или 'Estimated CPU cost'?
Что-то же должно изменяться! Время выполнения запроса не уменьшилось.
Re[3]: Помогите оптимизировать запрос.
От: Ellin Россия www.rsdn.ru
Дата: 16.07.09 10:11
Оценка:
Здравствуйте, ppvrt, Вы писали:

P>Убрал top 1.

P>Как же так? А почему тогда на плане запроса не меняется значение 'Estimated I/O cost' или 'Estimated CPU cost'?
P>Что-то же должно изменяться! Время выполнения запроса не уменьшилось.

Оптимизатор скорее всего и так все сам убирает.
Re[2]: Помогите оптимизировать запрос.
От: ppvrt  
Дата: 16.07.09 10:26
Оценка:
Здравствуйте, ilya.buchkin, Вы писали:

P>>Имеется запрос вида:

P>>
P>>if exists (select top 1 Column1 from Table1 where Column2 = @value)
P>>...

P>>Так вот, Top 1 сначала находит все строки, удовлетворяющие условию поиска, а потом усекает результаты запроса и выводит клиенту первую строку из результата запроса.
P>>Мне же нужно сделать так, что если нашлась первая строка, удовлетворяющая условию поиска, то дальнейший поиск прекратить. Какой оператор для этого нужно использовать, применительно к MS SQL 2008.

IB>если есть индекс по "Column2" (или хотя-бы такой где "Column2" на первом месте), то должно работать как ты просишь — должен использоваться INDEX SEEK, и стоп на первой же записи.


На "Column2" есть некластерный индекс. Но "стоп" на первой записи не происходит. Т.е. на плане выполнения запроса я вижу большое число у параметра 'Estimated subtree cost'.
Re[4]: Помогите оптимизировать запрос.
От: ppvrt  
Дата: 16.07.09 10:30
Оценка:
Здравствуйте, Ellin, Вы писали:

P>>Убрал top 1.

P>>Как же так? А почему тогда на плане запроса не меняется значение 'Estimated I/O cost' или 'Estimated CPU cost'?
P>>Что-то же должно изменяться! Время выполнения запроса не уменьшилось.

E>Оптимизатор скорее всего и так все сам убирает.


А Вам приходилось оптимизировать работу AWE? В частности, управлять буферами на основе технологии NUMA?
Re[5]: Помогите оптимизировать запрос.
От: Ellin Россия www.rsdn.ru
Дата: 16.07.09 10:47
Оценка: 3 (1)
Здравствуйте, ppvrt, Вы писали:

P>А Вам приходилось оптимизировать работу AWE? В частности, управлять буферами на основе технологии NUMA?

Нет, не приходилось. Зато вот:

I'm running SQL Server 2000 and need to see whether a particular value exists in a table. Which is more efficient, using an EXISTS check or a SELECT COUNT(*)?

A Using the T-SQL EXISTS keyword to perform an existence check is almost always faster than using COUNT(*). EXISTS can stop as soon as the logical test proves true, but COUNT(*) must count every row, even after it knows one row has passed the test.


И, как выясняется, сей вопрос актуален лишь для SQL Server 2000, но не для SQL Server 2005 — который count caм заменяет на exists если видет его в if.
Re: Помогите оптимизировать запрос.
От: MasterZiv СССР  
Дата: 16.07.09 11:35
Оценка:
ppvrt пишет:

> Суть запроса — найти хоть одну строку, в которой значение в ячейке имеет

> искомое значение.
> Так вот, Top 1 сначала находит все строки, удовлетворяющие условию
> поиска, а потом усекает результаты запроса и выводит клиенту первую
> строку из результата запроса.

Так убери TOP 1 нахрен, зачем он там тебе ? Он же в EXISTS стоит.
Сервер и так знает, что тебе нужно знать только факт наличия хотя бы
одной записи.
Posted via RSDN NNTP Server 2.1 beta
Re[3]: Помогите оптимизировать запрос.
От: _d_m_  
Дата: 16.07.09 19:01
Оценка:
Здравствуйте, ppvrt, Вы писали:

Попробуй так:
if exists (select 1 from Table1 where Column2 = @value)


P>Убрал top 1.

P>Как же так? А почему тогда на плане запроса не меняется значение 'Estimated I/O cost' или 'Estimated CPU cost'?
P>Что-то же должно изменяться! Время выполнения запроса не уменьшилось.

Включи set showplan_text on, план скинь сюда, не забыв его поместить между тегами [code].
Re[4]: Помогите оптимизировать запрос.
От: ppvrt  
Дата: 17.07.09 06:16
Оценка:
Здравствуйте, _d_m_, Вы писали:

___>Попробуй так:

___>
___>if exists (select 1 from Table1 where Column2 = @value)
___>


P>>Убрал top 1.

P>>Как же так? А почему тогда на плане запроса не меняется значение 'Estimated I/O cost' или 'Estimated CPU cost'?
P>>Что-то же должно изменяться! Время выполнения запроса не уменьшилось.

___>Включи set showplan_text on, план скинь сюда, не забыв его поместить между тегами.



Скрипт для создания тестовой таблицы.
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[tblExample](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ParentId] [int] NULL,
 CONSTRAINT [PK_tblExample] 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].[tblExample]  WITH CHECK ADD  CONSTRAINT [FK_tblExample_tblExample1] FOREIGN KEY([ParentId])
REFERENCES [dbo].[tblExample] ([Id])
GO

ALTER TABLE [dbo].[tblExample] CHECK CONSTRAINT [FK_tblExample_tblExample1]
GO


Запускаем запрос вида

if exists (select 1 from tblExample
           where ParentId is null)
           print 'Ok'
else
    print 'error'


И получаем следующий план выполнения:

  |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END))
       |--Nested Loops(Left Semi Join, DEFINE:([Expr1005] = [PROBE VALUE]))
            |--Constant Scan
            |--Index Seek(OBJECT:([saad_test2].[dbo].[tblExample].[IX_tblExample]), SEEK:([saad_test2].[dbo].[tblExample].[ParentId]=NULL) ORDERED FORWARD)


Как видно из плана выполнения, присутствует попытка поиска строк путём запуска вложенных циклов (nested loops). А это — очень и очень хреново. Как же так?
Re[5]: Помогите оптимизировать запрос.
От: MasterZiv СССР  
Дата: 17.07.09 08:30
Оценка:
ppvrt пишет:

> Запускаем запрос вида

>
> if exists (select 1 from tblExample
> where ParentId is null)
> print 'Ok'
> else
> print 'error'
>
>
>
> И получаем следующий план выполнения:
>
> |--Compute Scalar(DEFINE[Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END))
> |--Nested Loops(Left Semi Join, DEFINE[Expr1005] = [PROBE VALUE]))
> |--Constant Scan
> |--Index Seek(OBJECT[saad_test2].[dbo].[tblExample].[IX_tblExample]), SEEK[saad_test2].[dbo].[tblExample].[ParentId]=NULL) ORDERED FORWARD)
>
>
>
> Как видно из плана выполнения, присутствует попытка поиска строк путём
> запуска вложенных циклов (nested loops). А это — очень и очень хреново.

Кто тебе это сказал ?

Ничего хренового нет, это раз.

во-вторых, эта записть означает, что применяется алгоритм Nested Loops для
нижеследующих таблиц, и таблица эта -- одна -- tblExample. Так что фактически
никакого Nested Loops-а нет. И план хороший, по константе по индексу
ищет одну запись.
Posted via RSDN NNTP Server 2.1 beta
Re[5]: Помогите оптимизировать запрос.
От: Sshur Россия http://shurygin-sergey.livejournal.com
Дата: 17.07.09 08:40
Оценка:
P>И получаем следующий план выполнения:

P>
P>  |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END))
P>       |--Nested Loops(Left Semi Join, DEFINE:([Expr1005] = [PROBE VALUE]))
P>            |--Constant Scan
P>            |--Index Seek(OBJECT:([saad_test2].[dbo].[tblExample].[IX_tblExample]), SEEK:([saad_test2].[dbo].[tblExample].[ParentId]=NULL) ORDERED FORWARD)
P>


P>Как видно из плана выполнения, присутствует попытка поиска строк путём запуска вложенных циклов (nested loops). А это — очень и очень хреново. Как же так?



Разбираем план. Index seek — нашли записи с [ParentId]=NULL. Constant Scan — подставили 1 в выборку того, что вернул index seek. nested loops — проверили наличие записей вообще.

В использовании Nested Loops ничего криминального не вижу. Интересно было бы посмотреть на большой таблице, сколько строк возвращает index seek — это видно при включенном отображении плане запроса в студии(и не предполагаемого, а реального).
Шурыгин Сергей

"Не следует преумножать сущности сверх необходимости" (с) Оккам
Re[6]: Помогите оптимизировать запрос.
От: ppvrt  
Дата: 17.07.09 10:47
Оценка:
Здравствуйте, Sshur, Вы писали:

P>>И получаем следующий план выполнения:


P>>
P>>  |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END))
P>>       |--Nested Loops(Left Semi Join, DEFINE:([Expr1005] = [PROBE VALUE]))
P>>            |--Constant Scan
P>>            |--Index Seek(OBJECT:([saad_test2].[dbo].[tblExample].[IX_tblExample]), SEEK:([saad_test2].[dbo].[tblExample].[ParentId]=NULL) ORDERED FORWARD)
P>>


P>>Как видно из плана выполнения, присутствует попытка поиска строк путём запуска вложенных циклов (nested loops). А это — очень и очень хреново. Как же так?



S>Разбираем план. Index seek — нашли записи с [ParentId]=NULL. Constant Scan — подставили 1 в выборку того, что вернул index seek. nested loops — проверили наличие записей вообще.


S>В использовании Nested Loops ничего криминального не вижу. Интересно было бы посмотреть на большой таблице, сколько строк возвращает index seek — это видно при включенном отображении плане запроса в студии(и не предполагаемого, а реального).



StmtText    StmtId    NodeId    Parent    PhysicalOp    LogicalOp    Argument    DefinedValues    EstimateRows    EstimateIO    EstimateCPU    AvgRowSize    TotalSubtreeCost    OutputList    Warnings    Type    Parallel    EstimateExecutions
if exists (select 1 from tblExample where ParentId is not null)    1    1    0    NULL    NULL    1    NULL    1    NULL    NULL    NULL    0,003288537    NULL    NULL    COND WITH QUERY    0    NULL
  |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END))    1    2    1    Compute Scalar    Compute Scalar    DEFINE:([Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END)    [Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END    1    0    1E-07    11    0,003288537    [Expr1004]    NULL    PLAN_ROW    0    1
       |--Nested Loops(Left Semi Join, DEFINE:([Expr1005] = [PROBE VALUE]))    1    3    2    Nested Loops    Left Semi Join    DEFINE:([Expr1005] = [PROBE VALUE])    [Expr1005] = [PROBE VALUE]    1    0    4,18E-06    9    0,003288437    [Expr1005]    NULL    PLAN_ROW    0    1
            |--Constant Scan    1    4    3    Constant Scan    Constant Scan    NULL    NULL    1    0    1,157E-06    9    1,157E-06    NULL    NULL    PLAN_ROW    0    1
            |--Index Seek(OBJECT:([saad_test3].[dbo].[tblExample].[IX_tblExample]), SEEK:([saad_test3].[dbo].[tblExample].[ParentId] IsNotNull) ORDERED FORWARD)    1    5    3    Index Seek    Index Seek    OBJECT:([saad_test3].[dbo].[tblExample].[IX_tblExample]), SEEK:([saad_test3].[dbo].[tblExample].[ParentId] IsNotNull) ORDERED FORWARD    NULL    1    0,2557176    0,2173036    9    0,0032831    NULL    NULL    PLAN_ROW    0    1
   print 'ok'    2    6    1    NULL    NULL    2    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    PRINT    0    NULL
   else  print 'error    3    7    1    NULL    NULL    5    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    PRINT    0    NULL
'    0    8    0    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    TEXT    0    NULL
Re[7]: Помогите оптимизировать запрос.
От: Sshur Россия http://shurygin-sergey.livejournal.com
Дата: 17.07.09 13:16
Оценка:
Здравствуйте, ppvrt, Вы писали:

P>
P>StmtText    StmtId    NodeId    Parent    PhysicalOp    LogicalOp    Argument    DefinedValues    EstimateRows    EstimateIO    EstimateCPU    AvgRowSize    TotalSubtreeCost    OutputList    Warnings    Type    Parallel    EstimateExecutions
P>if exists (select 1 from tblExample where ParentId is not null)    1    1    0    NULL    NULL    1    NULL    1    NULL    NULL    NULL    0,003288537    NULL    NULL    COND WITH QUERY    0    NULL
P>  |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END))    1    2    1    Compute Scalar    Compute Scalar    DEFINE:([Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END)    [Expr1004]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END    1    0    1E-07    11    0,003288537    [Expr1004]    NULL    PLAN_ROW    0    1
P>       |--Nested Loops(Left Semi Join, DEFINE:([Expr1005] = [PROBE VALUE]))    1    3    2    Nested Loops    Left Semi Join    DEFINE:([Expr1005] = [PROBE VALUE])    [Expr1005] = [PROBE VALUE]    1    0    4,18E-06    9    0,003288437    [Expr1005]    NULL    PLAN_ROW    0    1
P>            |--Constant Scan    1    4    3    Constant Scan    Constant Scan    NULL    NULL    1    0    1,157E-06    9    1,157E-06    NULL    NULL    PLAN_ROW    0    1
P>            |--Index Seek(OBJECT:([saad_test3].[dbo].[tblExample].[IX_tblExample]), SEEK:([saad_test3].[dbo].[tblExample].[ParentId] IsNotNull) ORDERED FORWARD)    1    5    3    Index Seek    Index Seek    OBJECT:([saad_test3].[dbo].[tblExample].[IX_tblExample]), SEEK:([saad_test3].[dbo].[tblExample].[ParentId] IsNotNull) ORDERED FORWARD    NULL    1    0,2557176    0,2173036    9    0,0032831    NULL    NULL    PLAN_ROW    0    1
P>   print 'ok'    2    6    1    NULL    NULL    2    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    PRINT    0    NULL
P>   else  print 'error    3    7    1    NULL    NULL    5    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    PRINT    0    NULL
P>'    0    8    0    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    TEXT    0    NULL
P>



Как-то я в итакой солянке плохо разбираюсь Возьмите а) реальную таблицу с большим количеством данных и б) реальный графический план запроса в SSMS (или EM). Тогда, если вы наведете мышь на стрелочку, соединяющую index seek c nested loops, то там будет написано число 'actual row count'. Вот если там 1 — то все нормально. Я проверял, у меня был 1. А если просто по индексу данные доставать, то и запрос выполнялся дольше и значение было соответствующее
Шурыгин Сергей

"Не следует преумножать сущности сверх необходимости" (с) Оккам
Re[8]: Помогите оптимизировать запрос.
От: _d_m_  
Дата: 22.07.09 02:12
Оценка:
Здравствуйте, Sshur, Вы писали:

S>Как-то я в итакой солянке плохо разбираюсь



Да ты избалован MS SQL-ем
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.