PL SQL - DmitryGontarenko/usefultricks GitHub Wiki

ABOUT

PL/SQL - это процедурный язык, разработанный компанией Oracle, который явялется расширением стандартного SQL.

В PL/SQL все символы приводятся к верхнему регистру, поэтому объявленные переменные в примере ниже - будут идентичны.

a varchar2(255);
A varchar2(255);

Специальные символы:
Большенство символов (арифметических и логических), схожи со стандартными. Рассмотрим особенные для PL/SQL:
:= - присвоение;
|| - конкатенация строк;

Базовой единицей языка PL/SQL является блок и он имеет три раздела - Declaration (объявления), Body (тело) и Exception (исключения). Но использовать их всегда вместе - необязательно.

declare
	-- объявления
begin
	-- выполняемый код
exception
	-- обработка исключений
end;
/ -- символ завершения для запуска блока на компиляцию

Пример:

set serveroutput on
begin
   dbms_output.put_line('Hello World!');
end;
/

Результат:

Hello World!
PL/SQL procedure successfully completed.

Рассмотрим подробнее:
begin и end - это обрамляющий программный блок.
Строка set serveroutput on объявляется перед началом сеанса и говорит о том, что в консоль надо выводить все значения из dbms_output.
Символ / запускает скрипт на исполнение.

DECLARE

Рассмотрим пример с блоком declare:

declare
    a integer := 2;
    b integer;
begin
    b := 3;
    dbms_output.put_line(a+b); -- result: 5
end;
/

Мы объявили две переменные целочисленного типа в блоке declare. Переменную a мы инициализировали сразу, а переменной b присвоили значения уже в теле. И как результат мы вывели сумму двух значений этих переменных.

Но при выводе чисел в консоль произошло неявное преобразование типа - из числа в строку. Что бы преобразовать явно, можно воспользоваться методом to_char():

    dbms_output.put_line(TO_CHAR(a+b));

Так же можно преобразовать и строку в число, воспользовавшись методом to_number():

    dbms_output.put_line(TO_CHAR(TO_NUMBER(inn)+a+b));

Рассмотрим некоторые особенности при инициализации переменных:

declare
    age integer := 123; -- простоая переменная integer (подтип number)
    price number(4,2) := 12.34; -- number с плавающей точкой (максимум 4 знака и 2 после запятой).
    inn constant number := 12345; -- константа, в данном случае типа number
    name varchar2(255) := 'Sarah'; -- строковая переменная, может принимать NULL
    address varchar2(255) not null := 'Moscow'; -- строковая переменная, с проверкой на NULL
    country varchar2(50) default 'Russia'; -- строковая переменная со значением по умолчанию
    isOpen boolean not null := true; -- логическая переменная
begin
    DBMS_OUTPUT.put_line(TO_CHAR(price)); -- 12,34
    DBMS_OUTPUT.put_line(TO_CHAR(age)); -- 123
    DBMS_OUTPUT.put_line(TO_CHAR(inn)); -- 12345
    DBMS_OUTPUT.put_line(name); -- Sarah
    DBMS_OUTPUT.put_line(address); -- Moscow
    DBMS_OUTPUT.put_line(country); -- Russia
    -- переменную типа boolean нельзя вывести в консоли
end;
/

Также для переменных в PL/SQL можно устанавливать тип поля таблицы.
Например, у нас есть таблица Developer, которая содержит поле name varchar2(15).
Мы хотим использовать значения этого поля в теле функции, для этого мы должны создать переменную, такого же типа, что и поле.

declare
    d_name varchar2(15);
begin
    select name into d_name from developer where name = 'Sarah';
    dbms_output.put_line(d_name); -- Sarah
end;
/

Но! если тип поля name в исходной таблице изменится, например станет name varchar(50), мы уже не сможем присваивать значения этого поля для нашей переменной d_name, т.к. количество допустимых символов будет различаться.
В этом случае нам поможет конструкция %type, она имеет вид variable table.field%type.
Применим ее для нашего скрипта:

declare
    d_name developer.name%type;
begin
    select name into d_name from developer where name = 'Sarah';
    dbms_output.put_line(d_name); -- Sarah
end;
/

Теперь тип нашей переменной d_name будет всегда привязан к типу поля name.

SUBTYPE

