
Ноябрь 2004
Сделано в России
Аркадий
Старцев.
Импорт данных из DBF файла в Oracle Developer.
Источник:
http://dba.ru/, март 2003 года,
http://dba.ru/docs/dev6_dbf.html
Введение
В этом документе приведен пример работы со сторонним
источником данных через ODBC. Приложение Oracle Forms
использует Oracle Open Client Adapter для взаимодействия с
источником данных ODBC и, за некоторыми исключениями, ведет
себя так же, как при работе с базой данных Oracle.
Установка дополнительных компонентов. Сначала
необходимо установить дополнительные компоненты
Developer:
NTERSOLV DateDirect Drivers
Oracle Open Client Adapter for ODBC (OCA)
 |
|
Запустите установку
Developer. Выберите тип установки
Custom. В окне Software Asset
Managet отметьте соответствующие компоненты, если
они не установлены (см. рис.1). Нажмите кнопку
Установить. |
Рис.1. Выбор
дополнительных компонентов.
Добавление имени источника данных (DSN).
Затем необходимо создать имя источника данных (DSN). В W2K
это делается так:
- Мой компьютер -> Панель управления ->
Администрирование -> Источники данных
(ODBC).
- Перейдите на закладку Пользовательский DSN и
нажмите кнопку Добавить.
- В появившемся списке драйверов выберите
INTERSOLV
OEM 2.12 32-BIT dBASE (*.dbf) и нажмите кнопку
Готово.
- В окне
ODBC dBASE Driver Setup заполните
поля, как показано на рис.2
 Рис.2. Настройка
ODBC драйвера.
Data Source Name -
произвольное имя, которое вы будете использовать в
программе.
Database Directory - директория,
в которой лежит DBF файл.
Create Type - тип
DBF файла.
Нажмите кнопку OK. В большинстве
случаев этого достаточно. Но вы можете посмотреть
дополнительные параметры (кнопка Advanced).
Тестирование Oracle ODBC.
Теперь можно проверить, как работает то, что мы сделали.
Стартуйте программу тестирования Oracle ODBC:
- Программы ->
Oracle Developer 6.0 Direct
Drivers -> Oracle ODBC Test.
- В появившемся окне нажмите кнопку Connect.
Перейдите на закладку Источники данных компьютера.
Выделите имя
imp_sal и нажмите кнопку
OK.
- В верхнем окне введите команду:
SELECT * FROM SALARY SALARY - это имя
DBF файла. Нажмите кнопку Exetcute. Если в нижнем
окне появится результат выборки, как на рис.3, то все
нормально.
 Рис. 3. Тестирование
Oracle ODBC.
Постановка задачи.
Нам необходимо взять информацию о зарплате из файла
SALARY.DBF, проверить ее на ошибки и положить в
таблицу SALARY базы данных
ORACLE.
Структура входного файла SALARY.DBF:
|
Name |
Type |
Width |
Decimal |
Notes |
| DEP |
Numeric |
3 |
0 |
Код подразделения |
| TBN |
Numeric |
4 |
0 |
Табельный номер |
| NAME |
Character |
35 |
|
Фамилия, имя, отчество |
| SUM |
Numeric |
14 |
2 |
Сумма |
Структура выходной таблица SALARY и связанной
с ней таблицей стуруктурных подразделений
DEPT:
|
Columns of Salary |
|
Columns of DEPT |
|
Name |
Type |
Restrictions |
|
Name |
Type |
Restrictions |
| Deptno |
NUMBER(4) |
Not Null |
|
Deptno |
NUMBER(4) |
Not Null |
| Tabno |
NUMBER(6) |
Not Null |
|
Named |
VARCHAR2(35) |
|
| Namet |
VARCHAR2(35) |
|
|
|
|
|
| Summ |
NUMBER(10,2) |
>0 |
|
|
|
|
Поля Deptno и Tabno являются
первичным ключом таблицы Salary. Внешний ключ
Deptno таблицы Salary связан с
первичным ключом таблицы Dept.
Читаем записи входного файла Salary.dbf и
проверяем их на следующие ошибки:
- Поля
DEP и TBN не должны быть
пустыми.
- Поле
SUM должно быть больше нуля.
- Связка полей
DEP и TBN не
должны повторяться в разных записях.
- Для поля
DEP должна найтись запись в
таблице DEPT такая, что
DEP=Deptno.
Если ошибки есть, то пользователю выдается протокол ошибок.
Никакая информация при этом в выходную таблицу не
записывается.
Если ошибок нет, то все записи записываются в выходную
таблицу базы данных Oracle Salary. Пользователю
выдается соответствующее сообщение.
 |
