Зависит ли скорость запроса от условия?

Работа VB и СУБД (Access, MSSQL, MySQL, Oracle и пр.)
Правила форума
При создании новой темы не забывайте указывать используемую СУБД.
kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Зависит ли скорость запроса от условия?

Сообщение kibernetics » 09.06.2007 (Сб) 18:50

Скажите, есть ли различия в скорости запросов в следующих ситуациях:

Выбераем поля, напрмер:
Код: Выделить всё
SELECT CounterID, Description, Value

и делаем два различных условия:
Код: Выделить всё
WHERE Value = 0 AND Description Like "*text*" AND CounterID <> 5


или, одинаковое по значению

Код: Выделить всё
WHERE CounterID <> 5 AND Value = 0 AND Description Like "*text*"


как работает выборка? Вначале сортируется первое поле, потом следующие? Или всё делается одновременно? Просто я думаю, что если всё делается последовательно, то логичнее определить в выборе на первом месте наименьшее условие, а потом в найденных записях искать проверять остальные условия. Типа, зачем искать все поля в которых есть текст *что-то*, если к примеру второе поле должно равнятся True, и только после этого уже искать в найденных с полем True *что-то*.
Ну а если всё делается одновременно, т.е. проверяется КАЖДАЯ строчка на условие, то тогда вопрос сам собой отпадает...

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 09.06.2007 (Сб) 19:02

Ты не там об оптимизации задумался.
Оптимизировать бы следовало Like "*text*" (кстати, ты в курсе, что в ADO надо использовать %, а не * ?). На практике подобный поиск нужен редко и его вполне можно заменить на хотя бы text*.
Lasciate ogni speranza, voi ch'entrate.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 09.06.2007 (Сб) 19:39

alibek писал(а):Ты не там об оптимизации задумался.
Оптимизировать бы следовало Like "*text*" (кстати, ты в курсе, что в ADO надо использовать %, а не * ?). На практике подобный поиск нужен редко и его вполне можно заменить на хотя бы text*.


Да, в курсе... запросы работают, я уже столкнулся со *.

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

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 09.06.2007 (Сб) 19:47

Ты что, наугад выбираешь места для оптимизации? :-?
Изображение

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 09.06.2007 (Сб) 20:28

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

вот и в данном случае имею десять полей с запроса, и думаю, ускорится ли запрос при перемене очередности условия.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 09.06.2007 (Сб) 21:00

Вот меня, перфекциониста, более опытные товарищи постоянно учат не чинить то, что не сломано...
Изображение

iGrok
Артефакт VBStreets
Артефакт VBStreets
 
Сообщения: 4272
Зарегистрирован: 10.05.2007 (Чт) 16:11
Откуда: Сетевое сознание

Сообщение iGrok » 09.06.2007 (Сб) 23:07

Вообще, по идее, выборка должна работать по принципу проверки каждой записи на соответствие всем условиям сразу...

Но вот тут я могу и ошибаться..

Поправьте, если ошибаюсь, плз! Самому этот момент интересен..

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 09.06.2007 (Сб) 23:50

По-моему, скорость обработки запроса не зависит от последовательности записи условий. Сам когда-то эксперементировал, правда с джоинами. А потом смотрел схему выполнения и вне зависимости от моих "оптимизаций" MS SQL всегда выстраивал её одинаково или так как считал нужным. Т.ч. нет смысла заморачиваться с этим. Всё сказанное исключительно моё личное ничем не подверждённое субъективное ощущение.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 11.06.2007 (Пн) 11:31

чтобы не хламить форум, попробую задать вопрос тут:

1. можно ли запросить все записи, но получить только первые 50?
2. можно ли определить диапазон записей для получения? например, с 50 по 100?

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 11.06.2007 (Пн) 12:07

1. SELECT TOP 50 ...
2. Не во всех инкарнациях SQL есть возможность задать диапазон. Обычно пэйджинг реализуется указанием размера страницы (PageSize) и перебором записей в пределах выбранной страницы (AbsolutePage).

Код: Выделить всё
oRs.Open  sql, oConn, adOpenStatic
oRs.PageSize = 50
If (Not oRs.EOF) Then oRs.AbsolutePage = 2
Do Until (oRs.EOF Or oRs.AbsolutePage <> 2)
    ...
    oRs.MoveNext
Loop

Как-то так. Писал по памяти, могут быть ошибки.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 11.06.2007 (Пн) 12:24

skiperski
класс! спасибо!

пейджинг по всей видимости сильная штуковина, только нужно разобраться...

а что значит:
Не во всех инкарнациях SQL есть возможность задать диапазон


как можно узнать про свою инкарнацию? если есть абсолютпейдж, значит у меня инкарнация "нормальная"?

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 11.06.2007 (Пн) 12:51

