Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Оптимизация выборки  (Прочитано 9670 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Arinyshka
Белый клоун, бедный мученик...
Постоялец

by
Offline Offline
Пол: Женский

« : 16-10-2008 14:10 » 

Есть хранимая ( Sql Server 2005)
Тело ее довольно большое, суть - много-много left join

Код:
CREATE PROCEDURE log_sel_AllMercuryLog
  @DateStart datetime,
  @DateEnd datetime
AS
  DECLARE
    @Id_Event int,
    @Id_LogResult int

  IF @DateStart IS NULL SET @DateStart = GETDATE()
  IF @DateEnd IS NULL SET @DateEnd = GETDATE()
  SET @DateStart = CAST(CAST(@DateStart AS INT) AS DATETIME)
  SET @DateEnd = CAST(CAST(@DateEnd AS INT) AS DATETIME)

  SET NOCOUNT ON
  SET @Id_Event = (SELECT TLE.id_TB0017 Id_TypeEvent
                FROM  Admin.dbo.TB0017 TLE
                WHERE (TLE.Alias_01 LIKE '%procedure%'))
               
  SET @Id_LogResult = (SELECT TLR.id_TB0018 Id_TypeResult
                    FROM  Admin.dbo.TB0018 TLR
                    WHERE (TLR.Alias_01 LIKE '%end%'))
                   
                   
  SELECT
    ML.id_TB0008 Id_Log,
    CAST(FLOOR(CAST(ML.Alias_01 AS FLOAT)) AS DATETIME) DateTimeLog,
    ML.Alias_01 TimeLog,
    ML.Alias_02 Id_Proc,
    ML.Alias_03 ProcName,
    ML.Alias_04 Id_Record,
    ML.Alias_05 NumDoc,
    ML.Alias_06 DateDoc,
    ML.Alias_07 Note,
    ML.Alias_08 ParserComment,
    MLEnd.Alias_01 DateEnd,
    DS.Alias_01 name_datasource,
    dbo.DataCrypt(DB.Alias_01) name_db,
    dbo.DataCrypt(USR.Alias_02) nick_name,
    TT.Alias_01 NameTable,
    TLE.Alias_01 NameEvent,
    TLR.Alias_01 NameResult
  FROM
    Admin.dbo.TB0008 ML
    LEFT JOIN
      Admin.dbo.TB0008 MLEnd
      ON
        MLEnd.Alias_02 = ML.Alias_02
        AND
        MLEnd.id_TB0017 = @Id_Event
        AND
        MLEnd.id_TB0018 = @Id_LogResult
    LEFT JOIN
      Admin.dbo.TB0004 DS
      ON
        DS.id_TB0004 = ML.id_TB0004 
   inner JOIN
      Admin.dbo.TB0003 DB
      ON
        DB.id_TB0003 = ML.id_TB0003
   inner JOIN
      Admin.dbo.TB0026 USR
      ON
       USR.id_TB0026 = ML.id_TB0026
    LEFT JOIN
      Admin.dbo.TB0019 TT
      ON
        TT.id_TB0019 = ML.id_TB0019
    LEFT JOIN
      Admin.dbo.TB0017 TLE
      ON
        TLE.id_TB0017 = ML.id_TB0017
    LEFT JOIN
      Admin.dbo.TB0018 TLR
      ON
        TLR.id_TB0018 = ML.id_TB0018
 WHERE
 CAST(FLOOR(CAST(ML.Alias_01 AS FLOAT)) AS DATETIME) BETWEEN @DateStart AND @DateEnd
  ORDER BY
    ML.Alias_01

Проблема проста - долго. Хранимая выбирает логи, их скапливается тысячами записей...
Можно ли решить проблему выборки из многих таблиц не join-ами? А как-нибудь быстрее?  Скромно так...
Записан

Непонятная свобода обручем сдавила грудь...
Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 16-10-2008 14:26 » 

посмотреть запрос профайлером, изучить план запроса
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 16-10-2008 14:40 » 

попробуй также использовать вот такую конструкцию

SELECT A.*, B.* FROM A WITH (NOLOCK) , B WITH (NOLOCK)

это даст возможность "грязного"чтения
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 16-10-2008 21:09 » 

Arinyshka, можно полюбопытствовать?

Код: (Text)
  SET @DateStart = CAST(CAST(@DateStart AS INT) AS DATETIME)
  SET @DateEnd = CAST(CAST(@DateEnd AS INT) AS DATETIME)
Что это за танцы с бубном? Никогда такого не видел, но ощущение, что это написано не просто так. Время отрезает что ли? Если да, то это хакерство, которое привязывает процедуру к внутреннему представлению типа DateTime. Если он изменится в какой-то будущей версии, ваш код перестанет работать. Лучше использовать функцию datepart или partdate - не помню, как называется.


По сути вопроса: Sla уже предложил профайлер. Хотя можно оценить эффективность и чисто умозрительно.

Например, X JOIN Y ON X.ID=Y.XID JOIN Z ON Y.ID=Z.YID выполняется последовательно. Если X содержит миллион записей, Y - полмиллиона и Z - тысячу, то в начале после первого объединения получится результат объёмом порядка полмиллиона, который затем будет соединяться с Z, что даст в итоге результат порядка тысячи записей.

Можно изменить порядок соединения, например, X JOIN Y JOIN Z ON Y.ID=Z.YID ON X.ID=Y.XID. Тогда в начале будут соединены Y и Z, что даст результат порядка тысячи записей, который потом будет соединён с X, что даст опять же результат порядка тысячи записей. Вместо соединения миллиона с полумиллионом мы получили соединение миллиона с тысячей записей, что улучшит производительность.


Правда всё это к представленному запросу не относится, поскольку там: во-первых, OUTER JOIN, во-вторых все другие таблицы соединяются с первой Улыбаюсь Но, допустим, INNER JOIN'ы можно попробовать "поднять" наверх поскольку их результат скорее всего уменьшает объём выборки, и только потом применять LEFT JOIN'ы.

В любом случае профайлер покажет, как сервер выполняет запрос. Ещё следует учитывать, что по крайней мере с SQL Server 2000 была беда, когда хранимые процедуры обновлялись на старой долгоживущей базе. При их обновлении по неактуальной статистике таблиц создавался неоптимальный план исполнения, который иной раз порождал совершенно безумные шаги. В результате при обработке запросов перегонялись миллиарды несуществующийх записей, порождённых JOIN'ами, и процедуры зависали на часы и сутки.
« Последнее редактирование: 16-10-2008 21:11 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Buxpb
Гость
« Ответ #4 : 17-10-2008 03:22 » 

1. Смотреть план ВЫПОЛНЕНИЯ.
2. Для таблиц обычно лучше указать LOOP опцию для JOIN, при условии что везде есть индексы.
3. Создать индексы необходимые
4. Как уже писали JOINить от более мелких таблиц к более крупным.

2Dimka. Имхо, не принципиально насчёт даты/времени. Не думаю, что в ближайшие десятилетия что-то там изменится. Половина кодов написано с учётом того, что Дата = Float.
Записан
Arinyshka
Белый клоун, бедный мученик...
Постоялец

by
Offline Offline
Пол: Женский

« Ответ #5 : 17-10-2008 06:16 » 

Arinyshka, можно полюбопытствовать?

Код: (Text)
  SET @DateStart = CAST(CAST(@DateStart AS INT) AS DATETIME)
  SET @DateEnd = CAST(CAST(@DateEnd AS INT) AS DATETIME)
Что это за танцы с бубном?
dimka, все как обычно (со мной) Улыбаюсь Процедурка писана ни разу не мной, автор давно уволился Улыбаюсь Мне велено переделать модуль, одна из существенных претензий - медленно Жаль
inner join-ами я заменила пару left join-ов, посмотрев повнимательнее на результаты. Подниму наверх Улыбаюсь
Про время почитаю, поищу чем заменить "танец" Улыбаюсь

1. Смотреть план ВЫПОЛНЕНИЯ.
2. Для таблиц обычно лучше указать LOOP опцию для JOIN, при условии что везде есть индексы.
3. Создать индексы необходимые
4. Как уже писали JOINить от более мелких таблиц к более крупным.
Повожусь с профайлером (стыдно... но ни разу еще так не делала) - отчитаюсь Улыбаюсь
Спасибо  за алгоритм действий Скромно так...
« Последнее редактирование: 17-10-2008 06:18 от Arinyshka » Записан

Непонятная свобода обручем сдавила грудь...
Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 17-10-2008 06:30 » 

Arinyshka, я вернусь чуть взад

SELECT A.* FROM A  WITH (NOLOCK)

видимо ты делаешь выборку из часто заполняющихся таблиц, в результате возникают различные блокировки
NOLOCK позволит читать таблицы не дожидаясь разблокировки
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Arinyshka
Белый клоун, бедный мученик...
Постоялец

by
Offline Offline
Пол: Женский

« Ответ #7 : 17-10-2008 07:45 » 

Да, ты прав. Таблицы логов обновляются фактически непрерывно. Сейчас вставлю, посмотрю Улыбаюсь
Записан

Непонятная свобода обручем сдавила грудь...
HandKot
Молодой специалист

ru
Offline Offline

« Ответ #8 : 23-10-2008 13:28 » 

иногда создание временной таблицы
и замена left join на update дает прирост,
т.е сначала создаете временную таблицу, в которой поля соответствуют вашему select
потом в нее инсертете данные из Admin.dbo.TB0008 ML
потом вместо каждого left join делать update временной таблицы

Записан

I Have Nine Lives You Have One Only
THINK!
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines