четверг, 24 января 2013 г.

Триггеры в Oracle


Триггеры DML

CREATE [OR REPLACE] TRIGGER имя_триггера
{BEFORE | AFTER}
{INSERT | DELETE | UPDATE | UPDATE OF список_столбцов} ON имя_таблицы
[FOR EACH ROW]
[WHEN (...)]
[DECLARE ...]
BEGIN

... исполняемые операторы ...

[EXCEPTION ...]
END [имя_триггера];



Триггер это именованный блок PL/SQL.
Он не может быть вызван из процедуры и не принимает никаких параметров при вызове.
Этот блок срабатывает при определенном событии а именно при запуске операций DML:
-INSERT
-UPDATE
-DELETE

Существуют еще и системные триггеры, которые срабатывают на события самой БД.

Триггеры используют:
Для реализации сложных ограничений целостности данных, которые невозможно осуществить
через описательные ограничения, установленные при создании таблиц.
Организации всевозможных видов аудита.
Оповещения других модулей о том, что делать в случае изменения информации содержащейся в БД.
Для реализации так называемых "бизнес-правил"
Для организации каскадных воздействий на таблицы БД.

Бывают триггеры операторные и строковые.
Операторный триггер активизируется один раз до или после оператора DML/
Строковый триггер активизируется один раз для каждой строки, на которую воздействует оператор DML.

Изменил оператор UPDATE две строки в таблице: строковый триггер активизируется два раза,
а операторный один.

Пример операторного триггера:

create or replace trigger bfotst
    before update on tab1
declare
begin
    insert into tab2(col1, col2, col3, col4)
        values(USER, SYSDATE, 'Update', 'Before statement trigger');
end bfotst;
/



create or replace trigger afttst
    after update on tab1
declare
begin
    insert into tab2(col1, col2, col3, col4)
        values(USER, SYSDATE, 'Update', 'After statement trigger');
end afttst;
/


Строковые триггеры:

create or replace trigger bfotstr
    before update on tab1
    for each row
declare
begin
    insert into tab2(col1, col2, col3, col4)
        values(USER, SYSDATE, 'Update', 'Before row trigger');
end bfotstr;
/


create or replace trigger afttstr
    after update on tab1
    for each row
declare
begin
    insert into tab2(col1, col2, col3, col4)
        values(USER, SYSDATE, 'Update', 'After row trigger');
end afttstr;
/


before - триггер активируется до срабатывания оператора DML.
after  - треггер активируется после срабатывания оператора DML.


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

:old
:new


create or replace trigger dlttstr
    before delete on tab1
    for each row
declare
begin

    a =  :old.col1;
    c =  :new.col2;
    b =  :old.col3;
 .
 .
 .
end dlttstr;
/



                           :OLD                                            :NEW
--------------------------------------------------------------------------------
INSERT :    NULL                                              значение после вставки
UPDATE :  значение перед изменением       значение после изменения
DELETE :  значение перед удалением          NULL



Рассмотрим пример:

Имеется таблица tab1 с первичным ключом:

ID
col1
col2
col3

Пытаемся в нее сделать вставку так:

insert into tab1(col1, col2, col3) values(var1, var2, var3);

получаем ошибку: невозможно вставить NULL  в ID

Делать нужно так:

создаем последовательность:

create sequence seq1
 start with 8000
 increment by 1;
/

создаем триггер:

create or replace trigger insIDtrig
    before insert on tab1
    for each row
declare
begin
    select seq1.nextval into  :new.ID from dual;
end insIDtrig;
/

теперь можно делать наш INSERT.


Еще пример:

Имеются две таблицы:

tab1:

ID
col1
col2

tab2:

ID
col
col2
idt1

Хотим чтобы вставляя строки в главную таблицу, аналогично вставлялись строки
и в подчиненную таблицу.

Для главной таблицы мы уже создали sequence seq1 и триггер insIDtrig,
который добавляет значения в поле ID таблицы tab1.

Создадим для подчиненной таблицы sequence

create sequence seq2
 start with 5
 increment by 1;
/

и создадим триггер:

create or replace trigger aftinsttrig
    after insert on tab1
    for each row
declare
begin
    insert into tab2(tab2.ID, tab2.col1, tab2.col2, tab2.idt1)
    values(seq2.nextval, :new.col1, :new.col2, :new.ID);
end aftinsttrig;
/


Напишем аналогичный триггер на UPDATE:

create or replace trigger aftupdttrig
    after update on tab1
    for each row
declare
begin
    update tab2
    set
         tab2.col1 =  :new.col1,
         tab2.col2 =  :new.col2,
         tab2.idt1 =  :new.ID
    where
         tab2.idt1 =  :old.ID;
end aftupdttrig;
/


И на удаление:
(каскадное удаление)

create or replace trigger bfrdelttrig
    before delete on tab1
    for each row
declare
begin
    delete from tab2
    where tab2.idt1 =  :old.ID;
end bfrdelttrig;
/

Менять псевдозапись :new в строковом триггере AFTER не имеет смысла,
так как событие уже обработано.

Менять псевдозапись :new возможно в строковом триггере before.
А псевдозапись :old никогда не модифицируется, а только считывается.


Предложение WHEN

Используйте предложение WHEN для уточнения условий выполнения кода триггера.

В этом примере код триггера будет исполняться только при изменении col1 в таблице tab1:

create or replace trigger check_col1_trg
    after update of col1 on tab1
    for each row
when (:old.col1 != :new.col1) or
     (:old.col1 is null and :new.col1 is not null) or
     (:old.col1 is not null and :new.col1 is null)
begin
   .....    

end check_col1_trg;
/