kibernetics писал(а):а что значит:
Не во всех инкарнациях SQL есть возможность задать диапазон

В некоторых реализациях SQL, кажется так умеет MySQL, можно прямо в запросе указать диапазон выбираемых значений, т.е. не только верхние TOP 50, а например с 51 по 100.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 11.06.2007 (Пн) 15:31

особой скорости не прибавилось. :(
30.000 записей.
выборка идёт по 10 полям, которые в зависимости от id в поле, берут из соединённых таблиц значения. плюс, возникает иногда необходимость использование WHERE.

однако, если брать все записи:
Код: Выделить всё
SELECT * FROM tbl_Subjects
, без WHERE и смежных таблиц, то скорость возврата запроса просто отличная.

я думаю, если получить все 30.000 записей, потом определять PageSize (допустим 50), применять только к AbsolutPage фильтеринг WHERE и уже работать с полученным результатом. Если к примеру, PageSize не заполнился фильтром, то проверять до конца всех записей путём прибавления AbsolutPage + 1 до тех пор, пока PageSize = 50 или rs.EOF

или так не получится быстрее?

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 11.06.2007 (Пн) 15:41

мда... , в таком случае я не получу общее количество с условием по всей базе...

в общем, как я правильно понимаю, нужно оптимизировать запрос, но а если уже ничего не оптимиируется???

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 11.06.2007 (Пн) 17:06

Тут уже надо смотреть на мелочи: какой тип курсора, сколько записей кешируется, какие индексы в БД, как часто вся эта байда запрашивается и т.п. И обясните мне кому когда-либо может понадобиться 30 тыс. записей одновременно? Что он с ними будет делать? А вычитать 50 записей даже из такой выборки -- не проблема. Если тормозит, значит что-то не так с запросом. Проверяй ключи, индексы, типы полей. Выбирай только те поля, что нужны, исключи поля переменной длины и BLOB'ы. Иногда вместо одного сложного запроса существенно быстрее работают несколько маленьких подзапросов. Короче, вариантов для оптимизации -- море.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 11.06.2007 (Пн) 17:43

я разобрался скорей всего где у меня бага:
дело в том, что я постранично заполняю листвью. (ещё незнал про абсолютпейдж). Делал примерно так:
1. Запросил данные
2. Переменная iStep * 50 указывала на положение в данных
3. Делал при каждом обновлении листвью инкремент или уменьшение iStep +/- 1
4. Смещался на
rs.MoveFirst
rs.MoveNext на (iStep * 50) - 50
(это всё примерно, код сам не такой, но суть такая)
5. Заливал листвью 50 записями

и...
закрывал рекордсет!
следущий инкремент заново открывал rs (отсюда и тормоза) и всё по новому. Работало без проблем, пока я не прикрутил реальную БД. Изначально я тренировался на 200 записей примерно, а на 30.000 уже пошло тормозить.

Теперь я объявляю рекордсет как паблик в отдельном модуле:
Код: Выделить всё
Public vRS As New ADODB.Recordset


Однако, не смотря на то, что я нашёл причину тормозов, у меня есть несколько вопросов "пунктуации" кода.

1. Правильно ли я делаю, когда уничтожаю rs при закрытии child окна, таким образом:
Код: Выделить всё
Set vRS = Nothing
, или его желательно прибивать иначе и/или где-то в ином месте?

2. В приложении 4 окна, каждое предназначено для своих целей, поэтому, мне требуется четыре независимых rs но, работающих с одной и той же базой (отличаются запросами). Как правильно объявить recordsets в этом случае?
Код: Выделить всё
Public vRS As New ADODB.Recordset
Public vRS1 As New ADODB.Recordset
Public vRS2 As New ADODB.Recordset
Public vRS3 As New ADODB.Recordset
дело в том, что не факт, что окно откроют, а рекордсет уже проинициализируется и займёт место в памяти... может его как-то динамически создавать, (и как это делается), или его создавать только при загрузке чайлд-окна?

3. Если, к примеру, в четырех rs будет примерно 30.000, 15.000, 10.000, 2.000 записей соответственно, не сильно ли это скажется на подвисании компьютера?

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 11.06.2007 (Пн) 22:13

kibernetics писал(а):1. Правильно ли я делаю, когда уничтожаю rs при закрытии child окна, таким образом:
Код: Выделить всё
Set vRS = Nothing
, или его желательно прибивать иначе и/или где-то в ином месте?

Вообще-то лучше сначала его закрыть vRS.Close, а потом уже и прибить указатель на него.

kibernetics писал(а):2. В приложении 4 окна, каждое предназначено для своих целей, поэтому, мне требуется четыре независимых rs но, работающих с одной и той же базой (отличаются запросами). Как правильно объявить recordsets в этом случае?
Код: Выделить всё
Public vRS As New ADODB.Recordset
Public vRS1 As New ADODB.Recordset
Public vRS2 As New ADODB.Recordset
Public vRS3 As New ADODB.Recordset
дело в том, что не факт, что окно откроют, а рекордсет уже проинициализируется и займёт место в памяти... может его как-то динамически создавать, (и как это делается), или его создавать только при загрузке чайлд-окна?

Пока рекордсет не открыт -- это просто объект. Он конечно же откусывает память, но не сильно. Кроме того не обязательно создавать экземпляры объекта сразу. Т.е. переменные описаваем как
Код: Выделить всё
Public vRS1 As ADODB.Recordset
, т.е. без New, а когда объект нужен, то создаём его явно
Код: Выделить всё
Set vRS1 = New ADODB.Recordset



kibernetics писал(а):3. Если, к примеру, в четырех rs будет примерно 30.000, 15.000, 10.000, 2.000 записей соответственно, не сильно ли это скажется на подвисании компьютера?

Это зависит от типа курсора и способа открытия. Если курсор клиентский, то в RS выбираются все записи и тогда можно сэкономить на коннекции -- закрыть её. Получится отсоединённый RS. Такой RS удобен для длительной работы с одним и тем же набором записей, но жрёт память. Можно открыть динамически, т.е. он будет подтягивать с сервера только определённое кол-во записей, а при перемещении по нему и исчерпании кеша -- подтягивать следующую порцию. Если при этом выставить кол-во кешируемых записей равное размеру страницы, то в памяти единовременно будут только записи активной страницы. Будет немного притормаживать при листании (теребить сервер).

Как-то так. Меня тут на пол часа отвлекли, а теперь уже переключился на другую задачу и мыслЮ потерял. :)

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 12.06.2007 (Вт) 0:14

