вторник, 10 декабря 2013 г.

sqlldr + oracle instant client + loading files to blob

Есть отличная дока по созданию переносного клиента для работы с БД Oracle обладающая более широким функционалом, чем предлагаемый родной Oracle Instant Client. На странице 21 документа автору удается запустить sqlldr, но, поскольку утилита создана не для вывода справки о том, как с ней работать, а для работы с базой, этим реальная настройка не заканчивается, в связи с чем пишу очередное исследование на тему как скрестить sqlldr с oracle instant client.

Итак, сценарий 1. Загружаем строки и числа.

Целевая таблица:
CREATE TABLE BLOB_TEST.EMP(empno NUMBER, empname VARCHAR2(64), sal NUMBER, deptno NUMBER);

Объявляем переменную окружения ORACLE_HOME=путь_к_Instant_Client (для краткости назову директорию oic) и NLS_LANG=AMERICAN_AMERICA.UTF8.
Создаём в oic директорию rdbms/mesg, куда переписываем файл ulus.msb.
Для клиента под linux x32 этого достаточно, для клиента под linux x64 необходимо дополнительно прописать LD_LIBRARY_PATH=путь_к_Instant_Client.
К этому моменту имеем то, что описано в доке, ссылка на которую дана в начале (за исключением переменных окружения, но они, как покажет практика, для работы sqlldr не нужны).

Создаём файл tst.lst со следующим содержимым:
10001,"Scott Tiger", 1000, 40
10002,"Frank Naude", 500, 20

Создаём файл tst.ctl со следующим содержимым:
LOAD DATA
CHARACTERSET UTF8
INFILE 'tst.lst'
  APPEND
  INTO TABLE BLOB_TEST.EMP
  FIELDS TERMINATED BY ',' optionally enclosed by '"'
  ( empno, empname, sal, deptno )

Итого имеющаяся структура:
/home/oracle/oic/...
/home/oracle/tst.lst
/home/oracle/tst.ctl
ORACLE_HOME=/home/oracle/oic
NLS_LANG=AMERICAN_AMERICA.UTF8

Текущая директория:
$pwd
/home/oracle

Проверяем, что никакие другие модули sqlldr не могут быть использованы:
$which sqlldr
/usr/bin/which: no sqlldr in (...)

Проверяем, что наш sqlldr запускается корректно:
$ ./oic/sqlldr 
SQL*Loader: Release 11.2.0.2.0 - Production on Mon Dec 9 23:46:27 2013
Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.
Usage: SQLLDR keyword=value [,keyword=value,...]
...

На данный момент мы "убедились", что sqlldr работает так же, как указано в вышеуказанной доке. Дальше нас ждёт разочарование :)
$./oic/sqlldr system/oracle@localhost:1521/orcl tst.ctl 
SQL*Loader-704: Internal error: ulconnect: OCIEnvCreate [-1]

А раз так, значит работа по скрещиванию Oracle Instant Client и sqlldr не завершена.
Создаём в oic директорию oracore/zoneinfo, куда из полного клиента переписываем файл timezlrg_1.dat:
$mkdir -p oic/oracore/zoneinfo
$cp /home/oracle/ora_full_client/oracore/zoneinfo/timezlrg_1.dat /home/oracle/oic/oracore/zoneinfo/

Первый барьер пройден, теперь при запуске sqlldr получаем ошибку:
SQL*Loader-266: Unable to locate character set handle for UTF8.

Если из tst.ctl убрать явное указание кодировки, будет аналогичная ошибка, но с соответствующей настройкам кодировке. Например:
SQL*Loader-282: Unable to locate character set handle for character set ID (873).

Если в системе задан NLS_LANG, будет другая ошибка:
SQL*Loader-128: unable to begin a session
Error while trying to retrieve text for error ORA-12705

В общем веселуха. Но поскольку проблема подключения к базе осталась, продолжаем процесс скрещивания - создаём директорию nls/data, куда перебрасываем файлы lx00001.nlb,lx10001.nlb, lx1boot.nlb, lx20001.nlb, lx200ab.nlb, lx20367.nlb, lx20369.nlb:
$mkdir -p oic/nls/data
$cp /home/oracle/ora_full_client/nls/data/lx00001.nlb /home/oracle/oic/nls/data/
$cp /home/oracle/ora_full_client/nls/data/lx10001.nlb /home/oracle/oic/nls/data/
$cp /home/oracle/ora_full_client/nls/data/lx1boot.nlb /home/oracle/oic/nls/data/
$cp /home/oracle/ora_full_client/nls/data/lx20001.nlb /home/oracle/oic/nls/data/
$cp /home/oracle/ora_full_client/nls/data/lx200ab.nlb /home/oracle/oic/nls/data/
$cp /home/oracle/ora_full_client/nls/data/lx20367.nlb /home/oracle/oic/nls/data/
$cp /home/oracle/ora_full_client/nls/data/lx20369.nlb /home/oracle/oic/nls/data/