В PS/SQL можно создавать подтипы, то есть типы, основанные на других. Такие типы, например, как integer и decimal основаны на типе number.
Определение подтипа имеет вид subtype new_type is type.
Рассмотрим это на примере:

declare
    subtype name_type is developer.name%type; -- создали новый тип name_type на основе типа name
    d_name name_type(15); -- создали переменную на основе нового типа и ограничили количество допустимых символов
begin
    select name into d_name from developer where name = 'Sarah';
    dbms_output.put_line(TO_CHAR(d_name));
end;
/

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

IF-THEN-ELSE

Рассмотрим условные операторы if-then-else.
Они имеют общий вид:

    if(condition) then -- первое условие
    elsif(condition) then -- второе условие
    else -- ни одной из условий не выполнилось
    end if; -- конец условного оператора

При втором и последующем условии используется стандартный оператор elsif, не путать с elseif.

Рассмотрим на примере и реализуем простейший калькулятор:

declare
    a integer := 5;
    b integer := 5;
    operation varchar2(1) := '+';
begin
    if(operation = '+') then
        dbms_output.put_line('Result is '||TO_CHAR(a+b)); -- Result is 10
    elsif (operation = '-') then
        dbms_output.put_line('Result is '||TO_CHAR(a-b)); -- Result is 0
    else
        dbms_output.put_line('Требуемой операции не найдено');
    end if;
end;
/

Операция с числами в данном случае получается "зашитой" в скрипте. Изменим инициализацию операции таким образом, что бы операцию можно было выбирать каждый раз при запуске программы. Это можно сделать с помощью использования литерала &:

    operation varchar2(1) := '&Operation';

Теперь, каждый раз при запуске программы - будет появляется окно, с просьбой инициализировать операцию (Enter value for Operation).

LOOP

PL/SQL имеет три вида циклов:

  • Безусловные циклы (выполняемые бесконечно);
  • Интерактивные циклы (FOR);
  • Условные циклы (WHILE).

Для выхода из цикла используются три оператора:

  • EXIT - Безусловный выход из цикла. Можно использовать с условным оператором IF;
  • EXIT WHEN - Выход при выполнении условия;
  • GOTO - Выход из цикла во внешний контекст.

Для начала рассмотрим безусловный цикл:

declare
    i integer := 0;
begin
    loop
        i := i + 1;
        dbms_output.put_line(TO_CHAR(i)); -- 1 2 3 4 5
        if (i >= 5) then
            exit; -- используем безусловный выход из цикла
        end if;
    end loop;

    loop
        i := i + 1;
        dbms_output.put_line(TO_CHAR(i)); -- 6 7 8 9 10
        exit when (i >= 10); -- используем выход из цикла с условием
    end loop;
end;
/

Условный цикл while:

declare
    i integer := 0;
begin
    while (i < 5) loop
        i := i + 1;
        dbms_output.put_line(TO_CHAR(i)); -- 1 2 3 4 5
    end loop;
end;
/

Интерактивный цикл for:

declare
    i integer := 0;
begin
    for i in 1..5 loop
        dbms_output.put_line(TO_CHAR(i)); -- 1 2 3 4 5
    end loop;
end;
/

С помощью оператора reverse, в цикле for можно задать обратный порядок:

    for i in reverse 1..5 loop
        dbms_output.put_line(TO_CHAR(i)); -- 5 4 3 2 1
    end loop;

CURSOR

Курсор - это ссылка на конкретную область памяти, в которой хранится информация о результирующем наборе оператора SELECT или другого DML оператора (INSERT, UPDATE, DELETE, MERGE).

Курсоры бывают:

  • явными - запрос объявляется как явный курсор (обычно в разделе declare). После этого такой курсор можно открыть и выбирать нужные из него данные;
  • неявными - команда select .. into считывает одну строку данных и присваивает ее в качества значения локальной переменной программы.

Основные методы работы с курсором:

  • Объявить курсор - cursor get_data is select * from table;
  • Открыть курсор - open get_data;
  • Выбрать данные - fetch get_data into variable;
  • Закрыть курсор - close get_data;

Пример:

declare
    v_code developer.code%type;
    cursor get_code is select code from developer; -- создаем курсор
begin
    open get_code; -- открываем курсор
    fetch get_code into v_code; -- инициализируем переменную данными из курсора
    dbms_output.put_line(v_code);
    close get_code; -- закрываем курсор
end;
/