Ограничить время исполнения триггера insert периодом с 9 утра до 5 часов вечера можно так:

create or replace trigger check_time_trg
    before insert on tab1
    for each row
when ( to_char(SYSDATE, 'HH24') between 9 and 17 )

begin
   .....    

end check_time_trg;
/


Триггеры DDL

CREATE [OR REPLACE] TRIGGER имя_триггера
{BEFORE | AFTER} {DDL-событие}
ON {SCHEMA | DATABASE}
[WHEN (...)]
DECLARE
 -- variable declarations
BEGIN
  -- trigger code
EXCEPTION
  -- exception handler
END имя_триггера;
/


DDL - события:

BEFORE / AFTER ALTER
BEFORE / AFTER ANALYZE
BEFORE / AFTER ASSOCIATE STATISTICS
BEFORE / AFTER AUDIT
BEFORE / AFTER COMMENT
BEFORE / AFTER CREATE
BEFORE / AFTER DDL
BEFORE / AFTER DISASSOCIATE STATISTICS
BEFORE / AFTER DROP
BEFORE / AFTER GRANT
BEFORE / AFTER NOAUDIT
BEFORE / AFTER RENAME
BEFORE / AFTER REVOKE
BEFORE / AFTER TRUNCATE
AFTER SUSPEND


Пример:

CREATE TABLE tab1(
col1 VARCHAR2(20)NOT NULL,
col2 VARCHAR2(20)NOT NULL,
col3 DATE NOT NULL,
col4 VARCHAR2(20)NOT NULL,
col5 DATE,
col6 VARCHAR2(20)
);


CREATE OR REPLACE TRIGGER after_ddl_creation
AFTER CREATE ON SCHEMA
BEGIN
  INSERT INTO tab1 VALUES
  (SYS.DICTIONARY_OBJ_NAME,SYS.DICTIONARY_OBJ_TYPE,SYSDATE,USER,NULL,NULL);
END;
/


Триггеры событий базы данных

CREATE [OR REPLACE] TRIGGER имя_триггера
{BEFORE | AFTER} {событие базы данных}
ON {SCHEMA | DATABASE}
DECLARE
 -- variable declarations
BEGIN
  -- trigger code
EXCEPTION
  -- exception handler
END имя_триггера;
/

События базы данных:

AFTER STARTUP
BEFORE SHUTDOWN

AFTER LOGON
BEFORE LOGOFF

