Слайд 1Программирование в VFP: работа с БД
Тема Х. Лекция 7
Слайд 2Рабочие области и окружение данных
Рабочая область – некоторое абстрактное число,
идентифицирующее открытую таблицу.
Любая таблица (просмотр) может быть открыта в одной
или нескольких рабочих областях. При открытии каждой таблице задается псевдоним, который по умолчанию совпадает с именем файлов таблицы (двух одинаковых псевдонимов быть не должно).
Окружение данных – это набор конкретных таблиц, заданных на них порядков и временных связей, необходимых для работы конкретной компоненты FoxPro (форма, отчет, этикет)
Сессия данных (Data session) – копия рабочих областей.
Как правило, сессия данных используется для работы с буферизированными таблицами. Для каждой сессии требуется задавать ряд настроек FoxPro, определяющих режим работы с таблицами.
Слайд 3Выбор и открытие таблиц
Открытие таблицы в рабочей области с заданием
на ней нужного порядка
USE [ [in ][ORDER.TAG
порядка>][Alias <псевдоним>][Again]] && №=0 – первая свободная область, ПУ - текущая
Пример: USE students ORDER TAG name
Выбор и определение рабочей области.
SELECT <псевдоним БД>. псевдоним – это имя файла, если не менять
SELECT (…) – возвращает номер текущей рабочей области или области указанной
таблицы
Пример:
x=SELECT() &&Запомним текущую область
SELECT students &&Перейти на нужную таблицу
SELECT (X) &&Вернуться к исходной области (таблице) через макроподстановку
Изменение порядка просмотра записей
SET ORDER TO [TAG] <имя порядка> [Descending] | [Ascending=ПУ]
ORDER() &&Возвращает имя индекса для таблицы из текущей рабочей области
Пример: SET ORDER TO prim_index
Закрытие всех открытых таблиц и представлений данных:
CLOSE DATA ALL
Слайд 4Перемещения по записям таблицы
Перемещение по таблице
GO (TO) № записи TOP
BOTTOMN && Переход на №записи, в начло или конец
SKIP [
количество записей] && по умолчанию = 1 (на следующую запись)
Определение местонахождения (номера текущей записи)
RECNO([alias]) && ПУ – текущая рабочая область
BOF([alias]) &&это первая запись?
EOF([alias]) &&это место для добавления в конце новой записи?
Пример:
oldSel=SELECT() &&Запомним текущую область
oldOrder=ORDER() &&Запомним текущую область
If EOF() &&Если мы находимся в конце файла
GO BOTTOMN &&Перейти на последнюю запись
ENDIF
oldREC=RECNO() &&Запомним текущую область
….. &&Делаем нужные дела с другой и текущей таблицей
SELECT (X) &&Вернуться к исходной таблице
SET ORDER TO TAG (oldOrder) &&восстановить на ней нужный порядок
GOTO oldRec &&восстановить на ней нужный порядок
Аналогично, с проверкой EOF() и BOF(), организовываются перемещения по записям через
кнопки и c последующей подачей команды SKIP или SKIP-1
Слайд 5Удаление, изменение и вставка записей
Target = [DatabaseName!]TableName | Alias |
FileName | - целевая таблица.
ПУ – всегда в текущей рабочей
области
DELETE [Scope] [FOR lExpression1] [WHILE Expression2] [IN nWorkArea | cTableAlias] [NOOPTIMIZE] – отметить запись на удаление
DELETE [Target] FROM [FORCE] Table_List [[, Table_List ...] | [JOIN [ Table_List]]] [WHERE FilterCondition1 [AND | OR FilterCondition2 ...]] – отметить запись на удаление SQL
RECALL [Scope] [FOR lExpression1] [WHILE lExpression2] [NOOPTIMIZE] [IN nWorkArea | cTableAlias] – снять отметку записи на удаление
PACK [MEMO | DBF] [Tablename ] [IN nWorkarea | cTableAlias] - очистить таблицу (убрать записи, отмеченные на удаление и старые редакции мемо – полей)
ZAP [IN nWorkArea | cTableAlias]
INSERT – добавляет запись – теперь не применяется. Вместо нее – следующая или APPEND
INSERT (список полей) VALUE: (список значений) - SQL команда вставки записи
Слайд 6Поиск и фильтрация записей
LOCATE FOR [Scope] – поиск записи,
соответствующей
заданному выражению
SEEK (Expr [,Idx]) – поиск в индексной таблице
подходящего значения ключа
FOUND ( ) = .T., если запись найдена
CONTINUE – повторить поиск
SET NEAR ON|OFF (настройка):
On – остается на записи, после которой поиск не имеет смысла;
Off – выявляет все записи, начинающиеся на заданный символ.
SET FILTER TO [ExpL] – установка /отмена фильтра
ExpL – удобно заменить макроподстановкой на &str, где str – строка текста
Пусть даны 2 условия: поле1=маска поиска f1 и поле 2=макс.число X
Тогда для первого поля условие отбора запишется в виде like(f1,field1), а для
второго – field2str=IIF(EMPTY(f1),’’, like(f1,field1))
str1=IIF(EMPTY(X),’’, field2str=IIF(EMPTY(str1),’str’, IIF(EMTY(str);str1,str+’ and ’+ str1)
Слайд 7Анализ и обработка данных
COUNT TO (количество отобранных
записей)
SUM {список полей} to {}
CALCULATE…….
(ср. квадратичное, максимум, минимум,…)
Большинство команд и функций, возвращающие переменную
или массив, при их отсутствии создают их.
SCAN [] [FOR, WHILE]…[EXIT]…[LOOP]…ENDSCAN –
оператор цикла для перемещения по записям таблицы.
SCATTER [//FIELDS FieldNameList | FIELDS [LIKE Skeleton ] [EXCEPT Skeleton] | FIELDS EXCEPT Skeleton//] [MEMO] [BLANK] //TO ArrayName | TO ArrayName | MEMVAR | NAME ObjectName [ADDITIVE]// - переносит данные из записи в переменные. [BLANK] - создает пустые переменные; [ADDITIVE] – обновляет существующие свойства; [MEMO] – позволяет копировать мемо-поля. Поля типа General в свойства созданного объекта не копируются
Слайд 8Виды буферизации таблиц
Буферизация настраивается в формах и их наборах и
окружении
DataEnvironment. Всего существует следующие варианты настройки, или режимы, буферизации
Отключенная буферизация
Пессимистическая
буферизация записи. Запись блокируется сразу по началу ее редактирования. Обновление записи – при переходе на новую запись
Оптимистическая буферизация записи. Запись блокируется только при перемещении указателя на новую запись.
Пессимистическая буферизация таблицы - аналогична буферизации записи.
Оптимистическая буферизация таблицы - аналогична буферизации записи.
Для просмотров View (локальных и удаленных) возможны только 3 и 5 режимы буферизации.
Слайд 9Буферизация в формах и в окружении данных
В формах и их
наборах может быть задано 3 различных метода буферизации, задаваемых свойством
Object.Buffermode.
0. Выключенная буферизация. Блокировка начинается при начале редактирования поля. Поля записываются при перемещении указателя записи.
1. Пессимистическая буферизация. То же, что и режим 0, но по Tablerevert() можно отменить изменения, внесенные в текущую запись.
2. Оптимистическая буферизация. Запись блокируется только при записи на диск по TableUpdate().
В окружении буферизация задается свойством DataEnveronment.Cursor.BufferMode.Override индивидуально для каждого курсора.
При этом может быть реализовано 6 различных методов буферизации.
0. Буферизация отключена.
1. Буферизация, режим которой определяется формой (ПУ).
2. Пессимистическая буферизация записи
3. Оптимистическая буферизация записи.
4. Пессимистическая буферизация таблицы.
5. Оптимистическая буферизация таблицы.
Для просмотров View (локальных и удаленных) возможны только 3 и 5 режимы буферизации.
Слайд 12Функции фиксации/отмены транзакций
Begin Transaction программно изменить
End
Transaction значения в таблице
Roll
back – отменить сделанные изменения
Requery – применяется к буферизации View
Слайд 13Буферизация таблиц: комментарии
При реверте (откате) поля таблицы или курсора принимают
значения, которые они имели до начала внесения изменений. Для таблиц
все просто: при реверте в них устанавливаются значения из исходных таблиц (все буферизованные таблицы – это копии соответствующих исходных таблиц). Для курсоров же при откате восстанавливается только текущая запись и только то ее состояние, в котором она находилась до передачи внесенных в запись изменений в порождающие ее таблицы и другие курсоры. Иными словами, в курсоре отмнить измененные значения полей можно только до тех пор, пока указатель записи не переместится на другую строку.
При перезаписи (команда TABLEUPDATE) внесенные в буферизованную таблицу изменения переносятся в исходную таблицу. В зависимости от команды и режима буферизации обновляются данные только из текущей записи или всех записей таблицы, включая удаленые и добавленные записи. Для View необходимость в подаче данной команды почти не возникает, так как обновление данных имеет место всякий раз после внесения изменений в поле, помеченное в конструкторе просмотров на соответствующей закладке как ключевое. Поэтому в просмотре TABLEUPDATE подается только при программном изменении значения поля, если не предполагается переводить указатель записи в другое место. Ее необходимо также подавать тогда, когда изменения вносится только в мемо-поля, так как его их нельзя сделать ключевыми в конструкторе просмотров (и иным образом) для выполнения автоматической перезаписи.
Другая важная особенность выполнения перезаписи для просмотров (выполнеямой как командой TABLEUPDATE, так и автоматически при переходе на другую запись) заключается в том, что перезапись данных производится только в порождающих просмотр таблицах и других просмотрах, открытых в текущей сессии. Поэтому, чтобы сохранить внесеные изменения в самой БД (т.е. в исходных таблицах), необходимо дополнительно подать команду TABLEUPDATE для всех входящих в просмотр таблиц, в которые требуется внести сделанные в просмотре изменения.
Слайд 14Особенности работы с View
Команда восстановления REQUERY восстанавливает данные в запросах.
При этом важно отметить, что для потсроения таблиц используются не
данные буферизованных таблиц, на которых строится запрос, а данные непосредственно из исходных таблиц БД. Исключение – значения полей таблиц, входящих в запрос как параметры. Кроме того, если запрос построен на основе других запросов, то при восстановлении он использует данные из них. Поэтому в таких случаях команда REQUERY должна подаваться последовательно для всех просмотров, входящих в даннный, в соответствии с их иерархией. Если хранимые в запросе и его компонентах (порождающих запрос таблицах и других запросах) данные не согласованы, при восттановлении может также произойти ошибка обновления, вызванная конликтом данных. Поэтому перед подачей команды REQUERY необходимо сначала завершить все внесенные в запрос и его компоненты изменения, и лишь затем подавать саму команду. Важно помнить, что без данной команды никакие сделанные в его компонентах изменения в запросе не отразятся! К сожалению, точно и до конца все нюансы процесса выполнения команды восстановления REQUERY выяснить не удалось. В частности, вроде бы было установлено, что вычисляемые поля в просмотрах после восстановления не меняются, но это может оказаться не совсем так.
При перезаписи данных из просмотров (по команде TABLEUPDATE или автоматически) не исключена возможность возникновения конфликта перезаписи. Такой конфликт возникает в случае, если данные в порождающих просмотр таблицах изменены не за счет перезаписи из данного просмотра, а каким – либо иным образом (например, программно, другими средствами интерфейса, через другой просмотр, который может входить в данный просмотр, или другим пользователем). Подача в этом случае TABLEUPDATE с принудительной перезаписью приводит к отмечанию соответствующей строки просмотра на удаление, но перезаписи данных в конечном итоге не происходит. К ошибкам при перезаписи может приводить и случай, когда в просмотре создается новая строка, которой в порождающих просмотр таблицах соответствуют пустые записи, т.е. записи, в которой значения всех полей пусты. Другая причина возникновения конфликта – когда в просмотр вносятся данные, для которых отсутствует соответствующая запись в порождаюшей просмотр таблице. А для этого, в свою очередь, просмотр должен быть создан с использованием внешних соединений xx OUTER JOIN.
Слайд 15Работа с триггерами
В таблице знаком плюс отмечены методы, вызываемые при
совершении того или иного события. Последовательность вызова данных методов определяется
последовательностью столбцов данной таблицы.
Слайд 16Пример приложения: обозначения
Пример программного модуля для задания рабочего каталога приложения
*Main
Program "BookCeaper".Version V00.001124 (базовый номер и дата модификации)
*инициализация работы программы:
нахождение ее места на диске и задание нужных путей:
*cPathR: device:\...\cRootDir\ - к базовому каталогу (из корневого)
*cPathB: device:\...\cRootDir\cBaseDir\ - к рабочему каталогу и компонентам приложения из базового каталога
*cPathW: device:\...\cRootDir\cBaseDir\DAT\cPathW\ - к вариабельным данным
*где
*cRootDir - "корневой" или главный каталог, где лежат общие для всех приложений VFP каталоги: библиотек, процедур, графики и т.п.
* в текущем соглашении это каталоги: @Grf (графика), @Lib (библиотеки), @Prg (программы, универсальные процедуры и функции)
* в конечном продукте *.EXE - не нужен, так как все нужные компоненты из этого каталога уже д.б. включены в приложение
* при работе с *.(APP&FXP&PRG) в процессе разработки нужен, т.к. в нем м.б. нужные для проекта компоненты
*cBaseDir - "базовый" каталог приложения, где лежит: сначала только программа Setup, а затем - основной *.exe/app/prg - файл
* и прочие каталоги конкретного приложения. В частности, для BC это каталоги:
* DAT (данные), FRM (формы), PRG (программы), Grf (графика), Lib(библиотеки),RpL (Reports &Labels), Oth (Other Files: *.h, *.txt etc) - прочие файлы
*cWorkDir - "рабочий" каталог, находящийся внутри каталога DAT базового каталога с вариабельными данными по кокретному набору или группе
* для BC группа или набор - это данные, относящиеся к конкретно выбранному в процессе работы приложения предприятию
* в программе значение cWorkDir хранится в VarName "cWrkDir"
If Type("oBc")="O" &&объект oBc уже есть и значит программа - уже запущена!
MessageBox("This File Already Running! Программа уже запущена")
Return
EndIf
If Type("gnUsed")="U" &&This module not running before (IF000)
#INCLUDE MessBox.H &&в принципе, не нужно
Clear All - не может быть применена, т.к. класс - не уничтожается!!!
Close All
Clear Program
*Release All
Clear
ON KEY
Слайд 17Пример приложения: задание путей
Public cPathR,cPathB,cPathW,cWrkDir &&путь в каталоги: "корневой", базовый,
рабочий и его название
cWrkDir="New1" &&Name of Special Data Directory: название
рабочего каталога с конкретными данными
Local x,y,cSepatator1,cSepatator2
*FIND CURRENT PATH ====================================================================================
cPathB=sys(16,1) &&название исполняемой программы с полным путем к ней на уровне вложеннности программ=1.
If Empty(cPathB) &&если нет перезапуска BC, вложений нет и cPathB=''.
cPathB=sys(16,0) &&название исполняемой программы на уровне вложеннности программ=0 (первый пуск)
EndIf
x=Rat("\",cPathB) &&первое вхождение в текст пути с конца разделителя каталога
If x=0 &&разделителя каталогов не найдено
x=Rat(":",cPathB) &&найти разделитель устройства
cSepatator1=":"
MESSAGEBOX("Приложение не должно лежать в корневом каталоге!")
RETURN
Else
cSepatator1="\"
EndIf
cPathB=Left(cPathB,x-1) &&путь к базовому каталогу, не включая конечного разделителя ":" или "\"
x=Rat("\",cPathB) &&следующее вхождение в текст пути разделителя каталогов
If x=0 &&разделителя каталогов не найдено
cSepatator2=":"
x=Rat(":",cPathB) &&найти разделитель устройства
Else
cSepatator2="\"
EndIf
cPathR=Left(cPathB,x-1) &&каталог верхнего уровня без конечного разделителя ":" или "\"
Wait Window "Executable Base directory path= "+cPathB+", Root Dir/ path="+cPathR+", Main program ="+sys(16,0)
*Set path to вышележащий, текущий католог, а также каталоги в вышележащем каталоге (нач. с @)
*в которых хранятся данные и модули общего назначения
x=cPathR &&путь к "базе" без конечного разделителя
y=cPathB &&путь к "крыше" без конечного разделителя
cPathR=cPathR+cSepatator2 &&путь к "базе" c конечным разделителем
cPathB=cPathB+cSepatator1 &&путь к "крыше" с конечным разделителем
cPathW=cPathB+"DAT\«
*УСТАНОВКА БАЗОВЫХ ПУТЕЙ ====================================================================================
*в дистрибутиве - должно быть много меньше, только пути к данным
set path to &x,&y,;
&cPathB.dat,&cPathB.frm,&cPathB.Grf,&cPathB.Lib,&cPathB.Oth,&cPathB.prg,&cPathB.RpL,;
&cPathR.@Grf,&cPathR.@Lib,&cPathR.@Prg
clear
Слайд 18clear
*ПОДКЛЮЧЕНИЕ НУЖНЫХ БИБЛИОТЕК И ПРОЦЕДУР ====================================================================================
Set Procedure to
ProcUni &&универсальные процедуры общего назначения
Set ClassLib to BCLib
Set
ClassLib to BaseClass Additive
*#Define cProgName "BC\" &&название головного модуля программы "BC" - пока не используется
*СОЗДАНИЕ УПРАВЛЯЮЩЕГО ОБЪЕКТА & ИНИЦИАЛИЗАЦИЯ ПРИЛОЖЕНИЯ
=====================================================================================
*APP_MNG, APP_TLB &&Main ,Base Special Procedures, Base Toolbar
*#Define APP_MNG "oBC" &&название управляющего приложением объекта - для ссылки на него из классов
*#Define APP_TLB "otlbBase" &&название управляющего приложением объекта - для ссылки на него из классов
Public gnUsed,oAccess &&текущее количество окон, режим доступа к данным
Do Access.prg
gnUsed=0 &&количество открытых окон
on key label ALT+R do bc
EndIf &&***IF000
Public oBp, otlb
oTlb=CreateObject("tlbTable")
oTlb.show()
oTlb.Dock(0) &&ToolBar.Dock(nLocation [, X, Y]): -1=Undock, 0..3=top,left,right,bottom
oTlb.ControlBox=.f. &&No Handle Close!!!
oBp=CreateObject("cstBC") &&Base Procedures: Universal&Special Base Methods&Properties of The Application=Procedures,Functions and Public Variables
*Sintax: ObjectName=CreateObject("cClassName"[,{eParameter_i_For_Init,}])
gnUsed=0 &&количество открытых окон
Пример приложения: завершение старта
Слайд 19Требования к расчетному заданию
Создать приложение, запускаемое из окна VFP, включающее
меню и обеспечивающее реализацию главной функции приложения, включая возможность ввода,
удаления и редактирования соответствующих основному объекту приложения записей, их отбор и просмотр отчета
Создать для приложения главную форму, с помощью которой можно бы было просматривать списки, содержащие основные объекты или операции в соответствующей предметной области.
Предусмотреть возможность отбирать в списках записи, удовлетворяющие некоторому набору условий, свойств или требований, количество которых должно быть не менее двух. Выбор необходимых для отбора условий (одно или несколько из имеющихся) определяется пользователем.
На форме должна присутствовать информация об общем (отобранном) числе содержащихся в списке записей.
Приложение должно иметь панель инструментов, хотя бы одна кнопка которой синхронизирована с пунктом основного меню и кнопкой главной формы
Остальное содержание задания (необходимость включения дополнительных функций и форм) определяется исполнителем самостоятельно