2 Курс Информатика VBA(ЗО) / Книги / В.Д.Хорев - Самоучитель программирования на VBA в Microsoft Office
.pdfСравнительный анализ прайс-листов 123
не наступит. Исходный текст макроса в этом случае можно поместить в обработчик события рабочей книги Open (процедура обработки этого события должна называться Workbook_Open) — тогда макрос будет выполняться при открытии рабочей книги. Чтобы создать обработчик этого события, необходимо выбрать двойным щелчком в окне проекта Visual Basic элемент ThisWorkbook, а в списках окна кода — элементы Workbook и Open.
см. также в приложении раздел “Как открыть файл (оператор Open)”.
Словом, здесь возможны различные варианты, для этого же примера был выбран простейший, который является в данном случае самым универсальным.
Итак, создав тем или иным образом заготовку процедуры, приступим к разработке исходного текста макроса, который должен формировать сводный прайс-лист на основе прайсов “А” и “Б”.
Создание процедуры Сводка()
Вначале необходимо объявить объектные переменные, которые будут содержать в себе прайслисты “А” и “Б”, а также сводный прайс-лист. Назовем их, соответственно, PriceA, PriceB и PriceSvod. Объявить их необходимо как Worksheet, поскольку речь идет о рабочих листах.
Целочисленные переменные A, B и SvodCount будут использованы в качестве счетчиковуказателей строк для соответствующих листов — их также необходимо предварительно объявить как Integer.
Первый шаг состоит в том, чтобы создать переменные-объекты, заключающие в себе рабочие листы Excel, которые и будут обрабатываться. Поскольку предполагается, что рабочий лист сводного прайса является текущим, можно обратиться к нему, как к объекту ActiveSheet:
Set PriceSvod = ActiveSheet
Будем считать, что подлежащие анализу прайсы “А” и “Б” размещены на рабочих листах “Прайс-А” и “Прайс-Б”, которые находятся в книгах PriceA.xls и PriceB.xls, которые, в свою очередь, находятся в корневом каталоге диска C:. В этом случае можно создать переменные PriceA и PriceB следующим образом:
Set PriceA = Workbooks.Open("C:\PriceA.xls").Worksheets("Прайс-А")
Set PriceB = Workbooks.Open("C:\PriceB.xls").Worksheets("Прайс-Б")
Вызовом метода Open семейства рабочих книг (Workbooks) в это семейство добавляется книга, содержащаяся в указанном файле. И уже в семействе рабочих листов открытой книги (Worksheets) оператор Set найдет необходимый лист и присвоит его в качестве значения соответствующей переменной.
Счетчик строк сводного прайса необходимо установить на строку 2, чтобы пропустить заголовок:
SvodCount = 2
Теперь все готово для просмотра прайс-листа “А” и сопоставления его с прайс-листом “Б”. Чтобы просмотреть все строки диапазона “Наименование” в листе “А”, используем цикл For:
For A = 1 To PriceA.Range("Наименование").Rows.Count
...
Next A
Свойство Count семейства строк диапазона (Rows) задает верхнюю границу просмотра, поскольку в нем содержится количество строк в диапазоне (это число может меняться при вставке и удалении строк, и потому заранее неизвестно).
124 Глава 4. Практикум программирования на VBA для Excel и Word
Первое, что следует сделать при просмотре очередной строки прайса “А”, это скопировать ее в соответствующую позицию сводного прайса, поскольку в одном из анализируемых прайс-листов эта позиция уже встретилась:
PriceSvod.Cells(SvodCount, 1) = _
PriceA.Range("Наименование").Cells(A, 1)
PriceSvod.Cells(SvodCount, 2) = PriceA.Range("Цена").Cells(A, 1)
Копирование осуществляется в 1 и 2 столбцы SvodCount-й строки, то есть в столбцы, соответствующие наименованию товара и его цене по прайсу “А”.
Тут же, не покидая SvodCount-й строки сводного прайс-листа, необходимо просмотреть прайс “Б”: а нет ли там такой же позиции?
Если наименование одной из позиций прайса “Б” совпало с текущей позицией прайса “А”, то соответствующее ей значение цены необходимо добавить в сводный прайс-лист, теперь уже в 3-й столбец, который соответствует ценам прайса “Б”:
For B = 1 To PriceB.Range("Наименование").Rows.Count
If PriceB.Range("Наименование").Cells(B, 1) = _
PriceA.Range("Наименование").Cells(A, 1) _
Then
PriceSvod.Cells(SvodCount, 3) = _
PriceB.Range("Цена").Cells(B, 1)
PriceB.Range("Наименование").Cells(B, 1).Value = ""
End If
Next B
Обратите внимание на тот факт, что во втором операторе присваивания после Then совершается еще одно действие, необходимость в котором станет понятной в дальнейшем. Чтобы исключить из рассмотрения ту позицию прайса “Б”, цена которой была только что зарегистрирована в сводном прайс-листе, необходимо записать в столбец “Наименование” пустую строку. Теперь эта позиция повторно уже не “найдется”.
Если бы сравнительному анализу подвергались не два, а три или более прайс-листов, то в этом месте необходимо было бы выполнить аналогичные действия для прайсов “В”, “Г” и т.д.
Наконец, нужно передвинуть счетчик-указатель строки сводного листа на следующую строку:
SvodCount = SvodCount + 1
После того, как переменная A “дойдет” до конца диапазона “Наименование” прайс-листа “А”, на рабочем листе сводного прайса будет сформирован список всех позиций “А”, причем для тех из них, которые обладают “двойником” в листе “Б”, в 3-м столбце будет указана цена прайса “Б”. Но задача еще не решена! В прайсе “Б” могут найтись позиции, которых не было в прайсе “А”. Чтобы обнаружить таковые, нужно просмотреть теперь весь диапазон “Наименование” в прайсе “Б”.
В этом случае пригодится проявленная ранее предусмотрительность: все уже помещенные в сводку позиции из прайса “Б” исключены (для них в столбец “Наименование” была записана пустая строка), поэтому нет необходимости в дополнительных проверках — просто можно просмотреть лист “Б” точно так же, как это было сделано с листом “А”. Единственная особенность, которую необходимо здесь учесть, состоит в том, что вначале необходимо проверить, существуют ли очередная позиция. Иными словами, не “вычеркнута“ ли она из рассмотрения по той причине, что она встретилась при просмотре листа “А” и уже помещена в сводный прайс-лист. Таким образом, конструкцию цикла For необходимо дополнить наложением условия “ячейка в столбце наименования не пуста”:
For B = 1 To PriceB.Range("Наименование").Rows.Count
If PriceB.Range("Наименование").Cells(B, 1) <> "" Then
Сравнительный анализ прайс-листов 125
...
End If
Next B
Точно таким же образом в этом цикле в сводный прайс будет занесена любая встретившаяся непустая позиция:
PriceSvod.Cells(SvodCount, 1) = _
PriceB.Range("Наименование").Cells(B, 1)
PriceSvod.Cells(SvodCount, 3) = PriceB.Range("Цена").Cells(B, 1)
Затем, по логике копирования, необходимо проверить, нет ли такой позиции в прайсе “А”:
For A = 1 To PriceA.Range("Наименование").Rows.Count
If PriceA.Range("Наименование").Cells(A, 1) = _
PriceB.Range("Наименование").Cells(B, 1) _
Then
PriceSvod.Cells(SvodCount, 2) = _
PriceA.Range("Цена").Cells(A, 1)
End If
Next A
Однако в этом, конечно же, нет никакой нужды — все позиции прайса “А” уже просмотрены и включены в сводный список. Впрочем, если бы анализировали три или более прайс-листов, то проверку остальных, не просмотренных еще списков, в этом месте следовало бы выполнить.
Завершаться каждый проход этого цикла должен очередным шагом счетчика-указателя строк в сводном прайс-листе:
SvodCount = SvodCount + 1
В основном, дело сделано. Для того чтобы улучшить визуальное восприятие сводного прайслиста, можно выделить жирным шрифтом те значения цены, которые меньше соответствующего значения в другом прайс-листе. Для этого нужно во всех местах, где в ячейки записываются зна-
чения цены, выполнить проверку и изменить свойство Bold объекта Font, принадлежащего соответствующей ячейке. Например, это можно было бы сделать следующим образом:
If PriceSvod.Cells(SvodCount, 2) < PriceSvod.Cells(SvodCount, 3) Then
PriceSvod.Cells(SvodCount, 2).Font.Bold = True
Else
PriceSvod.Cells(SvodCount, 3).Font.Bold = True
End If
Автоматическое закрытие файла с отменой сделанных в нем изменений
Наконец, необходимо закрыть использованные рабочие листы и сделать текущим сводный прайс-лист. Проблема заключается в том, что такой объект, как рабочий лист закрыть нельзя. Закрыть можно рабочую книгу, где он содержится. Ссылка на “свою” рабочую книгу у каждого лис-
та Excel доступна через свойство Parent:
PriceA.Parent.Close SaveChanges:=False
PriceB.Parent.Close SaveChanges:=False
Обратите внимание на логический параметр SaveChanges — задав ему значение False, тем самым можно отменить сохранение изменений в файле рабочей книги. Благодаря этому запись пустых строк в столбец “Наименование” не будет иметь последствий.
126 Глава 4. Практикум программирования на VBA для Excel и Word
см. также в приложении раздел “Закрытие файлов (операторы Reset, Close)”.
Кроме этого, следует присвоить объектным переменным значение Nothing, чтобы освободить занятую ими память:
Set PriceA = Nothing
Set PriceB = Nothing
см. также в гл. 3 раздел “Освобождение системной памяти компьютера”.
И вот, макрос готов. Полный его исходный текст представлен в листинге 4.4.
ЛИСТИНГ 4.4
Sub Сводка()
Dim PriceA As Worksheet
Dim PriceB As Worksheet Dim PriceSvod As Worksheet
Dim A, B, SvodCount As Integer
Set PriceSvod = ActiveSheet
Set PriceA = Workbooks.Open("C:\PriceA.xls").Worksheets("Прайс-А")
Set PriceB = Workbooks.Open("C:\PriceB.xls").Worksheets("Прайс-Б")
SvodCount = 2
For A = 1 To PriceA.Range("Наименование").Rows.Count
PriceSvod.Cells(SvodCount, 1) = _
PriceA.Range("Наименование").Cells(A, 1)
PriceSvod.Cells(SvodCount, 2) = _
PriceA.Range("Цена").Cells(A, 1)
For B = 1 To PriceB.Range("Наименование").Rows.Count
If PriceB.Range("Наименование").Cells(B, 1) = _
PriceA.Range("Наименование").Cells(A, 1) _
Then
PriceSvod.Cells(SvodCount, 3) = _
PriceB.Range("Цена").Cells(B, 1)
PriceB.Range("Наименование").Cells(B, 1).Value = ""
End If
Next B
If PriceSvod.Cells(SvodCount, 2) < PriceSvod.Cells(SvodCount, 3) Then
PriceSvod.Cells(SvodCount, 2).Font.Bold = True
Else
PriceSvod.Cells(SvodCount, 3).Font.Bold = True
End If
SvodCount = SvodCount + 1
Next A
For B = 1 To PriceB.Range("Наименование").Rows.Count
Сравнительный анализ прайс-листов 127
If PriceB.Range("Наименование").Cells(B, 1) <> "" Then
PriceSvod.Cells(SvodCount, 1) = _
PriceB.Range("Наименование").Cells(B, 1)
PriceSvod.Cells(SvodCount, 3) = _
PriceB.Range("Цена").Cells(B, 1)
For A = 1 To _
PriceA.Range("Наименование").Rows.Count
If PriceA.Range("Наименование").Cells(A, 1) = _
PriceB.Range("Наименование").Cells(B, 1) _
Then
PriceSvod.Cells(SvodCount, 2) = _
PriceA.Range("Цена").Cells(A, 1)
End If
Next A
If PriceSvod.Cells(SvodCount, 2) < _
PriceSvod.Cells(SvodCount, 3) _
Then
PriceSvod.Cells(SvodCount, 2).Font.Bold = True
Else
PriceSvod.Cells(SvodCount, 3).Font.Bold = True
End If
SvodCount = SvodCount + 1
End If
Next B
PriceA.Parent.Close SaveChanges:=False
PriceB.Parent.Close SaveChanges:=False
Set PriceA = Nothing
Set PriceB = Nothing
PriceSvod.Activate
End Sub
Чтобы проверить макрос в действии, необходимо открыть сводный прайс-лист и выполнить макрос Сводка при помощи команды Сервис | Макрос | Макросы. Результат выполнения макроса изображен на рис. 4.8.
128 Глава 4. Практикум программирования на VBA для Excel и Word
Рис. 4.8. Сводный прайс-лист автоматически сформирован на основе данных прайс-листов “А“ и “Б“
Особенности исходных данных из сравниваемых прайс листов
Как уже отмечалось выше, в оформлении реально обращающихся прайс-листов наблюдается поистине великое разнообразие. Вполне возможен случай, когда наименование товара в одном из подлежащих анализу прайс-листов устроено совершенно иным образом. Например, в изображенном на рис. 4.9 прайсе техническое наименование картриджа включено в состав строки общего наименования.
Вообще говоря, для каждой подобной особенности в исходных данных потребуется специальное решение. В данном случае, например, можно воспользоваться тем фактом, что техническое наименование находится повсюду в одной и той же позиции строки общего наименования. Поэтому, при сравнении строковых значений можно применить функцию Mid, извлекая из сравниваемой строки 7 символов начиная с 16-го — это и будет в данном случае сопоставляемое наименование картриджа с наименованиями из других прайсов:
If Mid(PriceB.Range("Наименование").Cells(B, 1), 16, 7)= _
PriceA.Range("Наименование").Cells(A, 1) _
...
Сравнительный анализ прайс-листов 129
Рис. 4.9. Вариант прайс-листа, в котором технические подробности о товаре находятся в одной ячейке с его наименованием
Глава 5
Access: создание офисной базы данных
В этой главе будет рассмотрена работа с самым профессиональным приложением MS Office — системе управления базами данных (СУБД) MS Access. Конечно, эта программа предназначена для работы с базами данных не более чем офисного масштаба, но все же MS Access менее всех других приложений Office пригоден для непрофессиональных пользователей. Сказанное, однако, не означает, что только профессиональным программистам принадлежит монополия на работу в среде Access. Достаточно опытный пользователь, как вскоре можно будет убедиться, в состоянии многое сделать при помощи тех средств MS Access, которые не требуют знания теории баз данных и наличия серьезных навыков программирования.
Подобно рабочим книгам Excel, база данных Access позволяет работать с табличными данными, систематизированными каким-нибудь образом. Однако есть два основных отличия. Так легко, наглядно и просто, как это выглядит на рабочем листе Excel, работать с таблицами Access не удастся — здесь все сложнее по определенным првилам. Второе отличие заключается в “мощности” рабочих инструментов — средства Access обладают несопоставимо большими функциональными возможностями и позволяют решать абсолютно любые задачи обработки данных в масштабах офиса, не стесняя пользователя никакими ограничениями. Этих данных может быть сколь угодно много, и структура их может быть сколь угодно сложна.
Создание базы данных MS Access
Вначале создадим небольшую и несложную базу данных, а затем займемся автоматизацией работы с этой базой данных при помощи VBA.
Первый, самый простой шаг, состоит в создании пустой базы данных, с которой затем и предполагается дальше работать. После запуска MS Access на экране появится диалоговое окно, позволяющее выполнить одно из следующих действий:
•открыть один из существующих файлов с базой данных — переключатель Открыть базу данных;
•создать базу данных с помощью мастера или на основе готового проекта — переключатель
Мастера, страницы и проекты баз данных;
•создать пустую базу данных “без изысков” — переключатель Новая база данных.
Для нашего примера нужна пустая база данных. Для этого установите переключатель Новая база данных и щелкните на кнопке ÎÊ.
В открывшемся диалоговом окне задайте имя файла новой базы данных, — например, пусть он называется MyOffice.mdb, и щелкните на кнопке ÎÊ.
Первый шаг на пути освоения MS Access сделан. Теперь можно приступать к упражнениям.
Создание таблиц
Основа любой базы данных — это таблицы с данными. В состав базы можно затем включить отчеты и запросы, экранные формы и программные модули, но без таблиц с данными никакой базы не получится.
132 Глава 5. Access: создание офисной базы данных
ПРИМЕЧАНИЕ
Программа MS Access из пакета Office 2000 в больше отличается от своих предшественников, чем Word или Excel. Также изменения коснулись основ работы с Visual Basic для приложений — теперь в Access есть явный доступ к редактору Visual Basic. Формат базы данных Access претерпел серьезные изменения — вплоть до полной несовместимости с предыдущими версиями. Наконец, существенно изменился сам вид окна базы данных.
В левой части окна базы данных расположена панель инструментов с кнопками, переключающими вкладки окна. Обратимся к вкладке Таблицы — именно с создания таблиц начнем работу с базой данных (рис. 5.1).
Рис. 5.1. Окно базы данных
Вспомогательные таблицы−справочники
Создааемая здесь простейшая база данных будет состоять из двух основных таблиц. В первой, назовем ее “Клиенты”, будут накапливаться сведения о клиентах фирмы. Вторая таблица будет содержать данные о приходных и расходных операциях, связанных с этими клиентами. Но прежде, чем приступить к созданию сравнительно сложных основных таблиц, необходимо подготовить несколько простых таблиц-справочников. Эти справочники будут использованы затем при создании основных таблиц.
Создание справочника клиентов фирмы
Например, будем подразделять клиентов на несколько категорий и для каждой записи в таблице “Клиенты” укажем одну из категорий. Допустим, среди клиентов надо различать “реализаторов”, “постоянных покупателей” и “случайных клиентов” — для каждого предусмотрим отличающиеся условия (например, разные величины скидок). Чтобы можно было впоследствии без труда дополнять и модифицировать набор категорий в соответствии с текущими нуждами офиса, вынесем этот набор в отдельную таблицу-справочник. С этого и начнем.
Как создать таблицу в режиме конструктора
1.Щелкните на кнопке Таблицы в панели инструментов окна базы данных.
2.Выберите двойным щелчком в окне базы данных значок Создание таблицы в режиме Кон-
структора. В результате откроется окно конструктора таблиц.
3.В первой ячейке столбца Имя поля введите строку “КодТипа”, а в соответствующей (то есть первой) ячейке столбца Тип данных разверните список и выберите Счетчик.
4.Командой Правка | Ключевое поле сделайте это поле ключевым (аналогичного результата
можно достичь, щелкнув правой кнопкой мыши на первой строке и выбрав команду Ключевое поле в контекстном меню).