вторник, 27 ноября 2012 г.

О соединении данных столбца в одну строку с помощью SQL-запроса

Постановка задачи: в некоей таблице имеется столбец с данными. Необходимо эти данные склеить в строку с помощью одного SQL-запроса.

Часть I. Аналитика и иерархия.

Простой способ решения задачи подсказал Т.Кайт в статье On Ignoring, Locking, and Parsing. Этот способ основывается на использовании аналитических функций и функции SYS_CONNECT_BY_PATH для иерархических запросов и применим начиная с Oracle9i Database Release 1. Идея заключается в следующем.
  1. Выбираемые данные необходимо разделить на подгруппы, для которых будут склеиваться строки и в каждой подгруппе пронумеровать элементы в порядке их склеивания.
      select
        deptno,
        ename,
        row_number() over (partition by deptno order by ename) rn
      from emp
  2. С помощью иерархического запроса все данные подгруппы соединяются в одну ветку дерева. Для каждого узла с помощью функции SYS_CONNECT_BY_PATH вычисляется строка со всеми предшествующими значениями поля, разделенными заданным разделителем.
    select
      deptno,
      sys_connect_by_path(ename, ' ' ) scbp
    from
      (select
         deptno,
         ename,
         row_number() over (partition by deptno order by ename) rn
       from emp
      )
    start with rn = 1
    connect by prior rn = rn-1
    and prior deptno = deptno
  3. Далее остается сгруппировать полученные данные по идентификатору подгруппы и выбрать для каждой значение строки максимальной длинны.
    select
      deptno,
      max(sys_connect_by_path(ename, ' ' )) scbp
    from
      (select
         deptno,
         ename,
         row_number() over (partition by deptno order by ename) rn
       from emp
      )
    start with rn = 1
    connect by prior rn = rn-1
    and prior deptno = deptno
    group by deptno
    order by deptno
В результате получаем список сотрудников по отделам.
   DEPTNO         SCBP
---------         ----------------------------------
       10         CLARK KING MILLER
       20         ADAMS FORD JONES SCOTT ...
       30         ALLEN BLAKE JAMES MARTIN ...
Этот способ прост и удобен, но имеет один недостаток. Функция SYS_CONNECT_BY_PATH работает с переменными VARCHAR2, которые, как известно, ограничиваются размером 4000 символов. При большом объеме склеиваемых данных, неизбежно возникает ошибка ORA-01489: result of string concatenation is too long. Эту проблему решает второй способ решения задачи.

Часть II. XML и CLOB.

Второй вариант предусматривает использование аггрегатной функции XMLAGG для получения склеенной строки в виде XML-объекта, который может быть преобразован в CLOB.
select
  deptno,
  XMLAGG(XMLELEMENT("node", ename)).getCLOBVal() scbp
from emp
group by deptno
Для упорядочивания элементов в склеиваемой строке функции XMLAGG можно добавить сортировку.
select
  deptno,
  XMLAGG(XMLELEMENT("node", ename) order by ename).getCLOBVal() scbp
from emp
group by deptno
А если возникнет необходимость отсортировать полученные склеенные строки, можно воспользоваться преобразованием типов cast.
select
  deptno,
  XMLAGG(XMLELEMENT("node", ename) order by ename).getCLOBVal() scbp
from emp
group by deptno
order by cast( substr(scbp, 1, 4000) as varchar2(4000))
В результате получится следующее:
   DEPTNO         SCBP
---------         ----------------------------------
       20         <node>ADAMS</node><node>FORD</node> ...
       30         <node>ALLEN</node><node>BLAKE</node> ...
       10         <node>CLARK</node><node>KING</node>
Недостаток данного способа – наличие тэгов, которые необходимо обрабатывать в зависимости от целей поставленной задачи. Хотя для случая, ставшего причиной поиска описанного решения, тэги оказались скорее плюсом.
Этот же метод можно применить и для более коротких строк, где не требуется использование CLOB-данных. XML-объект можно преобразовать в varchar2 с помощью функции getStringVal().
select
  deptno,
  XMLAGG(XMLELEMENT("node", ename) order by ename).getStringVal() scbp
from emp
group by deptno
order by scbp

среда, 14 ноября 2012 г.

