C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
возможно ли средствами MSSQL просчитать заранее, сколько займет выполнение запроса
Tertium
Отправлено: 12.08.2005, 03:25


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



Есть запрос, он при различных условиях выполняется различное количество времени, может секунду, а может и час. Юзер априори туп. Поэтому ему надо показывать количество ожидания, на которое он себя обрекает. В средствах MSSQL есть чтонть типа пронозирования колва времени, затрачиваемого на выполнение запроса.
А то руками считать достаточно сложно. Запрос — фактически вызов ХП. В ней достаточно весомые операции, напр. связывание. Повторяемое в цикле. Медленная вещь, но быстрее сделать не могу пока.
Если интересно, вот она:
CODE
alter proc get_ci (@begin datetime, @end datetime, @minute_period int)
with ENCRYPTION
as
if (@end<@begin)  
begin  
   declare @tmp datetime  
   set @tmp = @end  
   set @end = @begin  
   set @begin = @tmp  
end  
DECLARE @first_time datetime  
DECLARE @last_time datetime  
select top 1 @first_time=last_query from stats where club_id in (1,2,3) order by last_query asc
select top 1 @last_time =last_query from stats where club_id in (1,2,3) order by last_query desc
if @begin<@first_time
set @begin=@first_time
if @end>@last_time
set @end=@last_time
DECLARE @last_datime datetime  
DECLARE @last_IN bigint  
DECLARE @last_OUT bigint  
DECLARE @prev_last_IN bigint  
DECLARE @prev_last_OUT bigint  
DECLARE @actual_record_cnt int  
SET @last_IN = 0  
SET @last_OUT = 0  
SET @prev_last_IN = 0  
SET @prev_last_OUT = 0
SET @actual_record_cnt = 0
--не меньше часа, не больше високосного года
if @minute_period<1
SET @minute_period=1
if @minute_period>527040
SET @minute_period=527040
create table #ret_table
(  
   currentIN     bigint,  
   currentOUT    bigint,  
   last_query    datetime,
   PRIMARY KEY  CLUSTERED
   (                      
    [last_query]      
   )                      
)        
SET @last_datime=@begin
while @last_datime<@end  
begin  
SET @last_datime=DATEADD(minute,@minute_period,@last_datime)
if @last_datime>@end
 set @last_datime=@end
---самый мат
--********************************************************************
Select @last_IN=sum(i1.currentIn), @last_OUT=sum(i1.currentOut) from  
  (select * from stats where club_id in (1,2,3)) as i1,
  (Select club_id,place_no, max(last_query) as max_last_query  
   from stats where club_id in (1,2,3) and last_query<=@last_datime  
   Group by club_id,place_no) as t1
  where i1.place_no=t1.place_no and i1.club_id=t1.club_id and i1.last_query=t1.max_last_query
--********************************************************************
if @last_IN IS NOT NULL and @last_OUT IS NOT NULL
begin --если не те же данные, что в прошлый раз  
 if @prev_last_IN!=@last_IN or @prev_last_OUT!=@last_OUT or @last_datime=@end or @last_datime=@begin
 begin
     insert into #ret_table(currentIN,currentOUT,last_query) values(@last_IN,@last_OUT,@last_datime)  
  set @actual_record_cnt = @actual_record_cnt + 1
  SET @prev_last_IN  = @last_IN
  SET @prev_last_OUT = @last_OUT
 end
end  
else --если нет записей, то доход/расход нулевой
begin --если не те же нули, что в прошлый раз  
 if @prev_last_IN!=0 or @prev_last_OUT!=0 or @last_datime=@end or @last_datime=@begin
 begin
     insert into #ret_table(currentIN,currentOUT,last_query) values(0,0,@last_datime)  
  set @actual_record_cnt = @actual_record_cnt + 1
  SET @prev_last_IN  = 0
  SET @prev_last_OUT = 0
 end
end  
end  
if @actual_record_cnt=0
begin
insert into #ret_table(currentIN,currentOUT,last_query) values(0,0,@begin)
insert into #ret_table(currentIN,currentOUT,last_query) values(0,0,@end)
end
--первая строка — число записей
declare @records_number int
select @records_number=count(*) from  #ret_table
insert into #ret_table(currentIN,currentOUT,last_query) values(@records_number,@records_number,DATEADD(Year,-1,@begin))
select * from #ret_table order by last_query
drop table #ret_table


Отредактировано Tertium — 12/08/2005, 03:35
olegenty
Отправлено: 12.08.2005, 06:58


Ветеран

Группа: Модератор
Сообщений: 2412



нет такой возможности. но пройдись по кускам планировщиком. написанный код почти всегда можно улучшить. понаблюдай за блокировками в БД. если дофига — перед началом выполнения основных действий скинь исходные данные во временные таблицы — станешь независим от блокировок реальных таблиц. на маленьких выборках, где в where и join используется только ключ, можешь попробовать табличные переменные.
Tertium
Отправлено: 12.08.2005, 12:45


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



