24 January 2009

Повышение скорости работы SQL-запросов

Lumber room
Сразу оговорюсь, запросы в примерах – Transact SQL, он мне как-то роднее =)
Но принципы, в общем-то, должны работать везде.
Статья не претендует на новизну, и тем более, на полноту. Я лишь попытался вспомнить часто встречающиеся ошибки или недочеты в запросах, которые приводят к медленной работе с БД.

Поиск показал, что статья частично пересекается с этим топиком, но не во всем =)



Типы данных в полях


Самое очевидное – не использовать типы данных «с запасом». То есть если у нас есть поле «ICQ» типа VarChar, делать его длиннее 10 символов бессмысленно. Аналогично, если есть внешний ключ к справочнику, в котором всего несколько записей, нет смысла задавать ему тип Int, хватит и SmallInt. Несмотря на очевидность ошибки, встречается повсеместно.

Использование * в запросе


Вообще говоря, было много споров на эту тему, но я стараюсь не использовать «*» в SQL-запросах.
Во-первых, явное перечисление выбираемых полей повышает читабельность кода.
Во-вторых, в выборке далеко не всегда нужны все поля таблицы. А если мы связываем в запросе несколько таблиц, то практически всегда конструкция «Select *» потянет из базы в выборку кучу ненужных полей, например, ключи, по которым связаны таблицы. Столкнулся один раз с ситуацией, когда в таблице в текстовом поле хранились наименования файлов, а в бинарном поле – их содержимое. И запрос, который должен был всего лишь выдавать список файлов, грузил в память сервера еще и их содержимое. Тормозило это безбожно.

Использование курсоров


Людям, привыкшим к императивным языкам программирования, курсоры в SQL – как бальзам на душу. Ведь цикл по записям – это так привычно и понятно. Иногда из-за этого рождается медленный код. Пример:
Предположим, у нас есть таблица, в ней 4 поля: ID, Value1, Value2 и Summa. Задача: пересчитать таблицу, то есть посчитать значение Value1+Value2 и положить в поле Summa.

Вариант 1, с курсором:


  1. DECLARE @ID int
  2. DECLARE @Val1 int
  3. DECLARE @Val2 int
  4. DECLARE #curr CURSOR LOCAL FAST_FORWARD READ_ONLY FOR
  5. SELECT ID, Value1, Value2 from Table1
  6. OPEN #curr FETCH #curr INTO @ID, @Val1, @Val2
  7. WHILE @@FETCH_Status=0
  8. BEGIN
  9. UPDATE Table1 SET Summa=@Val1+@Val2 where ID=@ID
  10. FETCH #curr INTO @ID, @Val1, @Val2
  11. END CLOSE #curr DEALLOCATE #curr


Вариант 2, без курсора:


  1. UPDATE Table1 SET Summa=Value1+Value2


Понятно, что на таком простом примере вариант 2 очевиден. Но при более сложных вычислениях, для простоты реализации программист выбирает вариант с курсором – и ощутимо проигрывает в скорости.

Использование индексов


Без комментариев. Про индексы забывают сплошь и рядом, особенно новички.

Использование хранимых процедур


При выполнении сложных вычислений, использующих много значений из БД, их лучше оформить в виде хранимых процедур на сервере, нежели вычислять на клиентской стороне – зачем передавать на клиент исходные данные для вычислений, когда можно передать только результат.

Использование временных таблиц


Периодически, особенно при работе с результатами вычислений или сводными данными, проще и быстрее один раз запустить сложный запрос на выборку всех необходимых данных, сохранить его результаты во временную таблицу и дальше работать уже с ней, нежели каждый раз запускать сложный и медленный запрос.

Пожалуй это все, что пришло в голову навскидку, если статья кого-нибудь заинтересует, можно повспоминать еще.
Tags:SQLбазы данныхскорость
Hubs: Lumber room
+7
1.9k 17
Comments 15
Ads