Окно - Исходный формат отчета отличается от конечного

Итак, если создан отчет с шаблоном в формате RTF,  а вывод в виде Excel, и выпадает окошко с сообщением, что исходный формат отчета отличается от конечного, а нам это окошко надо убрать с глаз долой, тогда правим:

В регистре:
HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Excel\Security
новый параметр (если нету) с типом DWORD , называем ExtensionHardening - значение 0

вторник, 6 ноября 2012 г.

Использование Oracle SQL*Loader для загрузки данных в таблицы

Использование Oracle SQL*Loader для загрузки данных в таблицы

Данный минифак не претендует на полноту отражения всех особенностей работы с SQL*Loader, а служит для того, чтобы быстро войти в тему
SQL*Loader — специальная утилита Oracle сервера для загрузки данных в БД из локальных файлов с данными. Для того чтобы воспользоваться ее функционалом создадим несколько файлов, которые будем использлвать в работе, и положим их в папку C:\oracle\ora90\BIN\load (в общем случае):

  • data.dat или data.txt — собственно файл с данными. Пусть он будет представлять строки данных разделенных сиволом перевода строки.
  • load.ctl — контрольный файл — содержит инструкции по загрузке данных в БД, в нашем случае будет иметь вид:
    LOAD DATA -- Загружаем данные
    INFILE 'data.txt'  "str '\n'" -- из файла data.txt, разделитель строк str '\n' - символ перевода строки
    INTO TABLE load_data -- в таблицу load_data
    FIELDS terminated BY ',' OPTIONALLY ENCLOSED BY '"'
    (DATA char) -- название столбца и тип данных в таблице load_data
    То что мы не используем, но встречается часто: fields terminated by ‘,’ означает, что границы данных определены по символу «,», а optionally enclosed by ‘»‘ определяет, что данные могут содержать символ обрамления.
  • load.bat — пакетный файл (англ. batch file) выполняющий запуск sqlldr.exe имеет вид:
    SET nls_lang=russian_cis.ru8pc866 -- устанавливаем нужную кодировку
    sqlldr.exe userid=user_name/user_pass@test control=LOAD.ctl errors=100 bad=DATA.bad -- test — строка связи*, load.ctl — контрольный файл, data.bad — файл плохих записей
    Строка связи — это та строка, которая должна быть прописана в ORA Network Configuration File: D:\oracle\ora90\network\admin\tnsnames.ora (в общем случае). Errors=100 — количество допустимых ошибок (по умолчанию 50).
  • Далее находим нашу папку C:\oracle\ora90\BIN\load, находим файл load.bat, запускаем его и любуемся на результат:
    SELECT * FROM load_data;
    Пока все, пишите об ошибках и неточностях.
Пара полезных ссылок по теме:
Примечание:
Интересно, что при сохранении *.xslx в «.csv с разделителем запятой» разделителем на самом деле становится точка с запятой.

Разбор/парсинг значения поля (строки) по разделителям на строки

Источник:  http://plsqlbook.ru/category/question-answer/


Простой пример того, о чем речь:
SELECT regexp_substr(str, '[^#]+', 1, LEVEL)
  FROM (SELECT 'one#two#tree' str FROM dual)
CONNECT BY instr(str, '#', 1, LEVEL-1) > 0
напоминалка:
  • ‘[^#]+’ такая маска найдет все символы кроме решетки
  • instr(str, ‘#’, 1, LEVEL-1) > 0 поиск позиции разделителя начиная с первой позиции и, видимо, с LEVEL = 2 (возвратит 0 если позиция при заданных параметрах не найдена)
  • CONNECT BY условие подсказывает ораклу как долго продолжать цикл
Результат:
1 one
2 two
3 tree
 
 
Вариант 2
 
select 
   ltrim(rtrim(
    substr(s_element,
         instr(s_element,',',1,iter.pos)+1,
         instr(s_element,',',1,iter.pos+1)-
         instr(s_element,',',1,iter.pos)),',')) s_element,iter.pos
from 
     (select ','||s_in||',' s_element from dual) csv,
     (select rownum pos from ra_customer_trx_all) iter
where 
     iter.pos <= ((length(csv.s_element)-length(replace(csv.s_element,',')))/length(','))-1 

где  s_in  - исходная строка