Этого набора достаточно для работы sqlldr с параметрами language = AMERICAN, territory = AMERICA.

Проверяем:
$export NLS_LANG=AMERICAN_AMERICA.UTF8
$./oic/sqlldr system/oracle@localhost:1521/orcl tst.ctl
SQL*Loader: Release 11.2.0.2.0 - Production on Tue Dec 10 02:48:54 2013

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.

Commit point reached - logical record count 1
Commit point reached - logical record count 2

Алилуйя! Получилось!

Теперь проверим сценарий 2. Загружаем файлы.

Меняем содержимое tst.lst на:
files/atis_armenia_201201.jpg
"files/ARMENIA 4_0 SLOVAKIA HRAPARAK 7.09.2011.flv"

Содержимое tst.ctl:
LOAD DATA
INFILE 'tst.lst'
  APPEND
  INTO TABLE BLOB_TEST.LOADME
  FIELDS TERMINATED BY ','  optionally enclosed by '"'
  (filename FILLER CHAR(100),
   fileb LOBFILE(filename) TERMINATED BY EOF)

Целевая таблица
create table BLOB_TEST.LOADME(fileb BLOB, created DATE default SYSDATE);

Создаём директорию files, куда заливаем файлы atis_armenia_201201.jpg и ARMENIA 4_0 SLOVAKIA HRAPARAK 7.09.2011.flv.

Итого имеющаяся структура:
/home/oracle/oic/...
/home/oracle/files/ARMENIA 4_0 SLOVAKIA HRAPARAK 7.09.2011.flv
/home/oracle/files/atis_armenia_201201.jpg
/home/oracle/tst.lst
/home/oracle/tst.ctl
ORACLE_HOME=/home/oracle/oic
NLS_LANG=AMERICAN_AMERICA.UTF8

Запускаем sqlldr:
./oic/sqlldr system/oracle@localhost:1521/orcl tst.ctl
SQL*Loader: Release 11.2.0.2.0 - Production on Tue Dec 10 04:45:24 2013

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.

Commit point reached - logical record count 1
Commit point reached - logical record count 2

Бинго! И заливка файлов работает. Итого, мы научили sqlldr работать в паре с Oracle Instant Client. Получилось с некоторыми ограничениями (nls_lang), но для моих задач полученный результат достаточен. Если вам требуется заточить sqlldr под другие значения языка и территории можно методом проб и ошибок найти тот необходимый набор файлов из .../nls/data, который требуется для работы, либо, как альтернативный вариант, переписать все файлы :)

Удачи!

четверг, 30 мая 2013 г.

Секционирование таблиц в Oracle. Загадочное увеличение размера файлов данных. Часть 2

Пересоздал табличные пространства и таблицу, создав по умолчанию две секции (каждая из которых в свою очередь включает по 4 подсекции):
CREATE TABLESPACE OP_J_01 DATAFILE 'op_j_tbs01.dbf' SIZE 128K AUTOEXTEND ON NEXT 1K MAXSIZE UNLIMITED;
CREATE TABLESPACE OP_J_02 DATAFILE 'op_j_tbs02.dbf' SIZE 128K AUTOEXTEND ON NEXT 1K MAXSIZE UNLIMITED;
CREATE TABLESPACE OP_J_03 DATAFILE 'op_j_tbs03.dbf' SIZE 128K AUTOEXTEND ON NEXT 1K MAXSIZE UNLIMITED;
CREATE TABLESPACE OP_J_04 DATAFILE 'op_j_tbs04.dbf' SIZE 128K AUTOEXTEND ON NEXT 1K MAXSIZE UNLIMITED;
CREATE TABLE ZAKUPKI_DEV.OPERATION_JOURNAL_REDIF
   (    OP_JOURNAL_ID NUMBER(20,0) NOT NULL ENABLE,
        ORDER_ID NUMBER(20,0),
        TMSTMP TIMESTAMP (6),
        EVENT_CLASS VARCHAR2(50)

   )