AFTER DB_ROLE_CHANGE -- for Data Guard failover and switchover
AFTER SUSPEND
AFTER SERVERERROR (does not trap ...

    ORA-01403: no data found (this is in the Oracle docs but does not seem to be correct)
    ORA-01422: exact fetch returns more than requested number of rows
    ORA-01034: ORACLE not available
    ORA-04030: out of process memory when trying to allocate string bytes (string, string)


Пример:

CREATE TABLE tab1 (
col1 DATE,
col2 VARCHAR2(20));

CREATE OR REPLACE PROCEDURE proc1 IS
BEGIN
  INSERT INTO tab1
  (col1, col2)
  VALUES
  (SYSDATE, USER);
END proc1;
/

CREATE OR REPLACE TRIGGER logintrig
AFTER LOGON ON DATABASE
CALL proc1
/


Получить информацию о триггере:

select * from user_triggers;

Активировать триггер:

alter trigger имя_триггера enable;

Активировать все триггеры для конкретной таблицы:

alter table имя_таблицы enable all triggers;

Заблокировать триггер:

alter trigger имя_триггера disable;

Удалить триггер:

drop trigger имя_триггера;

воскресенье, 20 января 2013 г.

Основы ООП в Python



Классы в Python это полноценные объекты,  даже если нет ни одного экземпляра.
Самый простой класс создается так:

>>> class database :
    pass

>>>

Это пустой класс с именем database без атрибутов и без методов.

Создадим класс с одним атрибутом:

>>> class database :
    name = 'oracle'
   
>>>

Теперь мы можем обращаться к этому атрибуту класса за пределами самого класса:

>>> print(database.name)
oracle
>>>

и даже изменять его значение:

>>> database.name = "ora11gR2"
>>> print(database.name)
ora11gR2
>>>

Тоже самое можно сделать и так:

- сначала создаем пустой класс

>>> class database :
    pass

>>>

После создания класса мы можем присоединить к нему атрибуты,
выполняя операции присваивания за пределами класса:

>>> database.name = "oracle"
>>> database.version = "11.2.0.2"
>>>
>>> print(database.name)
oracle
>>> print(database.version)
11.2.0.2
>>>
>>> database.version = "11.2.0.3"
>>> print(database.version)
11.2.0.3
>>>

Пока у нас имеется ссылка на класс, мы можем в любое время
добавлять или изменять его атрибуты по своему усмотрению.

Пусть у нас имеется простой класс с одним атрибутом:

>>> class test:
    name = 'Larry'

   
>>>

Создадим два экземпляра этого класса:

>>> x = test()
>>> y = test()
>>>

так как они помнят класс из которого были созданы,
они по наследству получат атрибуты класса:

>>> x.name, y.name
('Larry', 'Larry')
>>>

Если выполнить присваивание атрибуту экземпляра,
то будет создан (или изменен) атрибут этого конкретного объекта, а не другого.

Атрибуты обнаруживаются в результате поиска по дереву наследования,
но операция присваивания значения атрибуту воздействует только на тот объект,
к которому эта операция применяется.

>>>
>>> x.name = "Tom"
>>>
>>> test.name, x.name, y.name
('Larry', 'Tom', 'Larry')
>>>

Экземпляр x получил свой собственный атрибут name,
а экземпляр y по прежнему наследует атрибут name, присоединенный к классу test.

Добавим в наш простой класс метод setname с помощью которого мы получим возможность
изменять значение атрибута.


>>> class test:
    name = 'Larry'
    def setname(self, value):
        self.name = value

>>>

Создадим экземпляр класса test.

>>> x = test()
>>>

Он также по наследству получит атрибут name.

>>> x.name
'Larry'
>>>

Но теперь мы можем вызвать метод класса setname который изменяет значение атрибута name.
В качестве параметра укажем, атрибут какого экземпляра мы хотим изменить а также новое значение атрибута name.

>>> test.setname(x, "Tom")
>>>

Мы вызвали данный метод, как метод класса test.

test.setname(...)

Но так как наш экземпляр x, также по наследству от класса test получит и метод setname:

def setname(self, value):
    self.name = value

но уже в таком виде:

def setname(x, value):
    x.name = value

тут уже первый параметр - это имя созданного экземпляра.

То теперь проще вызвать данный метод не из класса test, а из созданного экземпляра x:

>>> x.setname("Tom")
>>>

Имя self внутри метода автоматически ссылается на обрабатываемый экземпляр x,
поэтому операция присваивания сохраняет значение в пространстве имен экземпляра а не класса.


В классе test атрибуты можно и не объявлять, а оставить только метод setname.

>>> class test:
    def setname(self, value):
            self.name = value

>>>

Тогда, после создания экземпляра

>>> x = test()
>>>

Никаких атрибутов он от класса не унаследует.
Унаследуется только метод setname.

И только после первого вызова метода setname

>>> x.setname("Larry")
>>>

У нас появится новый атрибут экземпляра с именем name

>>> x.name
'Larry'
>>>

Методы, которые обычно создаются инструкциями def, вложенными в инструкцию class,
могут создаваться совершенно независимо от объекта класса.

Пусть у нас имеется класс:

>>> class test:
    name = 'Larry'
    def setname(self, value):
        self.name = value

>>>

Мы хотим добавить в этот класс еще один метод getname,
который выводит текущее значение атрибута name.

Определим некую функцию вне класа:

def xyz(self):
    print(self.name)

Это обычная функция, она ничего незнает о классе test.
Она может вызываться как обычная функция.

Есть только единственное ограничение:
Объект, который она получает в качестве параметра, должен иметь атрибут name.

(Имя аргумента "self" не имеет никакого особого смысла и может называться как угодно)

Вызовем эту функцию, передав ей в качестве параметра наш ранее созданный объект x.

>>> xyz(x)
Larry
>>>

Однако, если эту функцию присвоить атрибуту нашего класса, она станет методом, вызываемым из любого экземпляра.
(А также через имя самого класса при условии, что функции вручную будет передан экземпляр)

>>> test.getname = xyz

>>> x.getname()
Larry

Вызвать через имя класса можно так:

>>> test.getname(x)
Larry
>>>

В итоге у нас получился такой класс:

class test:

    name = 'Larry'

    def setname(self, value):
        self.name = value

    def getname(self):
        print(self.name)


Определим новый класс testnew, который наследует все имена из класса test и добавляет свои собственные.


>>> class testnew(test):
    def dispname(self):
        print('Current value = "%s"' % self.name)

        # и такой же метод как и в test
    def getname(self):
        print('Value = "%s"' % self.name)
       
>>>


>>> x = test()

>>> z = testnew()

>>> z.setname("Tom")

>>> z.getname()     # вызовется метод из класса testnew
Value = "Tom"

>>> x.getname()    # вызовется метод из класса test
Larry
>>>

Допустим что класс test находится в модуле modtest.py

Мы хотим создать класс testnew который бы унаследовал все имена класса test.

Тогда нужно импортировать этот модуль

from modtest import test
class testnew(test):
...

Или эквивалентный вариант, импортируем весь модуль целиком:

import modtest
class testnew(modtest.test):
...
тут в скобках мы указываем полное имя.


Пусть имеется файл modtest.py и в нем определен класс:

class test:


Чтобы получить доступ к этому классу, нам необходимо обратиться к модулю, как обычно:

import modtest

x = modtest.test()

обращаемся к модулю и классу внутри модуля.

Можно также использовать инструкцию from

from modtest import test

x = test()

тут обращаемся только к классу.

Согласно общепринятым соглашениям, имена классов в языке Python должны начинаться с заглавной буквы,
а имена модулей с прописных.

import  modtest
x = modtest.Test()


Пусть имеется класс:

class MyClass:
    def display(self):
    print('Current value = "%s"' % self.data)

Создадим такой класс:

>>> class MyNewClass(MyClass):
    def __init__(self, value):
        self.data = value
    def __add__(self, other):
        return MyNewClass(self.data + other)
    def __str__(self):
        return 'MyNewClass: ' + self.data
    def mul(self, other):
        self.data *= other

       
>>> a = MyNewClass("abc")

>>> a.display()
Current value = "abc"

>>> print(a)
MyNewClass: abc

>>> b = a + 'xyz'

>>> b.display()
Current value = "abcxyz"

>>> print(b)
MyNewClass: abcxyz

>>> a.mul(3)

>>> print(a)
MyNewClass: abcabcabc

Обратите внимание, что метод __add__ создает и возвращает новый объект экземпляра
этого класса (вызывая MyNewClass, которому передается значение результата)

А метод mul изменяет текущий объект экземпляра выполняя присваивание атрибуту аргумента self.

Обычно встроенные типы, такие как числа и строки, всегда создают новые объекты при выполнении оператора *.


Атрибуты пространства имен.

>>> class test:
    pass

>>>
>>> test.name = "Tom"
>>> test.age = 40
>>>
>>> x = test()
>>> y = test()
>>>
>>> print(x.name)    # Унаследованные атрибуты
Tom
>>> print(y.name)    # Унаследованные атрибуты
Tom
>>>
>>> x.name = "Larry"
>>> print(x.name)     # Экземпляр x получил собственный атрибут
Larry
>>>

Атрибуты пространства имен обычно реализованы в виде словарей.
И деревья наследования классов тоже всего лишь словари со ссылками на другие словари.

Например в большинстве объектов, созданных на базе классов имеется атрибут :

__dict__

Который является словарем пространства имен.

>>> test.__dict__.keys()
dict_keys(['__module__', 'name', 'age', '__dict__', '__weakref__', '__doc__'])
>>>

>>> list(x.__dict__.keys())
['name']
>>>

>>> list(y.__dict__.keys())
[]
>>>

Как видим в словаре класса присутствуют атрибуты name и age, которые созданы ранее.

Объект x имеет свой собственный атрибут name, а объект y по прежнему пуст.

Каждый экземпляр имеет ссылку на свой наследуемый класс, она называется :

__class__

>>> x.__class__

>>>

Классы также имеют атрибут __bases__, который представляет собой картеж его суперклассов:

>>> test.__bases__
(,)
>>>

Эти два атрибута описывают, как деревья классов размещаются в памяти.

Классы и экземпляры - это всего лишь объекты пространства имен
с атрибутами создаваемыми на лету с помощью операции присваивания.

Обычно эти операции присваивания выполняются внутри инструкции class,
но они могут находиться в любом другом месте, где имеется ссылка на один из объектов в дереве.



#Модуль: test1.py

gl1 = 999              # Глобальная переменная модуля

def fn1():             # Имя функции глобально в пределах модуля
    lf1 = 111          # Локальная переменная в функции
    print("-fn1-")
    print("Видна только в функции", lf1)


class Cl1:

    ac1 = 888          # Атрибут класса

    def mt1(self):     # Метод экземпляра
        lm1 = 777      # Локальная переменная в методе
        self.ae1 = 555 # Атрибут экземпляра
        print("-mt1-")
        print("Видна только в методе", lm1)
        print("Видна только в экземпляре", self.ae1)


    def mt2():     # Статический метод
        lm2 = 333      # Локальная переменная в методе
        print("-mt2-")
        print("Видна только в методе", lm2)
    mt2 = staticmethod(mt2)


    def mt3(cls):     # Метод класса
        lm3 = 444      # Локальная переменная в методе
        print("-mt2-")
        print("Видна только в методе", lm3)
    mt3 = classmethod(mt3)


# Глобальная переменная модуля
print(gl1)

# Функция модуля
fn1()

# Атрибут класса
print(Cl1.ac1)

# Обращение к методам экземпляра

em1 = Cl1()      # Создаем ссылку экземпляр класса

Cl1.mt1(em1)     # Обязательно передаем имя экземпляра первым параметром
em1.mt1()        # Или так

Cl1().mt1()      # Ссылку на экземпляр можно и не создавать

# Обращение к статическим методам

Cl1.mt2()        # имя экземпляра первым параметром не передается
em1.mt2()        # имя экземпляра первым параметром не передается


# Обращение к методам класса

Cl1.mt3()        # автоматически передается имя класса первым параметром
em1.mt3()        # автоматически передается имя класса первым параметром




#Модуль: test2.py

Подсчет количества экземпляров класса:


class Cl1:

    ac1 = 0          # Атрибут класса

    def __init__(self):

        self.ae1 = 555 # Атрибут экземпляра
        print("-init-")
        Cl1.ac1 +=1

    def __del__(self):
        print("-del-")
        Cl1.ac1 -=1


    def mt1(self):     # Метод экземпляра
        print(Cl1.ac1)


ex1 = Cl1()
ex1.mt1()

ex2 = Cl1()
ex3 = Cl1()

ex1.mt1()

del ex2
del ex3

ex1.mt1()











среда, 16 января 2013 г.

Java Update

На металинке говорится что доступен Java SE TZUpdater.

Java Time Zone Updater Tool tzupdater quits with "There's no tzdata available for this Java runtime" [ID 1330586.1]

Сам Java SE TZUpdater скачиваем по ссылке:

http://www.oracle.com/technetwork/java/javase/downloads/tzupdater-download-513681.html


Заходим на сервер egar-app1.msk.vbrr.loc
su - oracle


Java SE TZUpdater уже загружен и разархивирован и на сервере egar-app1.msk.vbrr.loc находится по пути:

/home/oracle/tzupdater-1.3.42-2011k/tzupdater.jar


$ ls -l /home/oracle/tzupdater-1.3.42-2011k/tzupdater.jar
-rw-rw-r--  1 oracle oinstall 472738 Oct  4 21:02 /home/oracle/tzupdater-1.3.42-2011k/tzupdater.jar
$

В ORACLE_HOME ( /opt/oracle/ora_j2ee ) у нас имеется JDK и JRE

/opt/oracle/ora_j2ee/jdk/bin/
/opt/oracle/ora_j2ee/jre/1.4.2/bin/


Смотрим текущую  time zone data version  для наших  JDK и JRE

$cd /opt/oracle/ora_j2ee/jdk/bin/
$./java -jar /home/oracle/tzupdater-1.3.42-2011k/tzupdater.jar -V
tzupdater version 1.3.42-b02
JRE time zone data version: tzdata2003a
Embedded time zone data version: tzdata2011k

cd /opt/oracle/ora_j2ee/jre/1.4.2/bin/
$./java -jar /home/oracle/tzupdater-1.3.42-2011k/tzupdater.jar -V
tzupdater version 1.3.42-b02
JRE time zone data version: tzdata2003a
Embedded time zone data version: tzdata2011k


Другой способ посмотреть текущую  time zone data version  для наших  JDK и JRE

$ /usr/bin/od -c -j 11 -N 11 /opt/oracle/ora_j2ee/jre/1.4.2/lib/zi/ZoneInfoMappings
0000013   t   z   d   a   t   a   2   0   0   3   a
0000026
$

$ /usr/bin/od -c -j 11 -N 11 /opt/oracle/ora_j2ee/jdk/jre/lib/zi/ZoneInfoMappings
0000013   t   z   d   a   t   a   2   0   0   3   a
0000026
$


Перед патчем останавливаем сервер приложений:

cd /home/oracle/bin/pkg/ias_cold_backup
./stop_app_all.sh


Установка Java SE TZUpdater

$cd /opt/oracle/ora_j2ee/jdk/bin/

$ ./java -jar /home/oracle/tzupdater-1.3.42-2011k/tzupdater.jar -u -v
java.home: /opt/oracle/ora_j2ee/jdk/jre
java.vendor: Sun Microsystems Inc.
java.version: 1.4.2_06
JRE time zone data version: tzdata2003a
Embedded time zone data version: tzdata2011k
Extracting files... done.
Renaming directories... done.
Validating the new time zone data... done.
Time zone data update is complete.
$

cd /opt/oracle/ora_j2ee/jre/1.4.2/bin/

$ ./java -jar /home/oracle/tzupdater-1.3.42-2011k/tzupdater.jar -u -v
java.home: /opt/oracle/ora_j2ee/jdk/jre
java.vendor: Sun Microsystems Inc.
java.version: 1.4.2_06
JRE time zone data version: tzdata2003a
Embedded time zone data version: tzdata2011k
Extracting files... done.
Renaming directories... done.
Validating the new time zone data... done.
Time zone data update is complete.
$

Проверяем time zone data version  после установки Java SE TZUpdater для наших  JDK и JRE:

$cd /opt/oracle/ora_j2ee/jdk/bin/
$./java -jar /home/oracle/tzupdater-1.3.42-2011k/tzupdater.jar -V
tzupdater version 1.3.42-b02
JRE time zone data version: tzdata2011k
Embedded time zone data version: tzdata2011k

cd /opt/oracle/ora_j2ee/jre/1.4.2/bin/
$./java -jar /home/oracle/tzupdater-1.3.42-2011k/tzupdater.jar -V
tzupdater version 1.3.42-b02
JRE time zone data version: tzdata2011k
Embedded time zone data version: tzdata2011k


Другой способ посмотреть time zone data version  для наших  JDK и JRE

$ /usr/bin/od -c -j 11 -N 11 /opt/oracle/ora_j2ee/jre/1.4.2/lib/zi/ZoneInfoMappings

0000013   t   z   d   a   t   a   2   0   1   1   k
0000026
$


$ /usr/bin/od -c -j 11 -N 11 /opt/oracle/ora_j2ee/jdk/jre/lib/zi/ZoneInfoMappings

0000013   t   z   d   a   t   a   2   0   1   1   k
0000026
$


После установки Java SE TZUpdater запускаем сервер приложений:

cd /home/oracle/bin/pkg/ias_cold_backup
./start_app_all.sh


вторник, 15 января 2013 г.

Пакеты в Oracle

Процедуры и функции можно группировать в пакеты.
Пакеты инкапсулируют связанные функциональности в один автономный модуль.

Пакет состоит из двух компонентов:
- спецификации
- тела

Спецификация перечисляет все имеющиеся в пакете процедуры, функции, типы и объекты.
Можно сделать их доступными для всех пользователей у которых есть доступ к пакету.
Самого кода процедур и функций спецификация не содержит.

Тело пакета содержит сам код процедур и функций объявленных в спецификации.
Любая процедура или функция, содержащаяся в теле пакета и не упомянутая в спецификации,
будет доступна только внутри тела пакета (скрыта от внешнего мира).

Создание спецификации пакета:

create [or replace] package имя_пакета
{is | as}
спецификация_пакета
end имя_пакета;


Пример создания спецификации пакета:

create package my_package as

TYPE t_cur IS REF CURSOR;

FUNCTION get_val RETURN t_cur;

procedure update_tab_col(
par1 in tab.col1%type,
par2 in number
);

end my_package;
/


Создание тела пакета:

create [or replace] package body имя_пакета
{is | as}
тело_пакета
end имя_пакета;


Пример создания тела пакета:

create package body my_package as


FUNCTION get_val RETURN t_cur IS
cur t_cur;

BEGIN

OPEN cur FOR
SELECT col1, col2, col3  FROM tab;
return cur;

END get_val;



PROCEDURE update_tab_col(
par1 in tab.col1%type,
par2 in number
) as

var1 integer;

BEGIN

select count(*)
into var1
from tab
where col1 = par1;

if var1 = 1 then
  update tab
  set col2 = col2 * par2
  where col1 = par1;
  commit;
end if;

exception
when others then
rollback;

END update_tab_col;


end  my_package;
/


Вызов процедур и функций в пакете:

select my_package.get_val from dual;
call my_package.update_tab_col(7,  1.5);


Получить информацию о процедуре или функции из пакета можно так:

select * from user_procedures where object_name = 'MY_PACKAGE';

Удаление пакета:

DROP PACKAGE  my_package;

Функции в Oracle

create [or replace] function имя_функции
[(имя_параметра [ IN | OUT | IN OUT ]  тип [, ... ])]
{is | as}

begin
 тело_функции
end имя_функции;

in -режим по умолчанию
    входной параметр
    (параметр который к моменту выполнения уже имеет значение
     и это значение не может измениться в теле функции)

out -используется для параметров,
     значения которых устанавливаются только в теле функции.

in out -используется для параметров,
        которые могут иметь значения к моменту вызова функции,
        но эти значения могут быть изменены в теле функции.


create function func1 (
par1 in number
) return number as

var1 number := 10;
var2 number;

begin

var2 := var1 * par1;
return var2;

end func1;
/



create avg_tab_col (
par1 in integer
) return number as

var1 number;

begin

select avg(col2)
into var1
from tab
where col1 = par1;

return var1;

end avg_tab_col;
/


Вызываются функции так:

select func1(15) from dual;


select func1(par1 => 15) from dual;


select avg_tab_col(20) from dual;


Получить информацию о функции можно так:

select * from user_procedures where object_name in ('FUNC1, 'AVG_TAB_COL');


Удаление функции:

drop function  func1();

Процедуры в Oracle

create [or replace] procedure имя_процедуры
[(имя_параметра [ IN | OUT | IN OUT ]  тип [, ... ])]
{is | as}

begin
 тело_процедуры
end имя_процедуры;



in -режим по умолчанию
    входной параметр
    (параметр который к моменту выполнения уже имеет значение
     и это значение не может измениться в теле процедуры)

out -используется для параметров,
     значения которых устанавливаются только в теле процедуры.

in out -используется для параметров,
        которые могут иметь значения к моменту вызова процедуры,
        но эти значения могут быть изменены в теле процедуры.


create procedure update_tab_col(
par1 in tab.col1%type,
par2 in number
) as

var1 integer;

begin

select count(*)
into var1
from tab
where col1 = par1;

if var1 = 1 then
  update tab
  set col2 = col2 * par2
  where col1 = par1;
  commit;
end if;

exception
when others then
rollback;

end update_tab_col;
/


Вызвать данную процедуру можно так:

позиционная запись:
(для обязательных параметров)

call update_tab_col( 5, 10);

поименная запись:
(для необязятельных параметров)

call update_tab_col( par2 => 10, par1 => 5);

смешанная запись:
(начинается с позиционного набора)

call update_tab_col( 5, par2 => 10);


Получить информацию о процедуре можно так:

select * from user_procedures where object_name = 'UPDATE_TAB_COL';


Удаление процедуры:

drop procedure  update_tab_col;

понедельник, 14 января 2013 г.

Курсоры в Oracle

Курсор это средство извлечения данных из БД.
Курсоры содержат определения столбцов и объектов.
Курсоры используются для получения строк, возвращаемых запросом.
Для передачи строк в курсор используется запрос, после чего вы можете
выбирать строки из курсора по одной за один раз.
 

create table t1(id, type, text)
as
select object_id, object_type, object_name
from all_objects;

или так

create table t1
as
select object_id id, object_type type, object_name text
from all_objects;


select id, type, text from t1
where id =17367

17367    SCHEDULE    FILE_WATCHER_SCHEDULE



select id, type, text from t1
where type = 'SCHEDULE';

17364    SCHEDULE    DAILY_PURGE_SCHEDULE
17367    SCHEDULE    FILE_WATCHER_SCHEDULE
17372    SCHEDULE    PMO_DEFERRED_GIDX_MAINT_SCHED
18172    SCHEDULE    BSLN_MAINTAIN_STATS_SCHED



Неявные курсоры определяются в момент выполнения:

DECLARE
    v_text t1.text%TYPE;

BEGIN
    SELECT text INTO v_text
    FROM t1
    WHERE id = 17367;
    DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );
END;
/


text = FILE_WATCHER_SCHEDULE

В ходе выполнения кода создается курсор для выборки значения text.



Явный курсор определяется до начала выполнения:

DECLARE
    CURSOR c_get_text
    IS
    SELECT text
    FROM t1
    WHERE id = 17367;

    v_text t1.text%TYPE;

BEGIN
    OPEN c_get_text;
    FETCH c_get_text INTO v_text;
    DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );
    CLOSE c_get_text;