Курсорс может содержать больее одного результата запроса, но fetch будет возвращать только одну запись, при этом каждый вызов fetch будет возвращать новую запись, скажем так "двигать указатель".
Что бы решить эту проблему и не вызывать множество раз fetch - нужно использовать циклы.

Используем безусловный цикл:

declare
    cursor get_developer is select * from developer; -- создаем курсор
    v_developer get_developer%rowtype;
begin
    open get_developer; -- открываем курсор
    
    loop
        exit when get_developer%notfound; -- проверяем курсор на наличие значений
        fetch get_developer into v_developer; -- инициализируем переменную данными из курсора
        dbms_output.put_line(v_developer.code||' - '||v_developer.name);
    end loop;
    close get_developer; -- закрываем курсор
end;
/

В этом использован атрибут %rowtype - он позволяет хранить все типы полей конкретной таблицы в переменной.

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

declare
    cursor get_developer_for_name(d_name varchar2) is 
        select name from developer where name = d_name; -- создаем курсор
begin
    for i in get_developer_for_name('Sarah') loop
        dbms_output.put_line('Developer`s name ' ||i.name);
    end loop;
end;
/

При работе с циклом for он автоматически открывает и закрывает курсор, поэтому делать это вручную не надо.

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

begin
    for i in (select name from developer where name = 'Sarah') loop
        dbms_output.put_line('Developer`s name ' ||i.name);
    end loop;
end;
/

Атрибуты курсора.
Курсоры могут содержать некоторые атрибуты:

  • %rowtype - применяется для типизирования переменных (рассмотрен в примере выше);
  • %rowcount - возвращает число строк считанных курсором.

Возвращают логическое значение:

  • %found - проверяет, может ли fetch вернуть какие-либо данные;
  • %notfound - противоположная %found команда (рассмотрен в примере выше);
  • %isopen - возвращает true, если курсор открыт.

Атрибуты неявного курсора.
Любой DML оператор представляет собой неявный курсор и выполняется в пределах конкретной области. Неявные курсоры не надо открывать или закрывать вручную, PL/SQL делает это автоматически.
При всем этом, для неявных курсоров все равно можно использовать атрибуты - sql%[attribute].
Состав атрибутов у неявных курсоров такой же, как и у явных.

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

begin
    update developer set name = 'John' where id = 141319;
    
    if (sql%notfound) then
        insert into developer (id, name) values (141319, 'John');
    end if;
end;
/

В данном случае сработал оператор %notfound, т.к. SQL-операция ничего не нашла по заданному условию, а затем произошла вставка данных (такая конструкция немного напоминает работу оператора merge).

RECORD

RECORD - составной тип данных.
Синтаксис объявления составного типа:

type [name] is record (
   [field_1] [type] [not null] [:= data],
   [field_2] [type] [not null] [:= data],
   [field_n] [type] [not null] [:= data]
);

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

declare
    type record_address is record ( -- составной тип
        country varchar2(255) not null := 'Russia',
        city varchar2(255)
    );
    v_address record_address; -- переменная типа record_address
begin
    v_address.city := 'st. Petersburg'; -- инициализируем один из элементов составного типа
    dbms_output.put_line(v_address.country||' - '||v_address.city); -- Russia - st. Petersburg
end;
/

Присваивать значения для данных типа record можно как и при объявлении самого составного типа, так и в теле функции.
Например, присвоить значения можно также через оператор select, сделав запрос из реальной таблицы:

declare
    type record_address is record ( -- составной тип
        country address.country%type,
        city  address.city%type
    );
    v_address record_address; -- переменная типа record_address
begin
    select country, city 
    into v_address 
    from address where name = 'John';
end;
/

TABLE

TABLE в PS/SQL - это упорядоченный набор элементов одного типа. Каждый элемент имеет уникальный порядковый номер, который определяет его позицию в упорядоченной коллекции.
Синтаксис - type [name] is table of [type] index by binary_integer;.

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

declare
    type table_address is table of varchar2(255) -- таблица типа varchar2
        index by binary_integer;
    
    type table_developer is table of developer%rowtype -- таблица типа developer
        index by binary_integer;
    
    v_address table_address; -- переменная типа table_address
    v_developer table_developer;  -- переменная типа table_developer
