2004 г.
Проблемы отчетов в ACCESS
Сергей Гущенко, Издательский Дом "КОМИЗДАТ"
Важным элементом программ работы с базами данных является механизм "отчетов". Так
как структура баз может быть достаточно сложной, для реализации таких программ недостаточно
знать основные возможности "конструктора отчетов". Может потребоваться знание ряда слабо
документированных функций и технологических приемов
Выходные документы в СУБД проектируются с помощью механизма отчетов. В MS Access этот
механизм имеет массу возможностей, позволяющих создавать выходные документы без обращения
к программированию на встроенном языке VBA. Однако не все задачи можно решить таким путем.
Рассмотрим пример. Допустим, есть таблица "TELEFKOD" телефонных кодов с 528-ю записями,
начало которой имеет следующий вид:
NPP |
GOROD |
KOD_TEL |
OBLAST |
1 |
Авдеевка |
06236 |
Донецкая |
2 |
Акимовка |
06131 |
Запорожская |
3 |
Александрия |
05235 |
Кировоградская |
4 |
Александровка |
06269 |
Донецкая |
5 |
Александровка |
05269 |
Кировоградская |
6 |
Алушта |
06560 |
Крым |
Причем поле "NPP" имеет тип Счетчик (Длинное целое), особенность которого состоит в
следующем: при удалении записи в этом поле удаляется и значение, которое больше повторяться
не будет. В результате перечень порядковых номеров может не совпасть с перечнем значений
в этом поле.
В качестве выходного документа для этой таблицы подготовлен несложный отчет, вид которого
в Конструкторе представлен на рис. 1.
В этом отчете названия колонок (шапка таблицы) размещены в верхнем колонтитуле, в качестве
номера по порядку используется поле "NPP", в нижнем колонтитуле указывается номер страницы
и добавлено Примечание отчета с количеством выводимых записей и примером подписей. Модуль
класса пустой (то есть никаких программных кодов на языке VBA нет). Распечатывается такой
отчет без проблем. Но могут возникнуть претензии к размещению Примечания. По умолчанию
его свойство Не разрывать имеет значение "Да". (рис. 2).
Это означает: если всё Примечание не помещается на последней странице строк с данными,
то оно будет целиком перенесено на следующую. Но не всегда такое решение допустимо - особенно
если печатается финансовый документ. Поэтому можно поменять значение этого свойства на "Нет" -
тогда Примечание будет начинаться сразу после вывода последней записи. Это решает рассматриваемую
проблему, но не до конца. Может возникнуть ситуация, когда одна подпись (например, Директора)
окажется на одной странице (последней с данными), а другая перенесется на следующую,-
тоже плохо. Более приемлемым решением в этом случае - с небольшой высотой Примечания -
было бы его размещение полностью на отдельной странице, но так, чтобы к нему сверху автоматически
добавлялись несколько последних строк с данными. И вот тут без обращения к VBA уже не
обойтись. Вариантом решения этой задачи может оказаться следующий текст Модуля класса
с кодами обработки событий:
Option Compare Database
Option Explicit
Dim I1 As Integer, KolZap As Integer, KolZ As Integer
Dim dd As Database, zap As Recordset,
Private Sub Report_NoData (Cancel As Integer)
MsgBox (" А нету записей!")
Cancel = True
End Sub
Private Sub Report_Open (Cancel As Integer)
Set dd = CurrentDb
Set zap = dd. OpenRecordset (" TELEFKOD", dbOpenDynaset)
If Not zap. BOF Then
zap. MoveLast
KolZap = zap. RecordCount
End If
zap. Close
KolZ = 0
End Sub
Private Sub ВерхнийКолонтитул_Format
(Cancel As Integer, FormatCount As Integer)
Me![EndStr1]. Visible = False
I1 = 0
End Sub
Private Sub ОбластьДанных_Format
(Cancel As Integer, FormatCount As Integer)
I1 = I1 + 1
KolZ = Me. CurrentRecord
If I1 > 39 Then
If KolZap - KolZ < 3 Then
Me![EndStr1]. Visible = True
End If
End If
End Sub
Во-первых, здесь по ходу дела задействовано интересное свойство Report_NoData - обработка
ситуации, когда в таблице-источнике данных нет записей. Текст процедуры почти стандартен,
строка Cancel = True приводит к прекращению печати отчета.
Во-вторых, в Области данных установлен элемент управления Конец страницы (рис. 3),
действие которого контролируется в модуле класса.
При форматировании каждой новой страницы его "видимость" отключается (Me![EndStr1].
Visible = False). Включается же при форматировании Области данных - при наступлении определенного
условия, которое состоит из двух частей. Первая контролирует область листа, на котором
Примечание уже не может разместиться полностью. В данном отчете опытным путем определено,
что такой момент возникает после печати 39-й строки (If I1 > 39 Then). Целочисленной
переменной, в которой учитывается номер печатаемой строки, является I1. При форматировании
верхнего колонтитула она обнуляется, а при форматировании каждой новой строки данных ее
значение увеличивается на 1. Но этого мало - требуется еще определить, что выводятся последние
строки. Это делается через две другие переменные: KolZ, в которую заносится номер текущей
записи в печатаемой таблице (KolZ = Me. CurrentRecord) и KolZap, в которой хранится общее
количество записей, рассчитываемое при открытии отчета (KolZap = zap. RecordCount). В
конечном итоге, если возникает ситуация печати последних двух записей в зоне листа, где
уже не может полностью разместиться Примечание, то они печатаются вместе с ним на следующей
странице (рис. 4).
Но в этом примере есть и ошибка - итоговое поле для подсчета количества записей в Примечании
отчета насчитало 528 записей. А последний Номер по порядку значится как "529". Такая ситуация
возможна в случае, если в качестве номера по порядку выводится поле, имеющее тип Счетчик,
а в процессе заполнения таблицы были выполнены удаления записей (в примере была удалена
одна запись). Поэтому в качестве поля порядкового номера в отчете лучше использовать свободное
поле, не связанное с исходной таблицей. В качестве данных для такого поля надо установить
значение "=1" и указать вариант Для всего в параметре Сумма с накоплением (рис. 5 - вид
в Конструкторе и рис. 6 - при предварительном просмотре).
Может возникнуть необходимость нумерации не только в границах всего отчета, но и постранично.
Это также делается через свободное поле (рис. 7), но его заполнение выполняется в модуле
класса. Под него объявляется новая переменная, например: Dim NStrP As Integer. Она должна
обнуляться при форматировании верхнего колонтитула и заполняться при форматировании области
данных, например:
Private Sub ВерхнийКолонтитул_Format
(Cancel As Integer, FormatCount As Integer)
…..
NStrP = 0
End Sub
Private Sub ОбластьДанных_Format
(Cancel As Integer, FormatCount As Integer)
…..
NStrP = NStrP + 1
Me![NSTR1] = NStrP
End Sub
Последним вариантом учета строк рассмотрим нумерацию в группе. Access позволяет сортировать
и группировать данные прямо при выводе. Эти настройки выполняются в Конструкторе через
диалоговое окно Сортировка и группировка (вызывается по команде меню Вид) - рис. 8. Для
создания группировки по какому-либо полю этого окна, его (поле) надо выбрать в списке
(в примере - поле OBLAST) и указать "Да" в параметре Заголовок группы. Здесь же можно
установить сортировку для других полей без группировки по ним (в примере - по полю GOROD).
Вывод порядкового номера в группе производится в "свободном" поле, в параметре Данные
которого указано "=1", но Сумма с накоплением установлена Для группы. Фрагмент результата
показан на рис. 9.
Следующей проблемой может оказаться размещение в нижнем колонтитуле промежуточных сумм
по какому-либо полю. Такая ситуация часто возникает в финансовых документах - например,
при распечатке ведомости на зарплату. На первый взгляд, сделать это можно по технологии,
рассмотренной ранее: объявляется переменная, обнуляется при форматировании верхнего колонтитула
и заполняется при форматировании области данных. Но поскольку результат должен быть занесен
в "свободное" поле, размещенное в другом разделе (в Нижнем колонтитуле), сумма окажется
неправильной - ведь процесс форматирования некоторых разделов "отчетов" в Access может
повторяться!!! Это является особенностью технологии подготовки "отчетов" - нужно помнить
об этом при программировании. В частности, в рассматриваемом примере заполнение переменной
для промежуточной суммы (SumStr) можно выполнять не при форматировании области данных,
а при отработке свойства печати области данных (ОбластьДанных_Print), в которой можно
проконтролировать ситуацию - будет печататься лист с этими данными или нет. И именно в
зависимости от результатов контроля должно быть заполнено поле, расположенное в нижнем
колонтитуле. Допустим, оно имеет имя SumS, а переменная, в которой накапливается значение
на странице,- SumStr. Тогда текст кода подобной процедуры будет иметь следующий вид:
Private Sub ОбластьДанных_Print
(Cancel As Integer, PrintCount As Integer)
'PrintCount = 0 - страница не печатается,
' = 1 - будет печататься
SumStr = SumStr + NStrP
If PrintCount = 1 Then Me![SumS] = SumStr
End Sub
Подобная же ситуация может возникнуть при попытке использовать Подчиненный отчет, который
чаще всего вставляется в Примечание отчета или в Примечание группы. Если в нем требуется
указывать какое-то расчетное значение (например, тот же номер на странице), то оно может
оказаться неправильным, так как такой отчет будет форматироваться несколько раз (рис.
10).
Для решения проблемы команду заполнения поля № строки на странице Подчиненного отчета
надо переместить из обработки события Форматирования в обработку события Печать раздела
Область данных, например:
Private Sub ОбластьДанных_Print
(Cancel As Integer, PrintCount As Integer)
NStrP = NStrP + 1
Me![NSTR1] = NStrP
SumStr = SumStr + NStrP
If PrintCount = 1 Then Me![SumS] = SumStr
End Sub