END;
/

text = FILE_WATCHER_SCHEDULE



Преимущество явного курсора заключается в наличии у него атрибутов,
облегчающих применение условных операторов.



CREATE OR REPLACE PROCEDURE proc1
AS
    CURSOR c_get_text
    IS
    SELECT text
    FROM t1
    WHERE id = 17367;

    v_text t1.text%TYPE;

BEGIN
    OPEN c_get_text;
    FETCH c_get_text INTO v_text;
    IF  c_get_text%NOTFOUND THEN
        DBMS_OUTPUT.PUT_LINE( 'Данные не найдены. ' );
    ELSE
        DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );
    END IF;
    CLOSE c_get_text;
END;
/

BEGIN
    proc1;
END;
/

text = FILE_WATCHER_SCHEDULE


А как подобное сделать с неявным курсором:


CREATE OR REPLACE PROCEDURE proc2
AS
    v_text t1.text%TYPE;
    v_bool BOOLEAN := TRUE;

BEGIN
    BEGIN
        SELECT text INTO v_text
        FROM t1
        WHERE id = 17367;

    EXCEPTION
        WHEN no_data_found THEN
            v_bool := FALSE;
        WHEN others THEN
            RAISE;
    END;

    IF NOT v_bool THEN
       DBMS_OUTPUT.PUT_LINE( 'Данные не найдены. ' );
    ELSE
       DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );
    END IF;