begin
    v_address(1) := 'London'; -- задаем значение элементу с индексом 1
    select * into v_developer(5) from developer where name = 'Kate';  -- задаем значение элементу с индексом 5
    
    dbms_output.put_line(v_address(1)); -- London
    dbms_output.put_line(v_developer(5).name||' - '||v_developer(5).age); -- Kate - 28
end;
/

Также table может содержать разные атрибуты:

  • count - возвращает число строк таблицы - table.count;
  • delete(n, m) - удаляет строки в таблицы - table.delete(1);
  • exists(n) - возвращает true, если указанный элемент находится в таблице - table.exists(1);
  • first и last - возвращает индекс первой или последний строки - table.last;
  • next(n) и prior(n) - возвращают индекс строки таблицы, которая следует или предшествует строке - table.next(1).

FUNCTIONS

В PL/SQL используется множество разных функций, с полным списком которых можно ознакомиться в официальной документации.
Рассмотрим основные из них:

    -- функции строки
    dbms_output.put_line(lower('Hello world')); -- hello world
    dbms_output.put_line(upper('Hello world')); -- HELLO WORLD
    dbms_output.put_line(replace('Hello world', 'world')); -- Hello
    dbms_output.put_line(replace('Hello world', 'world', 'everyone')); -- Hello everyone
    dbms_output.put_line(substr('Hello world', 0, 5)); -- Hello
    dbms_output.put_line(length('Hello world')); -- 11
    
    -- функции даты
    dbms_output.put_line(add_months('09-02-2021', 5)); -- 09.07.21
    dbms_output.put_line(last_day('29-01-2015')); -- 31.01.15 
    dbms_output.put_line(months_between('25-04-2015', '16-03-2015')); -- 1,2
    dbms_output.put_line(next_day('29-01-2021', 'Суббота')); -- 30.01.21
    dbms_output.put_line(sysdate); -- 29.01.21, возвращает текущую дату
    dbms_output.put_line(to_char(sysdate, 'DD.MM.YY HH24:MI:SS')); -- 29.01.21 14:29:13
    dbms_output.put_line(to_char(to_date('29.01.21', 'DD.MM.YY'), 'DD Month YYYY')); -- 29 Январь   2021

PROCEDURE

Базовая единица PL/SQL блок - является по сути анонимным и компилируется каждый раз при выполнении. Он не хранится в базе данных и не может быть вызван из другого блока (ввиду отсутствия какого-либо имени/идентификатора).
Для таких случаев используются именованные блоки - процедуры и функции. Такие блоки хранятся в БД и могут быть использованы повторно.

Синтаксиси создания процедуры - create [or replace] procedure my_procedure[(param)] as ...
Можно использовать как ключевое слово as, так и is - они являются синонимами.
Удаление процедуры - drop procedure my_proc;

Тело процедуры содержит собственный исполняемой код и распологается между ключевыми словами begin и exception (который можно опустить), после него идет закрывающий end.
Раздел объявления переменных (аналог declare) располагается между операторами as или is.

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

create or replace procedure my_proc as -- создаем процедуру
begin
   dbms_output.put_line('Hello!');
end my_proc;
/

-- вызваем процедуру
set SERVEROUTPUT ON -- разрешаем вывод в консоль
exec my_proc; -- Hello!

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

set SERVEROUTPUT ON
begin
   my_proc;
end;
/

PARAMETERS

Процедура может содержать ряд аргументов.

Рассмотрим выходной параметр:

create or replace procedure my_proc(in_name IN varchar2) as
v_code_by_name varchar(50);
begin
    select code into v_code_by_name 
    from employees where employees .name = in_name;

    dbms_output.put_line('Kate`s code: ' || v_code_by_name);
end my_proc;
/

-- вызываем процедуру
set SERVEROUTPUT ON
exec my_proc('Kate'); -- Kate`s code: 123

Ключевое слово IN при определении параметров процедуры обозначает, что параметр является входным, т.е. значения для него должно быть установлено при вызове процедуры.

Теперь рассмотрим выходной параметр:

-- создаем процедуру
create or replace procedure my_proc(in_name IN varchar2, out_code OUT number) as
begin
    select code into out_code 
    from employees where employees.name = in_name;
end my_proc;
/

-- вызываем процедуру в блоке pl/sql
set SERVEROUTPUT ON
declare 
    v_code number; -- создаем переменную
begin
    my_proc('Kate', v_code); -- передаем имя и созданную переменную
    dbms_output.put_line('Kate`s code: ' || v_code); -- Kate`s code: 123