PARTITION BY RANGE (TMSTMP) INTERVAL (NUMTOYMINTERVAL(1,'MONTH'))
  SUBPARTITION BY LIST (EVENT_CLASS)
  SUBPARTITION TEMPLATE
    (
    SUBPARTITION EC_REGISTER_CONTRACTS VALUES ('REGISTER_CONTRACTS') TABLESPACE OP_J_01,
    SUBPARTITION EC_ORDER VALUES ('ORDER') TABLESPACE OP_J_02,
    SUBPARTITION EC_AUTHORIZATION VALUES ('AUTHORIZATION') TABLESPACE OP_J_03,
    SUBPARTITION EC_OTHER VALUES (DEFAULT) TABLESPACE OP_J_04
    )
(PARTITION p201101 VALUES LESS THAN (TO_DATE('01012011','ddmmyyyy')),
 PARTITION p201102 VALUES LESS THAN (TO_DATE('01022011','ddmmyyyy'))
) ENABLE ROW MOVEMENT;

Смотрим размер файлов данных:
TABLESPACE_NAME BYTES USER_BYTES
OP_J_01 19005440 18939904
OP_J_02 19005440 18939904
OP_J_03 19005440 18939904
OP_J_04 19005440 18939904

А вот это уже интересно. Получается, на каждую субпартицию заранее резервируется место в табличном пространстве: на одну субпартицию 10МБ, на две 18МБ.
Для чистоты эксперимента проделал тоже самое, но с созданием трех секций:
TABLESPACE_NAME BYTES USER_BYTES
OP_J_01 27394048 27328512
OP_J_02 27394048 27328512
OP_J_03 27394048 27328512
OP_J_04 27394048 27328512
Т.е. на три секции требуется 26МБ на каждую субпартицию. Похоже, это и есть ответ на вопрос из предыдущего поста, почему размер файлов данных вырос до 43МБ.

В результате этих манипуляций возникло нелепое предположение: может быть такое, что независимо от количества данных в каждой партиции размер файлов данных увеличивается синхронно для всех? Проведем эксперимент:
Заново создаю файлы данных и таблицу с одной секцией по-умолчанию (всё как в первом посте), имею размер файлов данных 10616832 байт (~10МБ).
Вставляю в таблицу 383940 записей с EVENT_CLASS = 'ORDER' и tmstmp < TO_DATE('01012011', 'ddmmyyyy'), ожидая при этом, что увеличиться только файл данных, соответствующий табличному пространству EC_ORDER VALUES:
TABLESPACE_NAME BYTES USER_BYTES
OP_J_01 10616832 10551296
OP_J_02 19005440 18939904
OP_J_03 10616832 10551296
OP_J_04 10616832 10551296

Так и получается. При этом на самом деле был создан дополнительный экстент для сегмента, соответствующего субпартиции P201101_EC_ORDER.
Нелепое предположение осталось неподтвержденным и это прекрасно :)

среда, 29 мая 2013 г.

Секционирование таблиц в Oracle. Загадочное увеличение размера файлов данных

Выполнил следующий эксперимент:
Имею:
Red Hat Enterprise Linux Server release 5.5 (Tikanga)
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production

Создаю 5 табличных пространств с минимальным допустимым размером и шагом увеличения:
CREATE TABLESPACE OP_J_01 DATAFILE 'op_j_tbs01.dbf' SIZE 128K AUTOEXTEND ON NEXT 1K MAXSIZE UNLIMITED;
CREATE TABLESPACE OP_J_02 DATAFILE 'op_j_tbs02.dbf' SIZE 128K AUTOEXTEND ON NEXT 1K MAXSIZE UNLIMITED;
CREATE TABLESPACE OP_J_03 DATAFILE 'op_j_tbs03.dbf' SIZE 128K AUTOEXTEND ON NEXT 1K MAXSIZE UNLIMITED;
CREATE TABLESPACE OP_J_04 DATAFILE 'op_j_tbs04.dbf' SIZE 128K AUTOEXTEND ON NEXT 1K MAXSIZE UNLIMITED;
CREATE TABLESPACE OP_J_05 DATAFILE 'op_j_tbs05.dbf' SIZE 128K AUTOEXTEND ON NEXT 1K MAXSIZE UNLIMITED;

Смотрю, что получилось:
SELECT tablespace_name, bytes, user_bytes FROM dba_data_files WHERE tablespace_name LIKE 'OP%' ORDER BY 1