END;
/


BEGIN
    proc2;
END;
/


text = FILE_WATCHER_SCHEDULE


Пришлось заниматься перехватом исключений, чтобы определить, была ли найдена запись.


Параметризация курсоров помогает повысить степень их повторного использования.

курсор с параметром:

DECLARE
    CURSOR c_get_text(par1 NUMBER)
    IS
    SELECT text
    FROM t1
    WHERE id = par1;

    v_text t1.text%TYPE;

BEGIN
    OPEN c_get_text(17367);
    FETCH c_get_text INTO v_text;
    DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );
    CLOSE c_get_text;
END;
/

text = FILE_WATCHER_SCHEDULE



Переменные типа REF CURSOR могут ссылаться на любые реальные курсоры.
Программа, использующая тип REF CURSOR, может работать с курсорами,
не заботясь о том, какие конкретно данные будут извлечены ими во время выполнения.


CREATE OR REPLACE PROCEDURE proc_ref
AS
    v_curs SYS_REFCURSOR;
    v_text t1.text%TYPE;

BEGIN
    OPEN v_curs
    FOR
    'SELECT text '
    || 'FROM t1 '            
    || 'WHERE id = 17367';

    FETCH v_curs INTO v_text;

    DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );

    CLOSE v_curs;
END;
/