|
На рис.4 показан индикатор
выполнения, который видит пользователь во время работы
программы. | Рис.4. Индикатор
выполнения.
Текст программы.
Ниже приведен текст подпрограммы с подробными
комментариями. После завершения работы программы, необходимо
проверить результат выполнения подпограммы. Для этого можно
использовать встроенную функцию Form_Success. Если он равен
FALSE, то следует показать пользователю
записанный в файл IMP_LOG.TXT протокол
ошибок. PROCEDURE TO_IMPORT_DBF IS
idConnection EXEC_SQL.CONNTYPE; -- дескриптор ODBC соединения
idCursor EXEC_SQL.CURSTYPE; -- идентификатор курсора
vSQLStr VARCHAR2(50) := '';-- строка для SQL выражения
iIgnore PLS_INTEGER := 0; -- пустышка
iRows PLS_INTEGER := 0; -- номер импортируемой строки
iAltogether PLS_INTEGER := 0; -- всего строк для импорта
nlocDep NUMBER(4) := 0; -- номер отдела
nlocTab NUMBER(6) := 0; -- табельный номер
nlocName VARCHAR2(35) := ''; -- ФИО
nlocSum NUMBER(12,2) := 0; -- сумма
logFile Text_IO.File_Type; -- файл протокола ошибок
nBarWidth NUMBER := 0; -- длина индикатора выполнения
bErr BOOLEAN := FALSE; -- признак завершение с ошибками
nErrNum NUMBER := 0; -- номер ошибки
vErrTxt VARCHAR2(240) := ''; -- текст ошибки
vOldMessageLevel VARCHAR2(3) := '0'; -- копия номера уровня сообщ.
-- Процедура завершения работы.
PROCEDURE TO_COMPLETE ( bError BOOLEAN) IS
nButton NUMBER := 0; -- номер кнопки
vErrText VARCHAR2( 100); -- текст ошибки
BEGIN
-- Если курсор или соединение не закрыты, закрываем.
-- Закрываем файл протокола ошибок.
IF EXEC_SQL.IS_Connected(idConnection) THEN
IF EXEC_SQL.Is_Open(idConnection, idCursor) THEN
EXEC_SQL.Close_Cursor(idConnection, idCursor);
END IF;
EXEC_SQL.Close_Connection(idConnection);
END IF;
Text_IO.Fclose (logFile);
-- Если были ошибки, выдаем соответствующее сообщение
-- и делаем откат транзакций.
IF bError THEN
Clear_Form( NO_VALIDATE, TO_SAVEPOINT);
vErrText :=
'Обнаружены ошибки. Все транзакции отменены. '||
'Исправьте ошибки и повторите импорт';
-- Если импорт закончился без ошибок, сообщаем об этом
-- пользователю и фиксируем транзакции.
ELSE
Commit_Form;
vErrText :=
'Успешно импортированы ' || to_char( iRows) ||
' записей';
END IF;
Set_Alert_Property('SIGNAL', ALERT_MESSAGE_TEXT, vErrText);
nButton := Show_Alert( 'SIGNAL');
-- Восстанавливаем старый уровень сообщений.
:SYSTEM.MESSAGE_LEVEL := vOldMessageLevel;-
-- Если были ошибки, вызываем встроенное исключение.
-- После завершения работы программы это позволит нам
-- с помощью Form_Success проверить наличие ошибок
-- и выдать протокол ошибок.
IF bError = TRUE THEN
Form_Trigger_Failure;
END IF;
END;
BEGIN
-- НАЧАЛО ПРОГРАММЫ.
-- Оформляем индикатор выполнения. Он представляет из себя
-- надпись :TXT_CALC, ниже которой находится длинный серый
-- прямоугольник RECT_BAR, внутри которого движется цветная
-- полоска BAR. Сначала делаем RECT_BAR и BAR невидимыми.
-- Переходим в блок индикатора.
:TXT_CALC := 'Удаление старых записей...';
Set_Item_Property( 'RECT_BAR', VISIBLE, PROPERTY_FALSE);
Set_Item_Property( 'BAR', VISIBLE, PROPERTY_FALSE);
Go_Block( 'BLK_CALC');
Synchronize;
-- Открываем файл протокола ошибок.
logFile := Text_IO.Fopen('imp_log.txt', 'w');
Text_IO.Put_Line(logFile, 'Ошибки импорта сведений '||
'по зарплате SALARY.DBF :');
Text_IO.New_Line(logFile, 1);
-- Присваеваем уровеню сообщений значение 5,
-- чтобы подавить вывод ненужных сообщений.
vOldMessageLevel := :SYSTEM.MESSAGE_LEVEL;
IF vOldMessageLevel < 5 THEN
:SYSTEM.MESSAGE_LEVEL := '5';
END IF;
-- Фиксируем транзакции.
-- В эту точку будет совешен откат в случае ошибок.
Commit_Form;
-- Удаляем все записи из таблицы, предназначенной для записи
-- импортируемой информации.
DELETE FROM salary;
-- Открываем соединение и курсор для чтения из DBF файла.
-- imp_sal - имя источника данных (DSN),
-- прописывается: панель управления->источники данных (ODBC)
idConnection :=
EXEC_SQL.Open_Connection('null/null@odbc:imp_sal');
idCursor := EXEC_SQL.Open_Cursor(idConnection);
-- Для обеспечения работы индикатора выполнения нужно знать
-- общее количество записей. Формируем в текстовой строке
-- соответствующий SQL запрос. Выполняем синтаксический разбор
-- SQL выражения и определяем переменную,
-- куда будет считан столбец 1.
vSQLStr := ' SELECT COUNT(*) FROM SALARY ';
EXEC_SQL.Parse(idConnection, idCursor, vSQLStr, exec_sql.V7);
EXEC_SQL.Define_Column(idConnection, idCursor, 1, iAltogether);
-- Выполняем запрос и выборку.
-- Если строки выбраны, записываем результат
-- в локальную переменную.
IF EXEC_SQL.Execute_And_Fetch(idConnection, idCursor) > 0 THEN
EXEC_SQL.Column_Value(idConnection, idCursor, 1, iAltogether);
END IF;
-- Приступаем к выборке данных. Опять готовим SQL выражение,
-- делаем его синтаксический разбор, определяем выбираемые колонки
-- и выполняем запрос.
vSQLStr := ' SELECT S.DEP, S.TBN, S.NAME, S.SUM FROM SALARY S ';
EXEC_SQL.Parse(idConnection, idCursor, vSQLStr, exec_sql.V7);
EXEC_SQL.Define_Column(idConnection, idCursor, 1, nlocDep);
EXEC_SQL.Define_Column(idConnection, idCursor, 2, nlocTab);
EXEC_SQL.Define_Column(idConnection, idCursor, 3, nlocName, 35);
EXEC_SQL.Define_Column(idConnection, idCursor, 4, nlocSum);
iIgnore := EXEC_SQL.Execute(idConnection, idCursor);
-- Меняем внешний вид индикатор выполнения. Вычисляем длину RECT_BAR.
-- Обнуляем длину BAR. Делаем оба элемента видимыми.
Set_Item_Property( 'BAR', WIDTH, 0);
nBarWidth := Get_Item_Property( 'RECT_BAR', WIDTH) - 2;
:TXT_CALC := 'Импорт ...';
Set_Item_Property( 'RECT_BAR', VISIBLE, PROPERTY_TRUE);
Set_Item_Property( 'BAR', VISIBLE, PROPERTY_TRUE);
Synchronize;
-- Здесь начинается цикл чтения - записи.
-- Извлекаем очередную запись, записываем данные
-- в локальные переменные, увеличиваем счетчик записей.
WHILE (EXEC_SQL.Fetch_Rows(idConnection, idCursor) > 0 ) LOOP
EXEC_SQL.Column_Value(idConnection, idCursor, 1, nlocDep);
EXEC_SQL.Column_Value(idConnection, idCursor, 2, nlocTab);
EXEC_SQL.Column_Value(idConnection, idCursor, 3, nlocName);
EXEC_SQL.Column_Value(idConnection, idCursor, 4, nlocSum);
iRows := iRows + 1;
-- Для удобства обработки ошибок поместим операторы записи
-- во вложенный блок со своим EXCEPTION. Таким образом, мы
-- разнесем обработку входных и выходных ошибок
BEGIN
-- Вставляем прочитанные данные в выходную таблицу.
INSERT INTO salary ( deptno, tabno, namet, summ)
VALUES( nlocDep, nlocTab, nlocName, nlocSum);
EXCEPTION
WHEN OTHERS THEN
-- Обработка ошибок записи.
-- Запоминаем код и текст ошибки.
-- Записываем в файл протокола ошибок сначала сами данные,
-- затем текст ошибки.
nErrNum := SQLCODE;
vErrTxt := SQLERRM;
bErr := TRUE;
Text_IO.Put_Line( logFile,
to_char( nlocDep) || ' ' || to_char( nlocTab)
||' ' || nlocName || ' ' || to_char( nlocSum));
-- Сначала проанализируем ошибки связанные с данными.
-- После обнаружения ошибки такого типа, мы записываем
-- информацию о ней, но обработку не прекращаем.
-- Таким образом, в конце работы пользователь
-- имеет информацию о всех ошибках.
-- Типы ошибок:
-- -2290 - нарушено правило целостности
-- -1400 - пропущен первичный ключ или
-- обязательный (NOT NULL) столбец,
-- или NULL во время операции INSERT
-- -2291 - родительский ключ не найден
-- -1 - нарушено правило уникальности ключа
IF nErrNum = -2290 THEN
Text_IO.Put_Line( logFile,
'Сумма зарплаты должна быть больше нуля');
ELSIF nErrNum = -1400 THEN
Text_IO.Put_Line( logFile,
'Ни одно из полей кроме фамилии не '||
'должны быть пустыми');
ELSIF nErrNum = -2291 THEN
Text_IO.Put_Line( logFile,
'Такое подразделение отсутствует в '||
'справочнике подразделений');
ELSIF nErrNum = -1 THEN
Text_IO.Put_Line( logFile,
'Запись с такими подразделением и '||
'табельным номером была уже введена');
ELSE
-- Остальные ошибки записи. Природу этих ошибок мы
-- не анализируем. Просто предполагаем, что обработку
-- далее продолжать невозможно. Поэтому завершаем работу.
Text_IO.Put_Line( logFile, vErrTxt);
bErr := TRUE;
TO_COMPLETE( bErr);
END IF;
Text_IO.New_Line(logFile, 1);
END;
-- Вычисляем новую длину цветной полоски
-- в индикаторе выполнения.
Set_Item_Property( 'BAR', WIDTH, nBarWidth *
iRows / iAltogether);
Synchronize;
END LOOP; -- Конец цикла чтения - записи.
--Завершение работы.
TO_COMPLETE( bErr);
-- Обработка ошибок чтения.
-- Записываем в файл протокола ошибок текст ошибки.
-- Завершаем работу.
EXCEPTION
WHEN EXEC_SQL.Invalid_Connection THEN
TEXT_IO.Put_Line(logFile,
'ОШИБКА соединения с источником данных.');
bErr := TRUE;
TO_COMPLETE( bErr);
WHEN EXEC_SQL.Invalid_Column_Number THEN
TEXT_IO.Put_Line(logFile,
'ОШИБКА: EXEC_SQL.Describe_Column '||
'встретила номер колонки отсутствывающий '||
'в исходных данных.');
bErr := TRUE;
TO_COMPLETE( bErr);
-- Ошибки общего типа.
WHEN EXEC_SQL.Package_Error THEN
TEXT_IO.Put_Line(logFile, 'ОШИБКА (' ||
TO_CHAR(EXEC_SQL.LAST_ERROR_CODE(idConnection))
||'): ' ||
EXEC_SQL.LAST_ERROR_MESG(idConnection));
bErr := TRUE;
TO_COMPLETE( bErr);
END;
|