TABLESPACE_NAME BYTES USER_BYTES
OP_J_01 131072 65536
OP_J_02 131072 65536
OP_J_03 131072 65536
OP_J_04 131072 65536
OP_J_05 131072 65536

Создаю секционированную таблицу:
CREATE TABLE OPERATION_JOURNAL_REDIF 
   (    OP_JOURNAL_ID NUMBER(20,0) NOT NULL ENABLE, 
        ORDER_ID NUMBER(20,0), 
        TMSTMP TIMESTAMP (6), 
        EVENT_CLASS VARCHAR2(50)
   ) 
PARTITION BY RANGE (TMSTMP) INTERVAL (NUMTOYMINTERVAL(1,'MONTH'))
  SUBPARTITION BY LIST (EVENT_CLASS)
  SUBPARTITION TEMPLATE
    (
    SUBPARTITION EC_REGISTER_CONTRACTS VALUES ('REGISTER_CONTRACTS') TABLESPACE OP_J_01, 
    SUBPARTITION EC_ORDER VALUES ('ORDER') TABLESPACE OP_J_02, 
    SUBPARTITION EC_AUTHORIZATION VALUES ('AUTHORIZATION') TABLESPACE OP_J_03, 
    SUBPARTITION EC_OTHER VALUES (DEFAULT) TABLESPACE OP_J_04
    )
(PARTITION p0 VALUES LESS THAN (TO_DATE('01012011','ddmmyyyy')))
TABLESPACE OP_J_05 ENABLE ROW MOVEMENT;

Снова смотрю на размеры файлов данных:

TABLESPACE_NAME BYTES USER_BYTES
OP_J_01 10616832 10551296
OP_J_02 10616832 10551296
OP_J_03 10616832 10551296
OP_J_04 10616832 10551296
OP_J_05 131072 65536

Файлы увеличились до 10МБ каждый. Initial_extent для всех созданных табличных пространств 65536. Почему тогда размер увеличен до 10МБ? Вопрос открытый, но дальше интересней.

Вставляю в таблицу 5000 записей с EVENT_CLASS = 'ORDER', ожидаю при этом, что все записи будут аккуратно сложены в табличное пространство OP_J_02, а значит в файл данных op_j_tbs02.dbf:
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36310210,40186,to_timestamp('22.01.13 18:27:43,477000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36310230,40185,to_timestamp('22.01.13 18:28:26,842000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36664590,42822,to_timestamp('28.02.13 17:09:19,933000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36664610,42822,to_timestamp('28.02.13 17:11:37,451000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36664660,42805,to_timestamp('28.02.13 17:15:38,845000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36664670,42805,to_timestamp('28.02.13 17:16:07,246000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (300089,9841,to_timestamp('17.02.11 03:46:55,749261000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (300150,9839,to_timestamp('12.10.10 13:39:50,158000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36310540,40206,to_timestamp('22.01.13 18:36:27,250000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36311360,40211,to_timestamp('22.01.13 18:48:02,011000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
...
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (350497,12338,to_timestamp('17.02.11 19:04:29,130000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36430590,41221,to_timestamp('04.02.13 16:53:14,609000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36430610,41221,to_timestamp('04.02.13 16:54:25,511000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36805210,44327,to_timestamp('02.04.13 17:29:46,846000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36805230,44325,to_timestamp('02.04.13 17:30:00,489000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36805280,44333,to_timestamp('02.04.13 17:34:35,661000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (36805290,44333,to_timestamp('02.04.13 17:34:43,450000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (349921,11667,to_timestamp('17.02.11 11:52:51,472000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (349922,11327,to_timestamp('17.02.11 11:53:39,030000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
Insert into OPERATION_JOURNAL_REDIF (OP_JOURNAL_ID,ORDER_ID,TMSTMP,EVENT_CLASS) values (349923,12291,to_timestamp('17.02.11 11:54:07,633000000','DD.MM.RR HH24:MI:SS,FF'),'ORDER');
commit;

И снова смотрю размеры файлов данных:
TABLESPACE_NAME BYTES USER_BYTES
OP_J_01 45219840 45154304
OP_J_02 45219840 45154304
OP_J_03 45219840 45154304
OP_J_04 45219840 45154304
OP_J_05 131072 65536

Удивительно, но все файлы данных имеют размер 43МБ. Этому объяснения пока не нашел.