тут ситуация не совсем в оптимизации текущего кода. Планировщиком проходил, говорит что большая частьвремени уходит на связывание
[CODE]Select @last_IN=sum(i1.currentIn), @last_OUT=sum(i1.currentOut) from
(select * from stats where club_id in (1,2,3)) as i1,
(Select club_id,place_no, max(last_query) as max_last_query
from stats where club_id in (1,2,3) and last_query<=@last_datime
Group by club_id,place_no) as t1
where i1.place_no=t1.place_no and i1.club_id=t1.club_id and i1.last_query=t1.max_last_query [CODE]
и я ему верю. Блокировок нет пока вообще. Вообще интрисует сама методика просчёта времени выполнения нелинейного детерминистичного запроса. Может есть какие-то усреднённые методы, типа пргонки на тестовых картежах, построения графиков а потом апроксимация их функциями?...
olegenty
Отправлено: 12.08.2005, 13:09


Ветеран

Группа: Модератор
Сообщений: 2412



не-а, нету. прикинь, на блокировку попадёшь (а рано или поздно при работе с MSSQL — попадёшь), и все твои расчёты — псу в гузно.

так, а план-то какой. Table Scan, index Scan, Index Seek? должны быть только Index Seek... а из Join — Nested Loop — это хуже, Bookark Lookup — получше, Merge — ещё получше.... из чего у тебя текущий план этого места состоит? может индекс-другой накинешь, и резко наступит счастье?
Tertium
Отправлено: 12.08.2005, 15:26


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



Таблица
CODE
IF NOT EXISTS (select * from sysobjects o where o.name='Stats')
CREATE TABLE [Stats] (
[id] [int]   IDENTITY (1, 1) NOT NULL ,
[place_no]   [tinyint] NOT NULL DEFAULT (0),
[club_id]   [int] NOT NULL DEFAULT (0),
[currentIn]  [bigint] NOT NULL DEFAULT (0),
[currentOut] [bigint] NOT NULL DEFAULT (0),
[last_query] [datetime] NOT NULL DEFAULT  GETDATE(),
PRIMARY KEY  CLUSTERED  
(
 [id] ASC
)
)
IF NOT EXISTS (select * from sysindexes o where o.name='my_unique')
create unique index my_unique on stats
(  
[place_no] ASC,  
[club_id] ASC,  
[last_query] ASC )  
with IGNORE_DUP_KEY  

а план выполнения запроса:
CODE

Select sum(i1.currentIn), sum(i1.currentOut) from  
(select * from stats where club_id in (1,2,3)) as i1,
(Select club_id,place_no, max(last_query) as max_last_query  
 from stats where club_id in (1,2,3) and last_query<='2006-08-11 19:12:26.000'  
 Group by club_id,place_no) as t1
where i1.place_no=t1.place_no and i1.club_id=t1.club_id and i1.last_query=t1.max_last_query

такой:

Clustered Index (который primary) Scan------------------¬
stats.my_unique(IndexScan)-- StreamAgregate -- HashMatch|InnerJoin (100%) -- StreamAgregate -- Compute Scalar -- Select

Короче всё кроме хашмача по нулям. Добавлял индекс некластеровый неуникальный по club_id,place_no — та же песня sad.gif
olegenty
Отправлено: 12.08.2005, 15:52


Ветеран

Группа: Модератор
Сообщений: 2412



вот отсюда и тормоза: clustered index scan монопенисуально table scan, потом создам себе такую табличку, посмотрю, что можно сделать
Tertium
Отправлено: 12.08.2005, 16:10


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



>>clustered index scan монопенисуально table scan
да, ты знаешь, как сказать... убил ваще нахрен примари индекс — вот он заменил clustered index scan table scan'ом. Время запроса возросло более чем втрое
olegenty
Отправлено: 13.08.2005, 11:54


Ветеран

Группа: Модератор
Сообщений: 2412



я это к тому, что Scan — всегда плохо. а Seek — рулит.
Tertium
Отправлено: 13.08.2005, 12:18


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



судя по смыслу, понятно. вот тока как сделать, чтобы был сик, а не скан?...
olegenty
Отправлено: 13.08.2005, 12:28


Ветеран

Группа: Модератор
Сообщений: 2412



у меня нет рецепта. я в таких случаях долго экспериментирую с индексами, чтобы при минимуме индексов получить максимум приятных планов.
Tertium
Отправлено: 13.08.2005, 12:33


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



а концептуально, что нужно, чтоб был сик?
olegenty
Отправлено: 13.08.2005, 13:36


Ветеран

Группа: Модератор
Сообщений: 2412



наличие индекса, поля которого последовательно используются при выполнении запроса. при этом надо учитывать, что один индекс может охватывать как JOIN... ON условия, так и WHERE условия.
Tertium
Отправлено: 13.08.2005, 14:18


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



ммм... для меня это пока тёмный лес
olegenty
Отправлено: 15.08.2005, 15:09


Ветеран

Группа: Модератор
Сообщений: 2412



а ты экспериментируй, тогда, с течением времени, хорошо представляя собственноручно спроектированные таблицы, легко будешь это делать. сложнее всего, это когда БД разрабатывал не сам, но когда "въедешь" в логику создателя БД, достаточно будет навешивать индексы. минус: если БД спроектирована плохо, быстро попадёшь на сиуацию перегрузки таблиц индексами.

Вернуться в Работа с базами данных в C++Builder