|
|
|
| Хочу выполнить N раз запрос на добавление, в котором условием отбора будет значение поля таблицы, содержащей N записей. Т.е. циклично выполнить запрос для каждой записи таблицы.
Понимаю, что в процедуре должен использоваться рекордсет, переходящий при каждой итерации цикла к следующей записи, но как засунуть его в запрос и потом записать результаты в конечную таблицу? Просто я почти никогда не касался VBA и в программировании ламер.
Просто подскажите как организовать тело процедуры, какие методы и функции использовать. Дальше постараюсь сам разобраться.
Сам пока читаю "программирование на ВБА"
Спасибо всем, готовым помочь новичку! | |
|
| |
|
|
|
| если вы опишите задачу полнее, то возможно все можно сделать одним запросом для всех строк таблицы, а не для каждой в отдельности | |
|
| |
|
|
|
| Дело в том, что я так уже сделал. Мне нужно было посчитать сумму нарастающим итогом для каждой отдельной группы массива данных. В запросе мне это удалось сделать только через рекурсию исходного запроса на самого себя. Но это значит, что число оперций при выполнении данного запроса равно число записей исходного запроса в квадрате, т.е. растет экспоненциально числу записей.
Если обработку исходного запроса разделить на N частей (скажем по числу групп), то итогое число операций уменьшается в N раз. Для этого я и хочу такую процедурку. | |
|
| |
|
|
|
| а запрос с группировкой и суммированием нужных полей не подойдет для твоего алгоритма? | |
|
| |
|
|
|
| kot_k_k, к сожалению нет. (( Тут ни Sum, ни Dsum не поможет, но ни в этом дело. Запрос с расчетом нарастающего итога по группам я сделал, но он очень низкопроизводительный, и я хочу его оптимизировать.
Могу более детально описать ситуацию на примере.
Например исходный массив, который я предварительно собираю запросом:
№ Компания дата Сумма
1 К1 01.01.2010 100
2 К1 02.01.2010 0
3 К1 03.01.2010 -20
4 К2 01.01.2010 0
5 К2 02.01.2010 0
6 К2 03.01.2010 50
7 К3 01.01.2010 30
8 К3 02.01.2010 -20
9 К3 03.01.2010 5
N ...... ....... ....
Нужно было получить нар.итог и записать результат в таблицу (в след виде):
№ Компания дата Нар.итог
1 К1 01.01.2010 100
2 К1 02.01.2010 100
3 К1 03.01.2010 80
4 К2 01.01.2010 0
5 К2 02.01.2010 0
6 К2 03.01.2010 50
7 К3 01.01.2010 30
8 К3 02.01.2010 10
9 К3 03.01.2010 15
N .... ... ....
Если через Sum, тогда на 01.01.2010 сумма будет 130 для к1,к2 и к3, на 02.01.2010 - 110 и т.д.. Т.е. сложит все без группировки.
Я нар.итог посчитал как:
Нар.итог: (SELECT Sum(T.Сумма) FROM Таблица1 As T WHERE (Таблица1.Компания=T.[Компания]) And (T.[Дата]<=Таблица.[Дата]) )
Это рекурсия и для выполнения запроса производится N^2 итераций. Записей сейчас ок.10тыс., что дает 100млн. итераций, каждая из которых связана с кучей расчетов (не только по этому полю). Мой 4ядерный комп на этот запрос тратит более 2-х часов.
Поэтому я хочу оптимизировать процесс - и в процедуре считать нар.итог отдельно для к1, потом к2 и к3 (уже без рекурсии, через Sum). И последовательно записывать результат в итоговую таблицу.
Вот мой вопрос - как мне написать такую процедуру?
Думаю что ничего сложного, просто в ВБА ничего не секу ;)
Если будут другие идеи по реализации задачи буду рад! | |
|
| |
|
|
|
|
Нар.итог: (SELECT Sum(T.Сумма) FROM Таблица1 As T WHERE (Таблица1.Компания=T.[Компания]) And (T.[Дата]<=Таблица.[Дата]) )
Это рекурсия и для выполнения запроса производится N^2 итераций.
|
Вот здесь вы ошибаетесь ИМХО.
Никакой рекурсии я тут не наблюдаю.
Для каждой записи Таблица1 будет выполнен ОДИН суммирующий запрос.
Выполнение этих суммирующих запросов при посредничестве VBA только затормозит расчет.
Наличие индексов по полям Таблица1.Компания и Таблица.[Дата] могут ускорить первоначальный запрос.
Надеюсь поле Таблица1.Компания имеет тип Long и содержит внешний ключ на справочную таблицу компаний?
Если нет, и это поле типа Text, то это и есть "главный тормоз". | |
|
| |
|
|
|
|
Никакой рекурсии я тут не наблюдаю.
Для каждой записи Таблица1 будет выполнен ОДИН суммирующий запрос.
|
Lukas, может вы и правы и это не рекурсия в чистом виде. Но ведь сравнивается таблица1 с самой собой и для выполнения суммирующего запроса для каждой записи просматриваются все записи этой же таблицы1.
Надеюсь поле Таблица1.Компания имеет тип Long и содержит внешний ключ на справочную таблицу компаний?
|
к сожалению, тут вы правы "Таблица1.Компания" - это текстовое поле. Я его связал с ключевой таблицей и включил индексацию по Компания и Дата. В результате +1/3 производительности. Это конечно хорошо, за что, Lukas, большое спасибо! Но в целом проблему не решает. Если бы уменьшить время в разы...
Все же хочу сделать что запланировал, чувствую, что должно помочь. Ведь тогда, вместо того геморойного подзапроса для "нар.итог" всей исходной таблицы достаточно будет делать обычную группировку с суммированием для каждой группы: к1,к2 и т.д.
По-прежнему, прошу (уже всех) показать как получить на VBA список компаний и в цикле выполнять один и тот же запрос для каждой компании пока список не кончится одновременно записывая результаты выполнения запросов в стороннюю таблицу.
Кстати, может есть другой способ рассчитать поле "нар.итог" из моего примера? | |
|
| |
|
57 Кб. |
|
| Параллельно еще вопрос.
Стал разбираться в азах модели ADO и в самом начале затыка.
Компилятору не нравится строчка: Dim rsTransh As ADODB.Recordset
Ругается на определение типа переменной (см. вложение)
Как что поправить? Вроде же этот стандартный тип в Access? | |
|
| |
|
|
|
| Ну так компилятор и говорит, что нет описания этого типа/класса.
То есть не подключена библиотека ADO. | |
|
| |
|
|
|
| Спасибо! Библиотеку подключил, дело пошло дальше )) | |
|
| |
|
|
|
| Можно сделать что-то типа этого:
Dim Db As Database
Dim Rs As Recordset
Dim Itog As Long
Set Db = CurrentDb
Set Rs = Db.OpenRecordset("SELECT DISTINCT Компания FROM Tabl", dbOpenDynaset)
Rs.MoveFirst
Do Until Rs.EOF
Set Rs1 = Db.OpenRecordset("SELECT * FROM Tabl WHERE Компания=""" & Rs.[Компания] & """ ORDER BY Дата", dbOpenDynaset)
Rs1.MoveFirst
Do Until Rs1.EOF
Itog = Itog + Rs1.[Сумма]
Rs1.[Итог] = Itog
... Ну, и другие расчеты ...
Rs1.Update
Rs1.MoveNext
Loop
Rs1.Close
Rs.MoveNext
Loop
Rs.Close
Db.Close
Это конечно не SQL, но зато никакой рекурсии - последовательное обращение к записям.
Даже 10 тыс. записей должны посчитаться довольно быстро.
ADO не юзаю, уж извиняйте... | |
|
| |
|
|
|
| Спасибо, большое! Буду пробовать.
Как сделаю, отпишусь. | |
|
| |
|
24 Кб. |
|
|
Set Rs1 = Db.OpenRecordset("SELECT * FROM Tabl WHERE Компания=""" & Rs.[Компания] & """
|
Lvm, ругается на отсутствие такого метода (см. вложение). Может как-то по другому надо обращаться к полю?
Я смысл ошибки понимаю, но т.к. не знаю VBA, опять прошу Вашей подсказки. | |
|
| |
|
|
|
| Убери везде " As Recordset"
Не спрашивайте, почему - не знаю... | |
|
| |