BEGIN
    proc_ref;
END;
/


text = FILE_WATCHER_SCHEDULE


Во время компиляции Oracle не знает, каким будет тексе запроса, - он видит строковую переменную.
Но наличие типа REF CURSOR говорит ему о том, что надо будет обеспечить некую работу с курсором.


Например я могу создать функцию, которая принимает некий входной параметр, создает курсор и возвращает тип REF CURSOR :


CREATE OR REPLACE FUNCTION func1(par1 NUMBER)
RETURN SYS_REFCURSOR
IS
    v_curs SYS_REFCURSOR;

BEGIN
    OPEN v_curs
    FOR
    'SELECT text '
    || 'FROM t1 '            
    || 'WHERE id = '
    || par1;

    RETURN v_curs;
END;
/




Другой пользователь может воспользоваться этой функцией так:

DECLARE

    v_curs SYS_REFCURSOR;
    v_text t1.text%TYPE;

BEGIN
    v_curs := func1(17367);

    FETCH v_curs INTO v_text;

    IF  v_curs%NOTFOUND THEN
        DBMS_OUTPUT.PUT_LINE( 'Данные не найдены. ' );
    ELSE
        DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );
    END IF;

    CLOSE v_curs;
END;
/

text = FILE_WATCHER_SCHEDULE