тип курсора:
Код: Выделить всё
vRS.Open sSQL, vDB, adOpenStatic, adLockOptimistic


перепробовал все. эта связка самая быстрая, быстрее даже чем readonly почему-то...

ну еще можно попробовать adLockPessimistic, по скорости вроде одинаково

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

skiperski
писал(а):
Код:
Public vRS1 As ADODB.Recordset
, т.е. без New, а когда объект нужен, то создаём его явно
Код:
Set vRS1 = New ADODB.Recordset


вот это важное замечание, спасибо.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 12.06.2007 (Вт) 11:03

Блин, не могу скумекать как сделать переключатель между рекордсетами:

Private vRS0 As ADODB.Recordset
Private vRS1 As ADODB.Recordset

Public Sub LoadQry(iCurrRS as Integer, boolNew as Boolean)

Select Case iCurrRS
Case 0
'текущий рекордсет такой-то '(0)
Case 1
'текущий рекордсет такой-то '(1)
End Select
End Sub

If boolNew Then 'если рекордсета не существует, то создать
Set текущий рекордсет = New ADODB.Recordset
End If

' работаем с текущевыбранным
For i = 1 To теущевыбранныйрекордсет.RecordsCount
...
Next i

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 12.06.2007 (Вт) 11:37

Во-первых, никто не мешает сделать массив Recordset'ов.
Во-вторых, проверять лучше не левую переменную, а сам RS на предмет того, что он Nothing.
В-третьих, RS лучше перебирать в условных циклах до EOF, потому, что не все типы RS сразу после открытия возвращают правильный RecordCount.

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 12.06.2007 (Вт) 11:59

понял! осенило...
сейчас попробую всё на деле...

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 13.06.2007 (Ср) 19:15

всё работает, но вопросы остались. если можно ответьте плиз:

Есть ли смысл открывать весь рекордсет на стороне клиента, и применять к нему фильтры? Или лучше запрашивать новый запрос с условиями? Что будет быстрее работать?

Т.е. у меня есть rs я из него хочу выбрать по условию какие-то записи в новый rs...
Что будет лучше, запросить заново или выбрать из уже открытого? Нужна скорость. От этого просто зависит тип курсора, потому как клиентский, при открытии, работает медленнее... А к сервачному не применишь .Filter

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 13.06.2007 (Ср) 19:40

Сильно зависит от задачи. Если всегда требуются актуальные данные, то только постоянные запросы. Если актуальностью данных на время сессии можно пренебречь, и убедить пользователя, что программа долго открывается, но потом быстро работает ("лучше день подождать, а потом за час долететь" Крылья, ноги и хвосты), то можно на клиента при открытии заливать данные, а потом вообще отключаться от сервера и оперировать данными в памяти. Но программку запускают несколько раз на день и она жутко тормозит, то опять лучше запросы. Короче, общего подхода нет.


Вернуться в Базы данных

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 242

    TopList