end;
/

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

Суммируя вышесказанное, IN-параметр можно только считывать, но не изменить (мы работаем только с его содержимым, т.е. передаем параметр по значению, а не по ссылке);
А в OUT-параметр мы можем только записывать, но не считывать с него (т.е. даже при передаче не-пустого параметра мы не сможем его считать и его значение просто перепишется на новое).

Существует еще и параметр IN OUT - этот тип представляет собой комбинацию видов IN и OUT. Внутри процедуры значение параметра может быть считано и в него же можем быть записано значение.

-- создаем процедуру
create or replace procedure my_proc(io_param IN OUT varchar2) as
begin
    select code into io_param 
    from employees where employees .name = io_param;
end my_proc;
/

-- вызываем процедуру в анонимном блоке
set SERVEROUTPUT ON
declare 
    v_code_by_name varchar2(50); -- создаем переменную
begin
    v_code_by_name := 'Kate'; -- инициализируем переменную
    my_proc(v_code_by_name); -- передаем созданную и проинициализированную переменную в процедуру
    dbms_output.put_line('Kate`s code: ' || v_code_by_name); -- Kate`s code: 123
end;
/

Как видно из примера, процедура с начала считала значение Kate из переданной переменной, а потом переписала ее значение на результат запроса - 123.
Важно отметить, что в данном случае, при передачи обычного литерала, например my_proc('Kate'), процедура не сработала бы, т.к. литерал не хранится в памяти после использования и туда нельзя записать выходное значение.

BOUND OF TYPE

Еще один важный момент - накладывать ограничения на параметры функций в PL/SQL запрещено.
Т.е. такой код приведет к ошибке компиляции:

create or replace procedure my_proc(io_param IN OUT varchar2(50)) as ...

Мы попытались установить ограничение в 50 симолов для строкового типа.

Но возможна еще одна ошибка:

create or replace procedure my_proc(in_param IN OUT varchar2, io_param IN OUT number) as
begin
    in_param := 'abs';
    io_param := 15.5;
end my_proc;
/

Когда в теле процедуры мы инициализировали параметры, для них неявно задался тип: in_param varchar2(3) и io_param number(3,1).
Теперь попробуем вызвать эту процедуру в анонимном блоке, передава ей переменные с типом varchar(1) и number(3, 2):

set SERVEROUTPUT ON
declare 
    v_str varchar2(1);
    v_num number(3,2);
begin
    my_proc(v_str, v_num);
end;
/

Мы получим ошибки, для строковой переменной это - буфер символьных строк слишком маленький ошибка числа или значения, а для числовой - точность числа слишком большая ошибка числа или значения.

Cпособом избежать таких ошибок - является использование оператора %type, а также документирование всех налагаемых на процедуру ограничений.

create or replace procedure my_proc(in_param IN OUT employees.name%type) -- при создании процедуры
 v_str employees.name%type; -- при создании переменных

NAMED ARGUMENTS

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

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

-- создаем процедуру
create or replace procedure my_proc(
        in_1 IN number,
        in_2 IN varchar2,
        in_3 IN number) as
begin
    null;
end my_proc;
/

-- вызываем процедуру
set SERVEROUTPUT ON
declare 
    v_1 number;
    v_2 varchar2(50);
    v_3 number;
begin
    my_proc(in_2 => v_2, in_1 => v_1, in_3 => v_3); -- используем именованные аргументы
end;
/

DEFAULT ARGUMENTS

Параметры процедуры могут иметь значения по умолчанию. В это случае, значение параметру передавать необязательно. Если же параметр будет передан - он перепишет значение по умолчанию.

Пример:

create or replace procedure my_proc(
        in_1 IN varchar2,
        in_2 IN varchar2 default 'world!') as
begin
    dbms_output.put_line(in_1 || ' ' || in_2);
end my_proc;
/

set SERVEROUTPUT ON
exec my_proc('Hello'); -- Hello world!

При вызове процедуры мы указали только один параметр.

FUNCTION

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

Рассмотрим пример, создав функцию преобразования типа boolean к типу varchar2:

-- создаем функцию
create or replace function boolean_to_char(in_boolean IN boolean) return varchar2
is
    out_string varchar2(5);
begin
    if (in_boolean) then out_string := 'true';
    elsif (not in_boolean) then out_string := 'false';
    else out_string := 'null';
    end if;
    
    return(out_string);
end boolean_to_char;
/

-- вызываем функцию и передаем туда параметры
set SERVEROUTPUT ON
begin
  dbms_output.put_line(boolean_to_char(TRUE)); -- true
  dbms_output.put_line(boolean_to_char(FALSE)); -- false
  dbms_output.put_line(boolean_to_char(NULL)); -- null
end;
/

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

TRIGGER

Триггер - наименованый блок PL/SQL (наряду с процедурой и функцией), который хранится как объект БД. Но он не может быть вызван вручную и соответствено принять какие то параметры. Данный блок срабатывает только при заранее определенных событиях, а именно до или после операций INSERT, UPDATE или DELETER.

Синтаксиси создания триггера:

create [or replace] trigger <name> 
    before|after -- момент времени срабатывания
    insert [of column] -- определяет, какой DML оператор вызывает активацию триггера (можно указать конкретное поле)
    on <table>
    [for each row] -- определяет, активируется ли триггер на каждую строку или один раз до или после оператора
    [where (condition)] -- ограничения
[declate] -- блок создания переменных можно опустить
begin
    <body>
end <name>;
/

Рассмотрим на примере.
Для начала создадим две таблицы - developers и developers_audit:

create table developers (id int, name varchar2(50), age int);
create sequence DEVELOPERS_SEQ;

create table developers_audit (user_name varchar2(50), last_date date);

Теперь создадим два триггера:
триггер dev_dml_check каждый раз, при использовании dml-операций с таблицой developers, будет оставлять запись таблице аудита developers_audit:

create or replace trigger dev_dml_check after 
    insert or delete or update on developers
begin
    insert into developers_audit(user_name, last_date) values(USER, SYSDATE);
end dev_dml_check;
/

триггерdev_id_gen каждый раз, при вставке значений в таблицу developers, будет генерировать идентификатор записи.

create or replace trigger dev_id_gen before
    insert on developers
    for each row
begin
    select DEVELOPERS_SEQ.nextval into :new.id from dual;
end dev_id_gen;
/

Pseudorecords

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

Активизирующий оператор :old :new
insert Значение не определено - null Значения, которые будут введены после выполнения опреатора
update Исходные значения, содержащиеся в строке перед обновлением Новые значения, которые будут введены после выполнения
delete Исходные значения, содержащиеся в строке перед удалением Значение не определено - null

Рассмотрим на примере.
Создадим триггер, который будет записывать в специальную таблицу аудита старые и новые значения после обновления таблицы developers:

-- таблица для аудита
create table developers_update_audit(last_date date, old_value varchar2(50), new_value varchar2(50));

-- создаем триггер
create or replace trigger dev_update_check after
    update on developers
    for each row
begin
    insert into developers_update_audit(last_date, old_value, new_value) values (SYSDATE, :new.name, :old.name);
end dev_update_check;
/

Важно отметить, что при использовании псевдосимволов с оператором where, двоеточие не ставится!

create or replace trigger dev_update_check after
    update on developers
    for each row
    where (new.age > 18)
...

PREDICATES

В триггерах можно применять логические операторы - предикаты. При выполнении условия они возвращают true, в противном случае - false.
Их всего несколько:

Предикат Условие
inserting Оператор INSERT активировал триггер
updating Оператор UPDATE активировал триггер
updating('column') Оператор UPDATE, для конкретного столбца, активировал триггер
deleting Оператор DELETE активировал триггер

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

create or replace trigger dev_trigger before
    insert OR 
    update OF age, name OR 
    delete
    on developers
begin
    case
        when inserting then
            dbms_output.put_line('inserting');
        when updating('name') then
            dbms_output.put_line('updating name');
        when updating('age') then
            dbms_output.put_line('updating age');
        when deleting then
            dbms_output.put_line('deleting');
    end case;
end;
/

Теперь, при dml-операциях с таблицей developers, на консоль будут выводиться соответствующие записи.

insert into test_developers (name, age) values ('Kate', 24); -- inserting
update test_developers set age = 25 where name = 'Kate'; -- updating age
delete from test_developers where name = 'Kate'; -- deleting

SOURCES

Курсоры (CURSOR) в PL/SQL
Youtube. PL SQL
PL/SQL в Oracle

⚠️ **GitHub.com Fallback** ⚠️