Ноябрь 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 это делается так:

  1. Мой компьютер -> Панель управления -> Администрирование -> Источники данных (ODBC).

  2. Перейдите на закладку Пользовательский DSN и нажмите кнопку Добавить.

  3. В появившемся списке драйверов выберите INTERSOLV OEM 2.12 32-BIT dBASE (*.dbf) и нажмите кнопку Готово.

  4. В окне ODBC dBASE Driver Setup заполните поля, как показано на рис.2


    Рис.2. Настройка ODBC драйвера.

    Data Source Name - произвольное имя, которое вы будете использовать в программе.
    Database Directory - директория, в которой лежит DBF файл.
    Create Type - тип DBF файла.

    Нажмите кнопку OK.
    В большинстве случаев этого достаточно. Но вы можете посмотреть дополнительные параметры (кнопка Advanced).

Тестирование Oracle ODBC.

Теперь можно проверить, как работает то, что мы сделали. Стартуйте программу тестирования Oracle ODBC:

  1. Программы -> Oracle Developer 6.0 Direct Drivers -> Oracle ODBC Test.

  2. В появившемся окне нажмите кнопку Connect. Перейдите на закладку Источники данных компьютера. Выделите имя imp_sal и нажмите кнопку OK.

  3. В верхнем окне введите команду:
    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;
E-mail this page