Помогите оптимизировать код...
...все нормально работает, но есть один момент, который меня очень смущает...
...пришлось (из-за нехватки знаний) засунуть запрос в цикл...
...тормоз получился страшный...
...если кто имеет опыт написания такого рода прог - подскажите пожалста как можно умнее реализовать это в коде...
в цикле передаются значения переменных с кодами групп товаров и групп контрагентов...
...и еще границы периодов (даты начала и конца периода)...
Если можно, то как?
1. составление запросов это большое искуство (приходит с опытом), бывает 3 дня думаешь над запросом, чтобы написать потом 3 несчастные, но такие нужные строчки. Так что не бросай свой запрос, возвращайся к нему периодически, может наконец блеснет в голове мысль как его улучшить
2. Из RecordSet ты не сможешь сделать выборку (а также все остальное) используя SQL. SQL работает токо с сохраненными объектами базы данных: таблицы, запросы. У тебя 3 варианта:
2.1 скинуть выборку во временную таблицу
2.2 написать сложный запрос с подзапросами, который все перелапатит в нужный вид
2.3 работать с RecordSet через VB, на Recordset'ы можно накладывать фильтры, сортировку, наконец вручную бегать по всем записям. Недостатки: как правило медленее чем SQL запрос, разница ощутима на больших таблицах
Может процедуры и ф-ии для БД использовать?
Тока я не знаю как это делается...:{
Set ADO = CreateObject("ADODB.Connection")
Set RS = CreateObject("ADODB.Recordset")
я иначе инициализировал...
Set ADO = NEW ADODB.Connection
Set RS = NEW ADODB.Recordset
Видели такое???
Set ADO = CreateObject("ADODB.Connection")
Set RS = CreateObject("ADODB.Recordset")
я иначе инициализировал...
Set ADO = NEW ADODB.Connection
Set RS = NEW ADODB.Recordset
А в чем проблема? и так и так можно
Правда у меня был как то раз прикол, один компонент .ocx не хотел создаваться через Set =New, зато заработал через CreateObject(""), но там возможно причина была в попытке прикрутить к компоненту лиценз... а может еще что
Видели такое???
Set ADO = CreateObject("ADODB.Connection")
Set RS = CreateObject("ADODB.Recordset")
я иначе инициализировал...
Set ADO = NEW ADODB.Connection
Set RS = NEW ADODB.Recordset
Чёй-то я немного не понял. По поводу запроса могу сказать так: я, лично (во многом потому, что у пользователей слабые машины, а сервак у нас мощьный), стараюсь весь код перекладывать на сервер. Бвыают запросы, когда вообще полпроги сервак лопатит, особенно, если клиентом к нему идёт какой-нибудь захудалый 100й "пень", или вообще 486.
Совет следующий. Если работаешь с СУБД, позволяющей использовать хранимые (серверные) процедуры, лучше использовать их. Плюсов два:
1. Всё это дело выполняется на серваке и тебе возвращается готовая выборка.
2. Если одинаковый код запроса используется в нескольких местах, можно это дело вогнать в хранимую процедуру и просто вызывать её.
Кроме того, циклы и т.д. можно орагнизовывать (MS SQL) проямо на сервере (хотя не рекомендуется, честно говоря - тна клиенте, по-моему. по любому это дело при однократном выполнении быстрее работает. зато при многократном обращении SQL кэширует результаты предыдущих обработок и выборки производятся гораздо быстрее).
Лично у себя для работы с SP (хранимыми процедурами) я применяю следующий алгоритм:
Процедура, например, UpbdeBD
Получает номер версии БД
Обращается к таблице, в которой хранится текущий номер версии БД и получает его.
Если номер версии меньше той, которая нужна, создаёт или изменяет хранимые процедуры (там же иеняю и структуры таблиц и т.д.)
Записывает новый номер версии БД в таблицу.
Это крайне удобно тем, что можешь вообще клиентскую БД "ручками" не трогать. Если в новой версии программы необходимо изменить что-то в БД, пишешь следующий ключ в процедуру обновления и передаёшь ей, соответственно, следующий ключ. Потом одновременно ставишь эту версию всем клиентам и первый, кто запустит прогу, автоматически обновит БД.
...он и так перегружен, а если я еще и свои отчеты на него повешу, то ему будет ж**а...
...по-сему тока локально и ни как иначе...
можно конечно в проге динамически создавать текст запроса, добавляя в условия выборки нужные коды...
...я уже об этом думал, но, по-моему, попахивает это большим гемороем:)
хотя, наверное, так делать и придется...
если кто то уже это делал - поделитесь опытом...
ну типа:
есть стандартный бланк для запроса (мона сделать как ини-файл [select], [from], [where])...
...ну и добавлять туда ключи, по необходимости...
Через сервак никак не получится...
...он и так перегружен, а если я еще и свои отчеты на него повешу, то ему будет ж**а...
...по-сему тока локально и ни как иначе...
можно конечно в проге динамически создавать текст запроса, добавляя в условия выборки нужные коды...
...я уже об этом думал, но, по-моему, попахивает это большим гемороем:)
хотя, наверное, так делать и придется...
если кто то уже это делал - поделитесь опытом...
ну типа:
есть стандартный бланк для запроса (мона сделать как ини-файл [select], [from], [where])...
...ну и добавлять туда ключи, по необходимости...
В генерации текста SQL запроса на ходу нет никакого гемороя. Это вообще самый простой и удобный прием, прямо как лом. У меня например даже есть в Access функция, которая условия отбора считывает из таблицы, в которую пользователи добавляют/удаляют условия. И это при том, что там по этим условиям могут быть абсол. разные запросы - обновление, удаление. - ничего, пробегает по таблице, генерит сначал одни запросы, потом другие.
Вообщем с этим методом одна проблема, к нему легко привыкнуть, потом ограничивать себя сложно :-). Не знаю как в Интербейзовской БД, а в Access по возможности лучше использовать сохраненные запросы, потому как они уже откомпиллированы и оптимизированы для ядра БД и работают немного быстрее, чем сгенерированные на лету...
В генерации текста SQL запроса на ходу нет никакого гемороя. Это вообще самый простой и удобный прием, прямо как лом. У меня например даже есть в Access функция, которая условия отбора считывает из таблицы, в которую пользователи добавляют/удаляют условия. И это при том, что там по этим условиям могут быть абсол. разные запросы - обновление, удаление. - ничего, пробегает по таблице, генерит сначал одни запросы, потом другие.
Вообщем с этим методом одна проблема, к нему легко привыкнуть, потом ограничивать себя сложно :-). Не знаю как в Интербейзовской БД, а в Access по возможности лучше использовать сохраненные запросы, потому как они уже откомпиллированы и оптимизированы для ядра БД и работают немного быстрее, чем сгенерированные на лету...
Ну, во-первых. создание запроса "на лету" - довольно простая вешь. Например:
Private Sub ReListLv()
'МАХ процедура заполнения ЛистВью
On Error GoTo ErrHnd 'МАХ в случае ошибки передаём управление обработчику
Dim StrParam As String 'МАХ строка, хранящая параметры запроса
Dim BullWhere As Boolean 'МАХ флаг вставки Where
Dim LocADORs As New ADODB.Recordset 'МАХ локальный рекордсет
Dim I As Long
Dim Li As Object
Dim StrCl As String
Dim Key As String
BullWhere = False
StrParam = "'" 'МАХ SQL - нулевая строка
'МАХ формирую выражение условия и сортировки по значениям из полей _
критериев поиска
If TxtCriteria(0).Text <> vbNullString Then
StrParam = StrParam & " WHERE (TravID = " & CLng(TxtCriteria(0).Text) & ")"
BullWhere = True
End If
If TxtCriteria(1).Text <> vbNullString Then
If BullWhere Then
StrParam = StrParam & " AND (UPPER(Name1) LIKE UPPER('" & ToStrSql(TxtCriteria(1).Text) & "') + ''%'')"
Else
StrParam = StrParam & " WHERE (UPPER(Name1) LIKE UPPER('" & ToStrSql(TxtCriteria(1).Text) & "') + ''%'')"
BullWhere = True
End If
End If
If TxtCriteria(2).Text <> vbNullString Then
If BullWhere Then
StrParam = StrParam & " AND (UPPER(Name2) LIKE UPPER('" & ToStrSql(TxtCriteria(2).Text) & "') + ''%'')"
Else
StrParam = StrParam & " WHERE (UPPER(Name2) LIKE UPPER('" & ToStrSql(TxtCriteria(2).Text) & "') + ''%'')"
BullWhere = True
End If
End If
If TxtCriteria(3).Text <> vbNullString Then
If BullWhere Then
StrParam = StrParam & " AND (UPPER(OldName) LIKE UPPER('" & ToStrSql(TxtCriteria(3).Text) & "') + ''%'')"
Else
StrParam = StrParam & " WHERE (UPPER(OldName) LIKE UPPER('" & ToStrSql(TxtCriteria(3).Text) & "') + ''%'')"
BullWhere = True
End If
End If
If CbCntHtl(1).ListIndex > 0 Then
If BullWhere Then
StrParam = StrParam & " AND (HotelID = " & CbCntHtl(1).ItemData(CbCntHtl(1).ListIndex) & ")"
Else
StrParam = StrParam & " WHERE (HotelID = " & CbCntHtl(1).ItemData(CbCntHtl(1).ListIndex) & ")"
BullWhere = True
End If
End If
If CbCntHtl(0).ListIndex > 0 Then
If BullWhere Then
StrParam = StrParam & " AND (ContryID = " & CbCntHtl(0).ItemData(CbCntHtl(0).ListIndex) & ")"
Else
StrParam = StrParam & " WHERE (ContryID = " & CbCntHtl(0).ItemData(CbCntHtl(0).ListIndex) & ")"
BullWhere = True
End If
End If
If Not IsNull(DTPDate(0).Value) Then
If BullWhere Then
StrParam = StrParam & " AND ((convert(varchar,(DateFrom),104)) = convert(varchar,(Convert(DateTime,'" & ToSqlDate(DTPDate(0).Value) & "')),104))"
Else
StrParam = StrParam & " WHERE ((convert(varchar,(DateFrom),104)) = convert(varchar,(Convert(DateTime,'" & ToSqlDate(DTPDate(0).Value) & "')),104))"
BullWhere = True
End If
End If
If Not IsNull(DTPDate(1).Value) Then
If BullWhere Then
StrParam = StrParam & " AND ((convert(varchar,(DateTo),104)) = convert(varchar,(Convert(DateTime,'" & ToSqlDate(DTPDate(1).Value) & "')),104))"
Else
StrParam = StrParam & " WHERE ((convert(varchar,(DateTo),104)) = convert(varchar,(Convert(DateTime," & ToSqlDate(DTPDate(1).Value) & ")),104))"
BullWhere = True
End If
End If
If Not IsNull(DTPDate(2).Value) Then
If BullWhere Then
StrParam = StrParam & " AND ((convert(varchar,(DateInsert),104)) = convert(varchar,(Convert(DateTime,'" & ToSqlDate(DTPDate(2).Value) & "')),104))"
Else
StrParam = StrParam & " WHERE ((convert(varchar,(DateInsert),104)) = convert(varchar,(Convert(DateTime,'" & ToSqlDate(DTPDate(2).Value) & "')),104))"
BullWhere = True
End If
End If
If OptSel(1).Value = True Then
If BullWhere Then
StrParam = StrParam & " AND (DateInsert > GetDate())"
Else
StrParam = StrParam & " WHERE (DateInsert > GetDate())"
BullWhere = True
End If
End If
If OptSel(2).Value = True Then
If BullWhere Then
StrParam = StrParam & " AND (DateInsert <= GetDate())"
Else
StrParam = StrParam & " WHERE (DateInsert <= GetDate())"
BullWhere = True
End If
End If
'МАХ параметр сортировки
If CbxSorted.ListIndex >= 0 Then
Select Case CbxSorted.ListIndex
Case 0
Case 1
StrParam = StrParam & " ORDER BY Name1"
Case 2
StrParam = StrParam & " ORDER BY Name2"
Case 3
StrParam = StrParam & " ORDER BY OldName"
Case 4
StrParam = StrParam & " ORDER BY ContryName"
Case 5
StrParam = StrParam & " ORDER BY HotelName"
Case 6
StrParam = StrParam & " ORDER BY TravID"
Case 7
StrParam = StrParam & " ORDER BY DateFrom"
Case 8
StrParam = StrParam & " ORDER BY DateTo"
Case 9
StrParam = StrParam & " ORDER BY DateInsert"
End Select
End If
If CbxSorted.ListIndex <> 0 Then
If ChkRevers.Value = 1 Then
StrParam = StrParam & " DESC"
End If
End If
StrParam = StrParam & "'"
If LocADORs.State <> adStateClosed Then
LocADORs.Close
End If
StrSql = "EXECUTE Sp_MainSerch " & StrParam & ", " & ToStrSql(AgencyID)
Set LocADORs = GlADOConn.Execute(StrSql)
DoEvents
'МАХ теперь заполняем List View
I = 1
StrCl = "*"
LvFind.ListItems.Clear
If Not LocADORs.BOF And Not LocADORs.EOF Then
While Not LocADORs.EOF
With LvFind
Key = vbNullString
'МАХ чтобы избежать дублирования ключей
If Not IsNull(LocADORs.Fields(16)) And LocADORs.Fields(16) <> 0 Then
Key = LocADORs.Fields(16) & "_"
Else
Key = LocADORs.Fields(0) & StrCl
StrCl = StrCl & "*"
End If
Set Li = .ListItems.Add(I, Key) ', LocADORs.Fields(14)
.ListItems(I).Checked = False
If LocADORs.Fields(14) = 0 Then
Li.SubItems(1) = "Нет"
Else
Li.SubItems(1) = "Да"
End If
Li.SubItems(2) = LocADORs.Fields(2)
Li.SubItems(3) = LocADORs.Fields(13)
Li.SubItems(4) = LocADORs.Fields(10)
Li.SubItems(5) = LocADORs.Fields(11)
Li.SubItems(6) = LocADORs.Fields(12)
Li.SubItems(7) = LocADORs.Fields(1)
If CDate(LocADORs.Fields(3)) = CDate("01-01-1900") Then
Li.SubItems(8) = "Не определена"
Else
Li.SubItems(8) = Format(LocADORs.Fields(3), "DD.MM.YYYY")
End If
Li.SubItems(9) = LocADORs.Fields(15)
Li.SubItems(10) = LocADORs.Fields(4)
Li.SubItems(11) = LocADORs.Fields(0)
Li.SubItems(12) = LocADORs.Fields(8)
Li.SubItems(13) = LocADORs.Fields(16)
End With
LocADORs.MoveNext
Wend
End If
'МАХ выставим ToolTipText
LvFind.ToolTipText = "Всего по запросу найдено: " & LvFind.ListItems.Count
If Not (LocADORs Is Nothing) Then
If LocADORs.State <> adStateClosed Then
LocADORs.Close
End If
Set LocADORs = Nothing
End If
Exit Sub
ErrHnd: 'МАХ область обработки ошибок
If Not (LocADORs Is Nothing) Then
If LocADORs.State <> adStateClosed Then
LocADORs.Close
End If
Set LocADORs = Nothing
End If
MousePointer = vbDefault
'МАХ передача управления процедуры ведения лога
ErrDescr Err.Number, Err.Description, "FrmMenu.ReListLv"
End Sub
Процедура "на лету" генерит поисковые критерии для выборки и тип сортировки.
Кстати, если Вы заметили, опять-таки используется хранимая процедура в качестве "оси" запроса, куда просто передаётся набор параметров выборки в виде текстовой строки:) .
Ну а во-вторых, запросы всё равно выполняются на сервере, поэтому применение хранимых процедур не особо загрузит его (если эти SP простые - без циклов и т.д.). А выйгрыш в работе с кодом и по скорости вполне настоящий (особенно в части удобства работы с кодом). Сравни, например, если ты используешь клиентский курсор и передаёшь на сервер строку запроса бог знает какой длинны, то тебе надо отослать запрос на сервер, выполнить его, принять результаты со всеми ограничениями по скорости клиентского курсора... К тому же дождаться. пока на сервере очередь освободиться...
При использовании же SP ты передаёшь серваку имя SP и параметры (если необходимо). А он сам уже находит её у себя, сам лопатит, как ему надо, а, если SP в обозримом прошлом с такими же параметрами уже выполнялась, проверяет просто, не изменились ли результаты отработки той SP, и возвращает уже готовый результат (со всеми соответствующими ограничениями). Правда, если сервак здорово гружён, лучше не использовать в теле SP циклы, чтение построчно из временных таблиц и т.д. Но ведь и в чисто клиентском, передаваемом запросе это "в лоб" делать не рекомендуется - прийдётся делить код на чисто клиентский и серверный.