Для пользователя, вызывающего функцию  func1(), она для него представляет черный ящик, возвращающий курсор.



Сильнотипизированный и слаботипизированный REF CURSOR.

TYPE имя_типа_курсора IS REF CURSOR [ RETURN возвращаемый_тип ];


например:


TYPE refcursor IS REF CURSOR RETURN table1%ROWTYPE;

TYPE refcursor IS REF CURSOR;

Первая форма  REF CURSOR называется сильно типизированной, поскольку тип структуры,
возвращаемый курсорной переменной, задается в момент объявления
(непосредственно или путем привязки к типу строки таблицы).

Вторая форма (без предложения RETURN) называется слаботипизированной.
Тип возвращаемой структуры данных для нее не задается.
Такая курсорная переменная обладает большей гибкостью, поскольку для нее можно задавать любые запросы
с любой структурой возвращаемых данных.

В Oracle 9i появился предопределенный слабый тип REF CURSOR с именем SYS_REFCURSOR,
теперь можно не определять собственный слабый тип, достаточно использовать стандартный тип Oracle:

DECLARE
    my_cursor SYS_REFCURSOR;




Пример сильнотипизированного курсора:


DECLARE

    TYPE my_type_rec IS RECORD (text t1.text%TYPE);
    TYPE my_type_cur IS REF CURSOR RETURN my_type_rec;
    v_curs my_type_cur;
    v_text t1.text%TYPE;

BEGIN
    OPEN v_curs
    FOR
    SELECT text
     FROM t1            
     WHERE id = 17367;

    FETCH v_curs INTO v_text;

    DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );

    CLOSE v_curs;
END;
/

text = FILE_WATCHER_SCHEDULE


или так:

DECLARE

    TYPE my_type_cur IS REF CURSOR RETURN t1%ROWTYPE;
    v_curs  my_type_cur;
    v_var   t1%ROWTYPE;

BEGIN
    OPEN v_curs
    FOR
    SELECT *
     FROM t1            
     WHERE id = 17367;

    FETCH v_curs INTO v_var;

    DBMS_OUTPUT.PUT_LINE( 'id = ' || v_var.id || ', type = ' || v_var.type || ', text = ' || v_var.text );

    CLOSE v_curs;
END;
/


id = 17367, type = SCHEDULE, text = FILE_WATCHER_SCHEDULE





Пример слаботипизированного курсора:


DECLARE

    TYPE my_type_cur IS REF CURSOR;
    v_curs my_type_cur;
    v_text t1.text%TYPE;

BEGIN
    OPEN v_curs
    FOR
    SELECT text
     FROM t1            
     WHERE id = 17367;

    FETCH v_curs INTO v_text;

    DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );

    CLOSE v_curs;
END;
/


text = FILE_WATCHER_SCHEDULE


или так:

DECLARE

    v_curs SYS_REFCURSOR;
    v_text t1.text%TYPE;

BEGIN
    OPEN v_curs
    FOR
    SELECT text
     FROM t1            
     WHERE id = 17367;

    FETCH v_curs INTO v_text;

    DBMS_OUTPUT.PUT_LINE( 'text = ' || v_text );

    CLOSE v_curs;
END;
/

text = FILE_WATCHER_SCHEDULE



Курсор можно передавать в качестве параметра:

