четвер, 23 квітня 2015 р.

Передача массива с Delphi в коллекцию PL/SQL

Предыстория: понадобилось мне получать данные с кассового аппарата (КА) и передавать их в Оракл для дальнейшей обработки. Данные в КА хранятся в виде таблицы товаров с указанием цены, количества, к-ва продаж/возвратов и т.д. Получать данные можно только по одной строке с таблицы (такие методы у КА). И вот, что бы не передавать по одной строке с Делфи в Оракл решено было узнать как можно передать сразу все строки. Изначально план был такой: заполняем многомерный массив и передаем его в Оракл, но, скажу сразу, что ни многомерный, ни массив массивов передать не удалось и судя по некоторым форумам, которые я облазил за эти 2 дня, это сделать невозможно. У меня постоянно возникала ошибка несоответствия типов. Если все-таки такая возможность есть - комментируйте. 
В итоге я передавал в функцию, в качестве параметров одномерные массивы. Количество этих массивов соответствовало количеству необходимых мне для обработки колонок в таблице КА. 
Начну, пожалуй, с того, что я создал в 2 глобальных типа в PL/SQL:
create or replace type T_ExportDataFromMINI as object
  (
    ed_NumDoc           NUMBER,   -- Номер дока
    ed_NumRow           NUMBER, -- Номер строки в доке     

    ed_Price            NUMBER(12,2),   --Цена
    ed_QtySale          NUMBER,
    ed_SumSale          NUMBER(12,2),
    ed_QtyRet           NUMBER,
    ed_SumRet           NUMBER(12,2)
  )
 

и второй:
create or replace type t_exportdatafrommini_tree is table of t_exportdatafrommini 
Потом, в пакете, я прописал 7 локальных типов:
type t_mini_numdoc is table of number index by binary_integer; 
type t_mini_numrow is table of number index by binary_integer;
type t_mini_price is table of number(12,2) index by binary_integer;
type t_mini_qtysale is table of number index by binary_integer;
type t_mini_sumsale is table of number(12,2) index by binary_integer;
type t_mini_qtyret is table of number index by binary_integer;
type t_mini_sumret is table of number(12,2) index by binary_integer; 
  

и написал функцию, которая принимает параметры. Каждый параметр - это коллекция с типом, который я прописал выше. Сама функция:
function PARSEEXPORTDATAFROMMINI2(p_numdoc t_mini_numdoc, p_numrow t_mini_numrow, p_price t_mini_price,
p_qtysale t_mini_qtysal, p_sumsale t_mini_sumsale, p_qtyret t_mini_qtyret,
p_sumret t_mini_sumret) return integer is
i integer;
v_array t_exportdatafrommini_tree := t_exportdatafrommini_tree();
begin
--наполняем коллекцию
for i in 1..p_numdoc.COUNT loop
v_array.Extend();
v_array(v_array.count) := t_exportdatafrommini(p_numdoc(i), p_numrow(i), p_price(i),
p_qtysale(i), p_sumsale(i), p_qtyret(i), p_sumret(i));
end loop;
--проверяем, что же пришло с РКА
for rec in (select * from Table(cast(v_array as t_exportdatafrommini_tree))) loop
insert into tbl_roma
(tr_id, tr_doc_num, tr_log_info, tr_date)
values
(888, 888, rec.ed_NumDoc || '/' || rec.ed_NumRow || '/' || rec.ed_price || '/' ||
rec.ed_qtysale || '/' || rec.ed_sumsale || '/' || rec.ed_qtyret || '/' || rec.ed_sumret, sysdate);
end loop;
commit;
return 1;
end;
view raw 2 hosted with ❤ by GitHub
Далее переходим к Делфи. На форму был добавлен элемент TOracleQuery, прописаны параметры сессии, добавлен SQL текст:
begin
  :res := PKG_TEST.PARSEEXPORTDATAFROMMINI2(:p_numdoc, :p_numrow, :p_price, :p_qtysale, :p_sumsale, :p_qtyret, :p_sumret);
end;

И определены переменные через Variables Editor:
Самое главное, что бы для переменных, с помощью которых мы хотим передавать массивы была установлена галка PL/SQL Table.
Далее оглашаем необходимое количество массивов и инициализируем их:
.....
  arNumdoc, arNumrow, arPrice, arQtysale, arSumsale, arQtyret, arSumret: variant;
.....
  arNumdoc := VarArrayCreate([0, 3999], varVariant);
  arNumRow := VarArrayCreate([0, 3999], varVariant);
  arPrice := VarArrayCreate([0, 3999], varVariant);
  arQtysale := VarArrayCreate([0, 3999], varVariant);
  arSumsale := VarArrayCreate([0, 3999], varVariant);
  arQtyret := VarArrayCreate([0, 3999], varVariant);
  arSumret := VarArrayCreate([0, 3999], varVariant);


Я указал размер массива равным 4000 элементов, так как в КА нет метода, что бы получить количество элементов в памяти. После заполнения массивов данными (метод VarArrayPut) я меняю верхнюю границу массивов с помощью метода VarArrayRedim.
Далее заполняем SQL переменные:
with oqExportData do
    begin
      SetVariable('p_numdoc', arNumdoc);
      SetVariable('p_numrow', arNumrow);
      SetVariable('p_price', arPrice);
      SetVariable('p_qtysale', arQtysale);
      SetVariable('p_sumsale', arSumsale);
      SetVariable('p_qtyret', arQtyret);
      SetVariable('p_sumret', arSumret);
      Execute;
    end

 
И после всего этого обнуляем переменные:
   arNumdoc := Unassigned;
  arNumrow := Unassigned;
  arPrice := Unassigned;
  arQtysale := Unassigned;
  arSumsale := Unassigned;
  arQtyret := Unassigned;
  arSumret := Unassigned;

 
 

пʼятниця, 10 квітня 2015 р.

Знак зодиака в Oracle

Недавно, захотел я узнать представителя какого знака зодиака больше у нас в департаменте. Приведу кусок кода, который как-раз и парсит знаки по дате рождения:

      case 
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 3 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 4 AND EXTRACT(day FROM t.birthdate) <= 19THEN 'Овен'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 4 AND EXTRACT(day FROM t.birthdate) >= 20) OR (EXTRACT(MONTH FROM t.birthdate) = 5 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Телец'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 5 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 6 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Близнецы'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 6 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 7 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Рак'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 7 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 8 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Лев'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 8 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 9 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Дева'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 9 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 10 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Весы'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 10 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 11 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Скорпион'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 11 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 12 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Стрелец'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 12 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 1 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Козерог'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 1 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 2 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Водолей'
            WHEN (EXTRACT(MONTH FROM t.birthdate) = 2 AND EXTRACT(day FROM t.birthdate) >= 21) OR (EXTRACT(MONTH FROM t.birthdate) = 3 AND EXTRACT(day FROM t.birthdate) <= 20) THEN 'Рыба'
      end 

В общем по тем сотрудникам, у которых в названии профессии указано "программист" (искал like '%програм%')  вышел вот такой результат:
Так что если вы дева или весы - у вас больше шансов стать программистом =)