1. Функция принимающая курсор

CREATE OR REPLACE FUNCTION get_cursor(p_curs SYS_REFCURSOR)
RETURN VARCHAR2
IS
    v_text t1.text%TYPE;

BEGIN

    FETCH p_curs INTO v_text;

    IF  p_curs%NOTFOUND THEN
        DBMS_OUTPUT.PUT_LINE( 'Данные не найдены. ' );
    ELSE
        DBMS_OUTPUT.PUT_LINE( 'Данные найдены. ' );
    END IF;

    RETURN v_text;
END;
/


2. Процедура принимающая текст SQL

CREATE OR REPLACE PROCEDURE get_sql (p_sql VARCHAR2)
IS
    v_curs SYS_REFCURSOR;
    v_res  VARCHAR2(50);
BEGIN
    IF v_curs%ISOPEN THEN
        CLOSE v_curs;
    END IF;
    BEGIN
        OPEN v_curs FOR p_sql;
    EXCEPTION
        WHEN OTHERS THEN
              RAISE_APPLICATION_ERROR(-20000, 'Unable to open cursor');
    END;
    v_res := get_cursor(v_curs);
    CLOSE v_curs;
    DBMS_OUTPUT.PUT_LINE(v_res);
END;
/


Запускаем так:

BEGIN
    get_sql( 'SELECT text FROM t1 WHERE id = 17367' );
END;
/

Данные найдены.
FILE_WATCHER_SCHEDULE


Ещё примеры:

SET SERVEROUTPUT ON

DECLARE

  -- Объявляем переменые

  var1      tab.col1%TYPE;
  var2      tab.col2%TYPE;
  var3      tab.col3%TYPE;

  -- Объявляем курсор

  CURSOR cur IS
    SELECT col1, col1, col3
    FROM tab
    ORDER BY col1;

BEGIN
  -- Открываем курсор

  OPEN cur;

  LOOP
    -- Выбираем из курсора строки
    FETCH cur
    INTO var1, var2, var3;

    EXIT WHEN cur%NOTFOUND;

    -- Выводим значения переменных
    DBMS_OUTPUT.PUT_LINE( 'col1 = ' || var1 || ', col2 = ' || var2 || ', col3 = ' || var3 );
  END LOOP;

  -- Закрываем курсор
  CLOSE cur;
END;
/



Курсоры и цикл FOR

Для получения доступа к строкам из курсора можно использовать цикл FOR.
При использовании цикла FOR не нужно явно открывать курсор - цикл FOR сделает это автоматически.


SET SERVEROUTPUT ON

DECLARE

  CURSOR cur IS
    SELECT col1, col1, col3
    FROM tab
    ORDER BY col1;

BEGIN
  FOR var IN cur LOOP
    DBMS_OUTPUT.PUT_LINE( 'col1 = ' || var.col1 || ', col2 = ' || var.col2 || ', col3 = ' || var.col3 );
  END LOOP;
END;
/



Выражение OPEN - FOR

С курсором можно использовать выражение OPEN - FOR, которое добавляет еще больше гибкости при обработке курсоров,
поскольку вы можете назначить курсор для другого запроса.
Запрос может быть любым корректным выражением SELECT.
Это означает что вы можете повторно использовать курсор и назначить курсору позже в коде другой запрос.

SET SERVEROUTPUT ON

DECLARE

  -- Определим тип REF CURSOR
  TYPE t_cur IS
  REF CURSOR RETURN tab%ROWTYPE;


  -- Определим объект типа  t_cur
  cur t_cur;

  -- Определим объект для хранения столбцов из таблицы tab
  var tab%ROWTYPE;

BEGIN
  -- назначим запрос для объекта cur и откроем его
  OPEN cur FOR
  SELECT * FROM tab WHERE col1 < 5;

  -- Выбираем строки из cur в var
  LOOP
    FETCH cur INTO var;
    EXIT WHEN cur%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE( 'col1 = ' || var.col1 || ', col2 = ' || var.col2 || ', col3 = ' || var.col3 );
  END LOOP;

  -- Закрываем объект cur
  CLOSE cur;
END;
/




Все ранее рассмотренные курсоры имели конкретный возвращаемый тип, который должен совпадать
со столбцами в запросе исполняемом курсором.
Можно определить курсор, который не имеет возвращаемого типа и может исполнять любой запрос.

SET SERVEROUTPUT ON

DECLARE
  -- Определим тип REF CURSOR
  TYPE t_cur IS REF CURSOR;

  -- Определим объект типа  t_cur
  cur t_cur;

  -- Определим объект для хранения столбцов из таблицы tab1
  var1 tab1%ROWTYPE;

 -- Определим объект для хранения столбцов из таблицы tab2
  var2 tab2%ROWTYPE;

BEGIN
  -- назначим запрос для объекта cur и откроем его
  OPEN cur FOR
  SELECT * FROM tab1 WHERE col1 < 5;

  -- Выбираем строки из cur в var1
  LOOP
    FETCH cur INTO var1;
    EXIT WHEN cur%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE( 'col1 = ' || var1.col1 || ', col2 = ' || var1.col2 || ', col3 = ' || var1.col3 );
  END LOOP;

  -- назначим новый запрос для объекта cur и откроем его
  OPEN cur FOR
  SELECT * FROM tab2 WHERE col1 < 3;

  -- Выбираем строки из cur в var2
  LOOP
    FETCH cur INTO var2;
    EXIT WHEN cur%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE( 'col1 = ' || var2.col1 || ', col2 = ' || var2.col2 || ', col3 = ' || var2.col3 );
  END LOOP;

  -- Закрываем объект cur
  CLOSE cur;
END;
/