вторник, 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;
/



суббота, 12 января 2013 г.

Использование подзапросов в ORACLE

Типы подзапросов:

Однострочные
возвращают 0 или 1 строку
(если к тому же возвращается и один столбец то подзапрос скалярный)

Многострочные
возвращают одну или несколько строк

Подзапросы можно еще разделить на подтипы:

многостолбцовые

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

вложенные
(помещены внутрь другого  подзапроса, уровень вложенности может достигать 255)


Однострочные подзапросы.

Подзапросы во фразе WHERE:

select col1, col2
from tab
where col_tab_id = (select col_tab_id
                    from tab
                    where col2 = 'XXX');

Во фразе WHERE  можно использовать операторы сравнения такие как:
=, <>, <, >, <=, >=

select col1, col2, col3
from tab
where col3 > (select avg(col3)
              from tab);

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


Подзапросы во фразе HAVING:

select col1, avg(col2)
from tab
group by col1
having avg(col2) <  (select max(avg(col2))
                     from tab
                     group by col1)
order by col1;


Подзапросы во фразе FROM:
(встроенные представления)


select col1
from (select col1
      from tab
      where col1 < 10);


более полезный пример:

select t1.col_tab1_id, col2, t2.COL_TAB1_ID_COUNT
from tab1 t1, (select col_tab1_id, count(col_tab1_id) COL_TAB1_ID_COUNT
               from tab2
               group by col_tab1_id) t2
where t1.col_tab1_id =  t2.col_tab1_id;


Подзапросы не могут содержать фразу order by.
Любое упорядочивание должно проводиться во внешнем запросе:

select col1, col2, col3
from tab
where col3 > (select avg(col3) from tab)
order by col1 desc;

Многострочные подзапросы
Возвращают во внешний запрос одну или более строк.

Чтобы обработать подзапрос, возвращающий несколько строк, необходимо использовать один из операторов:
IN, ANY или ALL.

select col1, col2
from tab
where col1 IN (5,6,7);

Использование IN в многострочных подзапросах:

select col1, col2
from tab
where col1 IN (select col1
                from tab
                where col2 like '_z%');


select col1, col2
from tab1
where col1 NOT IN (select col1 from tab2 );


Использование ANY в многострочных подзапросах:

ANY используется для сравнения значения с любым значением в списке.
Перед ANY можно использовать операторы сравнения такие как:
=, <>, <, >, <=, >=

select col1, col2
from tab1
where col3 < ANY (select col1 from tab2 );


Использование ALL в многострочных подзапросах:

ALL используется для сравнения значения со всеми значениями из списка.
Перед ALL можно использовать операторы сравнения такие как:
=, <>, <, >, <=, >=

select col1, col2
from tab1
where col3 > ALL (select col1 from tab2 );


Многостолбцовые подзапросы:

Подзапросы могут возвращать несколько столбцов:

select col1, col2, col3
from tab1
where (col1, col3) IN
      (select col1, min(col3)
       from tab1
       group by col2);


Коррелированные подзапросы

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

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

select col1, col2, col3, col4
from tab out
where col4 > (select avg(col4) from tab inn
              where inn.col2 = out.col2);

тут строки внешнего запроса по очереди передаются в подзапрос.


Использование EXISTS и NOT EXISTS с коррелированными подзапросами.

EXISTS используется для проверки, существует ли хоть одна строка, возвращенная подзапросом.
NOT EXISTS проверяет что не существует ни одной строки, возвращенной подзапросом.

select col1, col2
from tab out
where EXISTS (select col1 from tab inn
              where inn.col3 = out.col1);

не важно сколько строк возвращает подзапрос,
важно знать, возвращаются ли в принципе какие либо строки.

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

select col1, col2
from tab out
where EXISTS (select 1 from tab inn
              where inn.col3 = out.col1);


до тех пор, пока подзапрос возвращает одну или более строк
EXISTS возвращает TRUE.

если подзапрос не возвращает ни одной строки
EXISTS возвращает FALSE.

Использование NOT EXISTS с коррелированными подзапросами.

select col1, col2
from tab1 out
where NOT EXISTS (select 1 from tab2 inn
                  where inn.col1 = out.col1);


Отличие EXISTS от IN

EXISTS проверяет только сам факт существования строк
IN проверяет их реальные значения
EXISTS более производительные оператор чем IN
Если в списке значений есть NULL, то NOT EXISTS возвратит TRUE, а NOT IN - FALSE.

select col1, col2
from tab1 out
where NOT EXISTS (select 1 from tab2 inn
                  where inn.col1 = out.col1);


select col1, col2
from tab1
where NOT IN (select col1 from tab2);

Разрешить NULL можно так:

select col1, col2
from tab1
where NOT IN (select nvl(col1,0) from tab2);

Вложенные подзапросы:

максимальная глубина вложенности - 255
Лучше использовать соединения чем вложенность.
Глубокая вложенность влияет на производительность.

select col1, avg(col2)
from tab1
group by col1
having avg(col2) < (select max(avg(col1))
                    from tab1
                    where col1 IN (select col1 from tab2 where col2 > 1)
                    group by col1)
order by col1;


UPDATE и DELETE c подзапросами:

update tab1 set col1 = (select avg(col1) from tab2)
where col2 = 4;

delete from tab1
where col1 > (select avg(col1) from tab2);


Quick Reference to RDBMS Database Patchset Patch Numbers [ID 753736.1]


What is the patch number of a patchset?

Patchset/PSU Patch Number Description
























11.2.0.3.4 14275605
DATABASE PATCH SET UPDATE 11.2.0.3.4 (INCLUDES CPUOCT2012)
11.2.0.3.3 13923374 DATABASE PATCH SET UPDATE 11.2.0.3.3 (INCLUDES CPU JUL2012):
11.2.0.3.2 13696216 DATABASE PATCH SET UPDATE 11.2.0.3.2 (INCLUDES CPU APR2012)
11.2.0.3.1 13343438 DATABASE PATCH SET UPDATE 11.2.0.3.1 (INCLUDES CPU JAN2012)
11.2.0.3 10404530 11.2.0.3.0 PATCH SET FOR ORACLE DATABASE SERVER
11.2.0.2.8 14275621 DATABASE PATCH SET UPDATE 11.2.0.2.8 (INCLUDES CPUOCT2012)
11.2.0.2.7 13923804 DATABASE PATCH SET UPDATE 11.2.0.2.7 (INCLUDES CPU JUL2012)
11.2.0.2.6 13696224 DATABASE PATCH SET UPDATE 11.2.0.2.6 (INCLUDES CPU APR2012)
11.2.0.2.5 13343424 DATABASE PATCH SET UPDATE 11.2.0.2.5 (INCLUDES CPU JAN2012)
11.2.0.2.4 12827726 DATABASE PSU 11.2.0.2.4 (INCLUDES CPUOCT2011)
11.2.0.2.3 12419331 DATABASE PSU 11.2.0.2.3 (INCLUDES CPUJUL2011)
11.2.0.2.2 11724916 DATABASE PSU 11.2.0.2.2 (INCLUDES CPUAPR2011)
11.2.0.2.1 10248523 DATABASE PSU 11.2.0.2.1
11.2.0.2 10098816 11.2.0.2.0 PATCH SET FOR ORACLE DATABASE SERVER
11.2.0.1.6 12419378  DATABASE PSU 11.2.0.1.6 (INCLUDES CPUJUL2011)
11.2.0.1.5 11724930 DATABASE PSU 11.2.0.1.5 (INCLUDES CPUAPR2011)
11.2.0.1.4 10248516 DATABASE PSU 11.2.0.1.4 (INCLUDES CPUJAN2011)
11.2.0.1.3 9952216 DATABASE PSU 11.2.0.1.3 (INCLUDES CPUOCT2010)
11.2.0.1.2 9654983 DATABASE PSU 11.2.0.1.2 (INCLUDES CPUJUL2010)
11.2.0.1.1 9352237 DATABASE PSU 11.2.0.1.1
11.1.0.7.13 14275623 [*] DATABASE PATCH SET UPDATE 11.1.0.7.13 (INCLUDES CPUOCT2012)
11.1.0.7.12 13923474 DATABASE PATCH SET UPDATE 11.1.0.7.12 (INCLUDES CPU JUL2012)
11.1.0.7.11 13621679 DATABASE PATCH SET UPDATE 11.1.0.7.11 (INCLUDES CPU APR2012)
11.1.0.7.10 13343461 DATABASE PATCH SET UPDATE 11.1.0.7.10 (INCLUDES CPU JAN2012)
11.1.0.7.9 12827740 DATABASE PSU 11.1.0.7.9 (INCLUDES CPUOCT2011)
11.1.0.7.8 12419384 DATABASE PSU 11.1.0.7.8 (INCLUDES CPUJUL2011)
11.1.0.7.7 11724936 DATABASE PSU 11.1.0.7.7 (INCLUDES CPUAPR2011)
11.1.0.7.6 10248531 DATABASE PSU 11.1.0.7.6 (INCLUDES CPUJAN2011)
11.1.0.7.5 9952228 DATABASE PSU 11.1.0.7.5 (INCLUDES CPUOCT2010)
11.1.0.7.4 9654987 DATABASE PSU 11.1.0.7.4 (INCLUDES CPUJUL2010)
11.1.0.7.3 9352179 DATABASE PSU 11.1.0.7.3 (INCLUDES CPUAPR2010)
11.1.0.7.2 9209238 DATABASE PSU 11.1.0.7.2 (INCLUDES CPUJAN2010)
11.1.0.7.1 8833297 DATABASE PSU 11.1.0.7.1 (INCLUDES CPUOCT2009)
11.1.0.7 6890831 11.1.0.7.0 PATCH SET FOR ORACLE DATABASE SERVER
10.2.0.5.9 14275629 [*] DATABASE PATCH SET UPDATE 10.2.0.5.9 (INCLUDES CPUOCT2012)
10.2.0.5.8 13923855 [*] DATABASE PATCH SET UPDATE 10.2.0.5.8 (INCLUDES CPU JUL2012)
10.2.0.5.7 13632743 [*] DATABASE PATCH SET UPDATE 10.2.0.5.7 (INCLUDES CPU APR2012)
10.2.0.5.6 13343471 [*] DATABASE PATCH SET UPDATE 10.2.0.5.6 (INCLUDES CPU JAN2012)
10.2.0.5.5 12827745 [*] DATABASE PSU 10.2.0.5.5 (INCLUDES CPUOCT2011)
10.2.0.5.4 12419392 DATABASE PSU 10.2.0.5.4 (INCLUDES CPUJUL2011)
10.2.0.5.3 11724962 DATABASE PSU 10.2.0.5.3 (INCLUDES CPUAPR2011)
10.2.0.5.2 10248542 DATABASE PSU 10.2.0.5.2 (INCLUDES CPUJAN2011)
10.2.0.5.1 9952230 DATABASE PSU 10.2.0.5.1 (INCLUDES CPUOCT2010)
10.2.0.5 8202632 10.2.0.5.0 PATCH SET FOR ORACLE DATABASE SERVER
10.2.0.4.14 14275630 [**] DATABASE PSU 10.2.0.4.14 (REQUIRES PRE-REQUISITE 10.2.0.4.4|INCLUDES CPUOCT2012)
0.2.0.4.13 13923851 [*] DATABASE PSU 10.2.0.4.13 (REQUIRES PRE-REQUISITE 10.2.0.4.4|INCLUDES CPUJUL2012)
10.2.0.4.12 12879933 [*]
DATABASE PSU 10.2.0.4.12 (REQUIRES PRE-REQUISITE 10.2.0.4.4|INCLUDES CPUAPR2012)
10.2.0.4.11 12879929 [*] DATABASE PATCH SET UPDATE 10.2.0.4.11 (PRE-REQ 10.2.0.4.4|INCLUDES CPUJAN2012)
10.2.0.4.10 12827778 DATABASE PSU 10.2.0.4.10 (REQUIRES PRE-REQUISITE 10.2.0.4.4|INCLUDES CPUOCT2011)
10.2.0.4.9 12419397 DATABASE PSU 10.2.0.4.9 (REQUIRES PRE-REQUISITE 10.2.0.4.4|INCLUDES CPUJUL2011)
10.2.0.4.8 11724977 DATABASE PSU 10.2.0.4.8 (REQUIRES PRE-REQUISITE 10.2.0.4.4|INCLUDES CPUAPR2011)
10.2.0.4.7 10248636 DATABASE PSU 10.2.0.4.7 (REQUIRES PRE-REQUISITE 10.2.0.4.4|INCLUDES CPUJAN2011)
10.2.0.4.6 9952234 DATABASE PSU 10.2.0.4.6 (REQUIRES PRE-REQUISITE 10.2.0.4.4|INCLUDES CPUOCT2010) 
10.2.0.4.5 9654991 DATABASE PSU 10.2.0.4.5 (REQUIRES PRE-REQUISITE 10.2.0.4.4|INCLUDES CPUJUL2010)    [overlay PSU]
10.2.0.4.4 9352164 DATABASE PSU 10.2.0.4.4 (INCLUDES CPUAPR2010)
10.2.0.4.3 9119284 DATABASE PSU 10.2.0.4.3 (INCLUDES CPUJAN2010)
10.2.0.4.2 8833280 DATABASE PSU 10.2.0.4.2 (INCLUDES CPUOCT2009)
10.2.0.4.1 8576156 DATABASE PSU 10.2.0.4.1 (INCLUDES CPUJUL2009)
10.2.0.4 6810189 10.2.0.4.0 PATCH SET FOR ORACLE DATABASE SERVER
10.2.0.3 5337014 10.2.0.3 PATCH SET FOR ORACLE DATABASE SERVER
10.2.0.2 4547817 10.2.0.2 PATCH SET FOR ORACLE DATABASE SERVER
10.1.0.5 4505133 10.1.0.5 PATCH SET FOR ORACLE DATABASE SERVER
10.1.0.4 4163362 10.1.0.4 PATCH SET FOR ORACLE DATABASE SERVER
10.1.0.3 3761843 10.1.0.3 PATCH SET FOR ORACLE DATABASE SERVER
9.2.0.8 4547809 9.2.0.8 PATCH SET FOR ORACLE DATABASE SERVER
9.2.0.7 4163445 9.2.0.7 PATCH SET FOR ORACLE DATABASE SERVER
9.2.0.6 3948480 9.2.0.6 PATCH SET FOR ORACLE DATABASE SERVER
9.2.0.5 3501955 ORACLE 9I DATABASE SERVER RELEASE 2 - PATCH SET 4 VERSION 9.2.0.5.0
9.2.0.4 3095277 9.2.0.4 PATCH SET FOR ORACLE DATABASE SERVER
9.2.0.3 2761332 9.2.0.3 PATCH SET FOR ORACLE DATABASE SERVER
9.2.0.2 2632931 9.2.0.2 PATCH SET FOR ORACLE DATABASE SERVER
9.0.1.5 3301544 9.0.1.5 PATCHSET
9.0.1.4 2517300 9.0.1.4 PATCH SET FOR ORACLE DATABASE SERVER
9.0.1.3 2271678 9.0.1.3. PATCH SET FOR ORACLE DATA SERVER
8.1.7.4 2376472 8.1.7.4 PATCH SET FOR ORACLE DATA SERVER
8.1.7.3 2189751 8.1.7.3 PATCH SET FOR ORACLE DATA SERVER
8.1.7.2 1909158 8.1.7.2.1 PATCH SET FOR ORACLE DATA SERVER


NOTE:
[*]   10.2.0.4 and 10.2.0.5 are now in extended support mode and PSU's released after Aug 01,2011 will need ES License to download them.
[**] Available only in limited platforms

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

Простые SQL-запросы из таблиц Oracle

SQL - запросы

Выборка информации из одной таблицы:

describe tab;

select * from tab;

select  col1, col2, col3  from tab;

select  ROWID, col1 from tab;
                                  
select * from tab  where tab_col_id = 15;

select  12 * 15  from dual;

select  to_date('28-APR-1971') + 12  from dual;

select  to_date('28-APR-1971') - 15  from dual;

select  to_date('28-APR-1971') - to_date('28-JAN-1971')  from dual;

select  col1, col2, col3 + 45  from tab;

select  12 * (25/5 - 1)  from dual;

select  col1, col1 * 2  DOUBLE_COL1  from tab;

select  col1, col1 * 2  "Double Col1"  from tab;

select  12 * (25/5 - 1)  AS  "Result"  from dual;

select  col1 || ' ' || col2  AS "Concatenation"  from tab;

select col1, col2  from tab  where  tab_col2 is null;

select col1, col2  from tab  where  tab_col2 is not null;

select col1, nvl(col2, 'Uncnown col2') AS COL_2 from tab;

select  DISTINCT col1  from tab;


Сравнение значений:


select col1, col2, col3  from tab where col1 <> 12;

select col1, col2, col3  from tab where col2 > 10;

select col1, col2, col3  from tab where col3 <= 100;

select col1, col2, col3  from tab where ROWNUM <= 15;

select col1, col2, col3  from tab where col1 > ANY( 12, 18, 25 );
(истина, когда col1 больше одного любого из перечисленных чисел, фактически если >12)

select col1, col2, col3  from tab where col1 > SOME( 12, 18, 25 );
(истина, когда col1 больше одного любого из перечисленных чисел, фактически если >12)

select col1, col2, col3  from tab where col1 > ALL( 12, 18, 25 );
(истина, когда col1 одновременно больше всех из перечисленных чисел, это эквивалентно >25)

select col1, col2 from tab where col1 LIKE '_z%';
(истина, если вторая буква в строке z)

select col1, col2 from tab where col1 NOT LIKE '_z%';
(истина, если вторая буква в строке не z)

select col1, col2 from tab where col1 LIKE '%\%%'  ESCAPE '\';
(истина, если в строке содержится символ %)

select col1, col2 from tab where col1 IN ( 12, 18, 25 );

select col1, col2 from tab where col1 NOT IN ( 12, 18, 25, null );
(если в списке NOT IN встретится null, то выражение вернет false)

select col1, col2 from tab where col1  BETWEEN ( 10 and 20 );

select col1, col2 from tab where col1  NOT BETWEEN ( 10 and 20 );

select col1, col2, col3  from tab where col1 > '28-JAN-1971' and col2 > 10;

select col1, col2, col3  from tab where col1 > '28-JAN-1971' or col2 > 10;

select col1, col2, col3  from tab where col1 > '28-JAN-1971' or ( col2 > 10 and col3 LIKE '_z%');
(у AND больший приоритет чем у OR)

select col1, col2, col3  from tab order by col1;

select col1, col2, col3  from tab order by 1;

select col1, col2, col3  from tab order by col1 ASC, col2 DESC;



Выборка строк из двух таблиц:

select tab1.col,  tab2.col
from   tab1, tab2
where  tab1.col_tab2_id = tab2.col_tab2_id
  and  tab1.col_tab1_id = 100;


(используя псевдонимы)

select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id = t2.col_tab2_id
  and  t1.col_tab1_id = 100;


(Декартово произведение)

select t1.col,  t2.col
from   tab1 t1, tab2 t2


Выборка строк из более чем двух таблиц:

select t1.col,  t2.col AS COL_T2, t3.col AS COL_T3
from   tab1 t1, tab2 t2, tab3 t3, tab4 t4
where  t1.col_tab1_id = t2.col_tab1_id
  and  t3.col_tab3_id = t2.col_tab3_id
  and  t3.col_tab4_id = t4.col_tab4_id
order by t3.col;


Типы соединений

 В предыдущих примерах в условиях соединения использовался знак равенства (=),
поэтому такие соединения называют соединениями по эквивалентности equijoins.

Соединения по эквивалентности:
используется знак =

Соединения по неэквивалентности:
используются операторы <, >, between и т.д.

Кроме того есть

Внутренние соединения:
в столбцах условия соединения содержатся значения удовлетворяющие условию,
т.е. нет пустых значений.

Внешние соединения:
когда один из столбцов соединения может сожержать значения NULL.

Самосоединения:
возвращают соединенные строки одной и той же таблицы


Примеры:

(соединение по неэквивалентности)

select t1.col1, t2.col1, t2.col2, t2.col3
from   tab1 t1, tab2 t2
where  t1.col1 BETWEEN t2.col1 and t2.col2
order  by t2.col3;


(внешнее соединение)

select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id = t2.col_tab2_id(+)
order by t1.col;

где (+), там могут быть пустые значения
(в нашем случае в таблице tab2)


Левое и правое внешние соединения

Левое соединение:
оператор внешнего соединения (+) появляется справа от знака =
where  t1.col_tab2_id = t2.col_tab2_id(+)

Правое соединение:
оператор внешнего соединения (+) стоит слева от знака =
where  t1.col_tab2_id(+) = t2.col_tab2_id

Ограничения внешних соединений

оператор  (+) можно поместить только с одной стороны от знака =

нельзя использовать (+) с IN
where  t1.col_tab2_id(+) IN ( 1, 2, 3 )

нельзя использовать (+) с OR
where .... (+) = .... OR .... = 10




(Левое внешнее соединение)

select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id = t2.col_tab2_id(+)
order by t1.col;

(Правое внешнее соединение)

select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id(+) = t2.col_tab2_id
order by t1.col;


(Полное внешнее соединение)

Двунаправленное внешнее соединение не разрешено:

where  t1.col_tab2_id(+) = t2.col_tab2_id(+)
получим ошибку ORA-01468

Остается один выход, объединить два запроса:

select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id(+) = t2.col_tab2_id
UNION
select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id = t2.col_tab2_id(+);


Самосоединения selfjoin
получается в результате соединения таблицы с самой собой.

select t1.col,  t2.col
from   tab t1, tab t2
where  t1.col1_id = t2.col2_id;
order by t1.col;

Можно выполнить внешнее соединение в сочетании с самосоединением:

select t1.col,  t2.col
from   tab t1, tab t2
where  t1.col1_id(+) = t2.col2_id;
order by t1.col;


Новый синтаксис соединений SQL/92
(появился в версии 9i)

До этого мы пользовались стандартом SQL/86

Примеры:

SQL/86:

select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id = t2.col_tab2_id
order by t1.col;

SQL/92:

select t1.col,  t2.col
from   tab1 t1
       INNER JOIN tab2 t2
ON     t1.col_tab2_id = t2.col_tab2_id
order by t1.col;


Во фразе ON можно использовать операторы неэквивалентности:

ON t1.col1 BETWEEN t2.col1 and t2.col2


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

select t1.col,  t2.col
from   tab1 t1
       INNER JOIN tab2 t2
ON     t1.col_tab2_id = t2.col_tab2_id
order by t1.col;

этот запрос можно переписать так:

select t1.col,  t2.col
from   tab1 t1
       INNER JOIN tab2 t2
USING( col_tab2_id )
order by t1.col;


ON     t1.col_tab2_id = t2.col_tab2_id
мы заменили на
USING( col_tab2_id )

Имя столбца соединения во фразе USING должно использоваться без псевдонимов
и теперь везде этот столбец нужно использовать без псевдонимов
в том числе и во фразе SELECT:

select t1.col,  t2.col, col_tab2_id
from   tab1 t1
       INNER JOIN tab2 t2
USING( col_tab2_id )
order by t1.col;

Внутренние соединения более двух таблиц:

select t1.col,  t2.col AS COL_T2, t3.col AS COL_T3
from   tab1 t1, tab2 t2, tab3 t3, tab4 t4
where  t1.col_tab1_id = t2.col_tab1_id
  and  t3.col_tab3_id = t2.col_tab3_id
  and  t3.col_tab4_id = t4.col_tab4_id
order by t3.col;

В нотации SQL/92 этот запрос можно переписать так:

select t1.col,  t2.col AS COL_T2, t3.col AS COL_T3
from   tab1 t1
       INNER JOIN tab2 t2 
       ON t1.col_tab1_id = t2.col_tab1_id
       INNER JOIN tab3 t3 
       ON t3.col_tab3_id = t2.col_tab3_id
       INNER JOIN tab3 t4 
       ON t3.col_tab4_id = t4.col_tab4_id
order by t3.col;

или даже так:

select t1.col,  t2.col AS COL_T2, t3.col AS COL_T3
from   tab1 t1
       INNER JOIN tab2 t2 
       USING(col_tab1_id)
       INNER JOIN tab3 t3 
       USING(col_tab3_id)
       INNER JOIN tab3 t4 
       USING(col_tab4_id)
order by t3.col;


Если в соединении используется более одного столбца из двух таблиц,
можно во фразе ON использовать оператор AND:

select t1.col, t2.col
from   tab1 t1
       INNER JOIN tab2 t2
       ON  t1.col1 = t2.col1
       AND t1.col2 = t2.col2;

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

select t1.col, t2.col
from   tab1 t1
       INNER JOIN tab2 t2
       USING(col1, col2);

Внешние соединения в нотации SQL/92

Существует три вида внешних соединений

LEFT  OUTER JOIN
RIGHT OUTER JOIN
FULL  OUTER JOIN


Левое внешнее соединение:

select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id = t2.col_tab2_id(+)
order by t1.col;

в нотации SQL/92 будет выглядеть так:

select t1.col,  t2.col
from   tab1 t1
       LEFT OUTER JOIN  tab2 t2
       ON  t1.col_tab2_id = t2.col_tab2_id
order by t1.col;

или

select t1.col,  t2.col
from   tab1 t1
       LEFT OUTER JOIN  tab2 t2
       USING(col_tab2_id)
order by t1.col;


Правое внешнее соединение

select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id(+) = t2.col_tab2_id
order by t1.col;

в нотации SQL/92 будет выглядеть так:

select t1.col,  t2.col
from   tab1 t1
       RIGHT OUTER JOIN  tab2 t2
       ON  t1.col_tab2_id = t2.col_tab2_id
order by t1.col;

или

select t1.col,  t2.col
from   tab1 t1
       RIGHT OUTER JOIN  tab2 t2
       USING(col_tab2_id)
order by t1.col;



Полное внешнее соединение

select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id(+) = t2.col_tab2_id
UNION
select t1.col,  t2.col
from   tab1 t1, tab2 t2
where  t1.col_tab2_id = t2.col_tab2_id(+);

в нотации SQL/92 будет выглядеть так:

select t1.col,  t2.col
from   tab1 t1
       FULL OUTER JOIN  tab2 t2
       ON  t1.col_tab2_id = t2.col_tab2_id
order by t1.col;

или

select t1.col,  t2.col
from   tab1 t1
       FULL OUTER JOIN  tab2 t2
       USING(col_tab2_id)
order by t1.col;



Самосоединения selfjoin
когда таблица соединяется сама собой.

select t1.col,  t2.col
from   tab t1, tab t2
where  t1.col1_id = t2.col2_id;
order by t1.col;

в нотации SQL/92 будет выглядеть так:

select t1.col,  t2.col
from   tab t1
       INNER JOIN tab t2
       ON  t1.col1_id = t2.col2_id;


Декартово произведение в нотации SQL/92 создается так:

select t1.col,  t2.col
from   tab1 t1
       CROSS JOIN tab2 t2;


Еще примеры:

drop table t;
create table t(id, type, text)
as
select object_id, object_type, object_name
from all_objects
order by 1;

drop table t1;
create table t1(id, type, text)
as
select id, type, text
from t
where rownum <= 5;


drop table t2;
create table t2
as
select id,  type , text
from t
where rownum <= 10;



select id, type, text from t1;

2    CLUSTER    C_OBJ#
3    INDEX    I_OBJ#
4    TABLE    TAB$
5    TABLE    CLU$
6    CLUSTER    C_TS#


select id, type, text from t2;
2    CLUSTER    C_OBJ#
3    INDEX    I_OBJ#
4    TABLE    TAB$
5    TABLE    CLU$
6    CLUSTER    C_TS#
7    INDEX    I_TS#
8    CLUSTER    C_FILE#_BLOCK#
9    INDEX    I_FILE#_BLOCK#
10    CLUSTER    C_USER#
11    INDEX    I_USER#

Работа с множествами:

select id, type, text from t1
UNION ALL
select id, type, text from t2;

2    CLUSTER    C_OBJ#
3    INDEX    I_OBJ#
4    TABLE    TAB$
5    TABLE    CLU$
6    CLUSTER    C_TS#
2    CLUSTER    C_OBJ#
3    INDEX    I_OBJ#
4    TABLE    TAB$
5    TABLE    CLU$
6    CLUSTER    C_TS#
7    INDEX    I_TS#
8    CLUSTER    C_FILE#_BLOCK#
9    INDEX    I_FILE#_BLOCK#
10    CLUSTER    C_USER#
11    INDEX    I_USER#



select id, type, text from t1
UNION ALL
select id, type, text from t2
order by 1;

2    CLUSTER    C_OBJ#
2    CLUSTER    C_OBJ#
3    INDEX    I_OBJ#
3    INDEX    I_OBJ#
4    TABLE    TAB$
4    TABLE    TAB$
5    TABLE    CLU$
5    TABLE    CLU$
6    CLUSTER    C_TS#
6    CLUSTER    C_TS#
7    INDEX    I_TS#
8    CLUSTER    C_FILE#_BLOCK#
9    INDEX    I_FILE#_BLOCK#
10    CLUSTER    C_USER#
11    INDEX    I_USER#


select id, type, text from t1
UNION
select id, type, text from t2;

2    CLUSTER    C_OBJ#
3    INDEX    I_OBJ#
4    TABLE    TAB$
5    TABLE    CLU$
6    CLUSTER    C_TS#
7    INDEX    I_TS#
8    CLUSTER    C_FILE#_BLOCK#
9    INDEX    I_FILE#_BLOCK#
10    CLUSTER    C_USER#
11    INDEX    I_USER#


select id, type, text from t1
INTERSECT
select id, type, text from t2;


2    CLUSTER    C_OBJ#
3    INDEX    I_OBJ#
4    TABLE    TAB$
5    TABLE    CLU$
6    CLUSTER    C_TS#



select id, type, text from t1
MINUS
select id, type, text from t2;

ничего не возвратит


select id, type, text from t2
MINUS
select id, type, text from t1;


7    INDEX    I_TS#
8    CLUSTER    C_FILE#_BLOCK#
9    INDEX    I_FILE#_BLOCK#
10    CLUSTER    C_USER#
11    INDEX    I_USER#


(select id, type, text from t1
UNION ALL
select id, type, text from t2)
MINUS
select id, type, text from t1;

7    INDEX    I_TS#
8    CLUSTER    C_FILE#_BLOCK#
9    INDEX    I_FILE#_BLOCK#
10    CLUSTER    C_USER#
11    INDEX    I_USER#


Функция TRANSLATE()

select TRANSLATE('TOM KYTE', 'ABCDEFGH', 'АБЦДЕФГХ') from dual;

TOM KYTЕ


select TRANSLATE('SCOTT URMAN', 'ABCDEFGH', 'АБЦДЕФГХ') from dual

SЦOTT URMАN

select id, type, TRANSLATE(text, 'ABCDEFGH', 'АБЦДЕФГХ') from t1

2    CLUSTER    Ц_OБJ#
3    INDEX    I_OБJ#
4    TABLE    TАБ$
5    TABLE    ЦLU$
6    CLUSTER    Ц_TS#


Функция DECODE:

SELECT
id,
DECODE (id,
    2, 'Кластер',
    3, 'Индекс',
    4, 'Таблица',
    5, 'Таблица',
    6, 'Кластер',
   'Тип неопределен') type ,
text
FROM t1;


2    Кластер    C_OBJ#
3    Индекс    I_OBJ#
4    Таблица    TAB$
5    Таблица    CLU$
6    Кластер    C_TS#


Функция CASE:

SELECT id,
       CASE id
       WHEN  2 THEN  'Кластер'
       WHEN  3 THEN 'Индекс'
       WHEN  4 THEN 'Таблица'
       WHEN  5 THEN 'Таблица'
       WHEN  6 THEN 'Кластер'
       ELSE   'Тип неопределен'
       END type,
       text
FROM t1;   

2    Кластер    C_OBJ#
3    Индекс    I_OBJ#
4    Таблица    TAB$
5    Таблица    CLU$
6    Кластер    C_TS#

или так

SELECT id,
       CASE
       WHEN id = 2 THEN  'Кластер'
       WHEN id = 3 THEN 'Индекс'
       WHEN id = 4 THEN 'Таблица'
       WHEN id = 5 THEN 'Таблица'
       WHEN id = 6 THEN 'Кластер'
       ELSE   'Тип неопределен'
       END type,
       text
FROM t1;   

2    Кластер    C_OBJ#
3    Индекс    I_OBJ#
4    Таблица    TAB$
5    Таблица    CLU$
6    Кластер    C_TS#


Фраза WITH и вынесенные подзапросы:

Общий вид такой:


WITH
   a AS (select ....from t)
  ,b AS (select ....from a)
  ,c AS (select ....from a, b)
select ... from a, b, c;


Например:

drop table t
create table t as

WITH X AS
(
    SELECT  1 ID, NULL PARENT_ID, 'Игорь'      F_NAME,   'Директор'               TITLE, 800000 SALARY FROM dual UNION ALL
    SELECT  2 ID,    1 PARENT_ID, 'Сергей'     F_NAME,   'Менеджер по продажам'   TITLE, 600000 SALARY FROM dual UNION ALL
    SELECT  3 ID,    2 PARENT_ID, 'Андрей'     F_NAME,   'Продавец'               TITLE, 200000 SALARY FROM dual UNION ALL
    SELECT  4 ID,    1 PARENT_ID, 'Виталий'    F_NAME,   'Менеджер'               TITLE, 500000 SALARY FROM dual UNION ALL
    SELECT  5 ID,    2 PARENT_ID, 'Александр'  F_NAME,   'Продавец'               TITLE,  40000 SALARY FROM dual UNION ALL
    SELECT  6 ID,    4 PARENT_ID, 'Владимир'   F_NAME,   'Персонал по поддержке'  TITLE,  45000 SALARY FROM dual UNION ALL
    SELECT  7 ID,    4 PARENT_ID, 'Николай'    F_NAME,   'Менеджер по поддержке'  TITLE,  30000 SALARY FROM dual UNION ALL
    SELECT  8 ID,    7 PARENT_ID, 'Ольга'      F_NAME,   'Персонал по поддержке'  TITLE,  29000 SALARY FROM dual UNION ALL
    SELECT  9 ID,    6 PARENT_ID, 'Марина'     F_NAME,   'Персонал по поддержке'  TITLE,  30000 SALARY FROM dual UNION ALL
    SELECT 10 ID,    1 PARENT_ID, 'Елена'      F_NAME,   'Операционный  менеджер' TITLE, 100000 SALARY FROM dual UNION ALL
    SELECT 11 ID,   10 PARENT_ID, 'Надежда'    F_NAME,   'Операционистка'         TITLE,  50000 SALARY FROM dual UNION ALL
    SELECT 12 ID,   10 PARENT_ID, 'Анна'       F_NAME,   'Операционистка'         TITLE,  45000 SALARY FROM dual UNION ALL
    SELECT 13 ID,   10 PARENT_ID, 'Татьяна'    F_NAME,   'Операционистка'         TITLE,  47000 SALARY FROM dual
)
select * from x;


1        Игорь    Директор    800000
2    1    Сергей    Менеджер по продажам    600000
3    2    Андрей    Продавец    200000
4    1    Виталий    Менеджер    500000
5    2    Александр    Продавец    40000
6    4    Владимир    Персонал по поддержке    45000
7    4    Николай    Менеджер по поддержке    30000
8    7    Ольга    Персонал по поддержке    29000
9    6    Марина    Персонал по поддержке    30000
10    1    Елена    Операционный  менеджер    100000
11    10    Надежда    Операционистка    50000
12    10    Анна    Операционистка    45000
13    10    Татьяна    Операционистка    47000


Иерархические запросы

Для выполнения иерархических запросов можно использовать фразы:
CONNECT BY  и START WITH  оператора SELECT


SELECT [LEVEL], столбец, ...
FROM таблица
[WHERE ...]
[ [START WITH стартовое условие]  [CONNECT BY PRIOR условие_prior] ]


LEVEL  - уровень вложенности узлов
1 - корневой узел

Стартовое условие  - с какого места начинать иерархический запрос
например ID = 1

Условие prior определяет отношение между родительскими и подчиненными строками
у нас это условие такое  ID = PARENT_ID



select * from t
start with id =1
connect by prior id = parent_id;

1        Игорь    Директор    800000
2    1    Сергей    Менеджер по продажам    600000
3    2    Андрей    Продавец    200000
5    2    Александр    Продавец    40000
4    1    Виталий    Менеджер    500000
6    4    Владимир    Персонал по поддержке    45000
9    6    Марина    Персонал по поддержке    30000
7    4    Николай    Менеджер по поддержке    30000
8    7    Ольга    Персонал по поддержке    29000
10    1    Елена    Операционный  менеджер    100000
11    10    Надежда    Операционистка    50000
12    10    Анна    Операционистка    45000
13    10    Татьяна    Операционистка    47000



Для удобства используем псевдостолбец LEVEL


select level,  t.* from t
start with id =1
connect by prior id = parent_id;


level   id
1    1        Игорь    Директор    800000
2    2    1    Сергей    Менеджер по продажам    600000
3    3    2    Андрей    Продавец    200000
3    5    2    Александр    Продавец    40000
2    4    1    Виталий    Менеджер    500000
3    6    4    Владимир    Персонал по поддержке    45000
4    9    6    Марина    Персонал по поддержке    30000
3    7    4    Николай    Менеджер по поддержке    30000
4    8    7    Ольга    Персонал по поддержке    29000
2    10    1    Елена    Операционный  менеджер    100000
3    11    10    Надежда    Операционистка    50000
3    12    10    Анна    Операционистка    45000
3    13    10    Татьяна    Операционистка    47000




Сколько уровней в дереве:

select count(distinct level) from t
start with id =1
connect by prior id = parent_id;

4


Форматирование результатов иерархического запроса

используем функцию LPAD()
она слева дополняет значение заданными символами
LPAD('    ',  2*LEVEL-1 )  - вставляет  2*LEVEL-1  пробелов


select level,
lpad(' ', 4*level-1) || F_NAME
from t
start with id =1
connect by prior id = parent_id;


1       Игорь
2           Сергей
3               Андрей
3               Александр
2           Виталий
3               Владимир
4                   Марина
3               Николай
4                   Ольга
2           Елена
3               Надежда
3               Анна
3               Татьяна


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

select level,
lpad(' ', 4*level-1) || F_NAME
from t
start with f_name = 'Елена'
connect by prior id = parent_id;


1       Елена
2           Надежда
2           Анна
2           Татьяна


или так:


select level,
lpad(' ', 4*level-1) || F_NAME
from t
start with id = 4
connect by prior id = parent_id;


1       Виталий
2           Владимир
3               Марина
2           Николай
3               Ольга


Во фразе START WITH можно использовать и подзапросы.


select level,
lpad(' ', 4*level-1) || F_NAME
from t
start with id = (select id from t where f_name = 'Елена')
connect by prior id = parent_id;


1       Елена
2           Надежда
2           Анна
2           Татьяна


Восходящий обход дерева

Можно вести обход дерева снизу вверх
для этого поменяем  местами в условии CONNECT BY PRIOR столбцы:

connect by prior id = parent_id
на
connect by prior parent_id = id;

и в start with указать с какого места начинать.


select level,
lpad(' ', 4*level-1) || F_NAME
from t
start with id = (select id from t where f_name = 'Ольга')
connect by prior parent_id = id;


1       Ольга
2           Николай
3               Виталий
4                   Игорь



Исключение из иерархического запроса узлов и ветвей

Исключим из результатов данные о служащем Сергее.


select level,
lpad(' ', 4*level-1) || F_NAME
from t
where f_name != 'Сергей'
start with id =1
connect by prior id = parent_id;

1       Игорь
3               Андрей
3               Александр
2           Виталий
3               Владимир
4                   Марина
3               Николай
4                   Ольга
2           Елена
3               Надежда
3               Анна
3               Татьяна


Сергей исключен, но его подчиненные Андрей и Александр остались.

Чтобы исключить всю ветвь, нужно добавить во фразу
CONNECT BY PRIOR  фоазу AND:

select level,
lpad(' ', 4*level-1) || F_NAME
from t
start with id =1
connect by prior id = parent_id
and  f_name != 'Сергей';

1       Игорь
2           Виталий
3               Владимир
4                   Марина
3               Николай
4                   Ольга
2           Елена
3               Надежда
3               Анна
3               Татьяна



Включение в иерархический запрос других условий:
например вывести только тех служащих, зарплата которых не превышает 50000

select level,
lpad(' ', 4*level-1) || F_NAME,
salary
from t
where salary <= 50000
start with id =1
connect by prior id = parent_id;


3               Александр    40000
3               Владимир    45000
4                   Марина    30000
3               Николай    30000
4                   Ольга    29000
3               Надежда    50000
3               Анна    45000
3               Татьяна    47000



Группировка  результатов:


drop table t;

create table t
as
select object_id id, owner own, object_type type, object_name text
from all_objects
where object_type in
('SEQUENCE',
'PROCEDURE',
'PACKAGE',
'TRIGGER',
'TABLE',
'INDEX',
'SYNONYM',
'VIEW',
'FUNCTION')
and owner in ('SYS','SYSTEM');



select type, count(text) from t
group by  type
order by  type;

FUNCTION    117
INDEX    1460
PACKAGE    721
PROCEDURE    151
SEQUENCE    180
SYNONYM    27
TABLE    1443
TRIGGER    13
VIEW    5742


select type, count(text) from t
group by rollup(type)
order by  type;

FUNCTION    117
INDEX    1460
PACKAGE    721
PROCEDURE    151
SEQUENCE    180
SYNONYM    27
TABLE    1443
TRIGGER    13
VIEW    5742
    9854


Передача в ROLLUP нескольких столбцов

select own, type, count(text) from t
group by rollup(own, type)
order by  own, type;

SYS    FUNCTION    113
SYS    INDEX    1224
SYS    PACKAGE    720
SYS    PROCEDURE    150
SYS    SEQUENCE    158
SYS    SYNONYM    19
SYS    TABLE    1265
SYS    TRIGGER    11
SYS    VIEW    5728
SYS        9388
SYSTEM    FUNCTION    4
SYSTEM    INDEX    236
SYSTEM    PACKAGE    1
SYSTEM    PROCEDURE    1
SYSTEM    SEQUENCE    22
SYSTEM    SYNONYM    8
SYSTEM    TABLE    178
SYSTEM    TRIGGER    2
SYSTEM    VIEW    14
SYSTEM        466
        9854


Изменим порядок столбцов в rollup:

select own, type, count(text) from t
group by rollup(type, own)
order by  type, own ;

SYS    FUNCTION    113
SYSTEM    FUNCTION    4
    FUNCTION    117
SYS    INDEX    1224
SYSTEM    INDEX    236
    INDEX    1460
SYS    PACKAGE    720
SYSTEM    PACKAGE    1
    PACKAGE    721
SYS    PROCEDURE    150
SYSTEM    PROCEDURE    1
    PROCEDURE    151
SYS    SEQUENCE    158
SYSTEM    SEQUENCE    22
    SEQUENCE    180
SYS    SYNONYM    19
SYSTEM    SYNONYM    8
    SYNONYM    27
SYS    TABLE    1265
SYSTEM    TABLE    178
    TABLE    1443
SYS    TRIGGER    11
SYSTEM    TRIGGER    2
    TRIGGER    13
SYS    VIEW    5728
SYSTEM    VIEW    14
    VIEW    5742
        9854


Фраза CUBE расширяет GROUP BY в том плане, что она возвращает строки,
содержащие предварительные итоги для всех комбинаций столбцов,
включенных во фразу CUBE, а в конце возвращается строка с итогом.

select own, type, count(text) from t
group by cube(own, type)
order by  own, type;

SYS    FUNCTION    113
SYS    INDEX    1224
SYS    PACKAGE    720
SYS    PROCEDURE    150
SYS    SEQUENCE    158
SYS    SYNONYM    19
SYS    TABLE    1265
SYS    TRIGGER    11
SYS    VIEW    5728
SYS        9388
SYSTEM    FUNCTION    4
SYSTEM    INDEX    236
SYSTEM    PACKAGE    1
SYSTEM    PROCEDURE    1
SYSTEM    SEQUENCE    22
SYSTEM    SYNONYM    8
SYSTEM    TABLE    178
SYSTEM    TRIGGER    2
SYSTEM    VIEW    14
SYSTEM        466
    FUNCTION    117
    INDEX    1460
    PACKAGE    721
    PROCEDURE    151
    SEQUENCE    180
    SYNONYM    27
    TABLE    1443
    TRIGGER    13
    VIEW    5742
        9854


Изменим порядок столбцов в cube()

select own, type, count(text) from t
group by cube(type, own)
order by  type, own;

SYS    FUNCTION    113
SYSTEM    FUNCTION    4
    FUNCTION    117
SYS    INDEX    1224
SYSTEM    INDEX    236
    INDEX    1460
SYS    PACKAGE    720
SYSTEM    PACKAGE    1
    PACKAGE    721
SYS    PROCEDURE    150
SYSTEM    PROCEDURE    1
    PROCEDURE    151
SYS    SEQUENCE    158
SYSTEM    SEQUENCE    22
    SEQUENCE    180
SYS    SYNONYM    19
SYSTEM    SYNONYM    8
    SYNONYM    27
SYS    TABLE    1265
SYSTEM    TABLE    178
    TABLE    1443
SYS    TRIGGER    11
SYSTEM    TRIGGER    2
    TRIGGER    13
SYS    VIEW    5728
SYSTEM    VIEW    14
    VIEW    5742
SYS        9388
SYSTEM        466
        9854



Функция GROUPING() принимает столбец, а возвращает 0 или 1
Если значение столбца null, то возвращает 1
если значение столбца не пустое, то возвращает 0

Это может пригодиться:

select  type, count(text) from t
group by cube(type)
order by  type;

FUNCTION    117
INDEX    1460
PACKAGE    721
PROCEDURE    151
SEQUENCE    180
SYNONYM    27
TABLE    1443
TRIGGER    13
VIEW    5742
    9854


select grouping(type),  type,  count(text)  from t
group by cube(type)
order by  type;

0    FUNCTION    117
0    INDEX    1460
0    PACKAGE    721
0    PROCEDURE    151
0    SEQUENCE    180
0    SYNONYM    27
0    TABLE    1443
0    TRIGGER    13
0    VIEW    5742
1        9854


select
case grouping(type)
    when 1 then 'Итог:'
    else type
end as type,
count(text)  from t
group by cube(type)
order by  type;

FUNCTION    117
INDEX    1460
PACKAGE    721
PROCEDURE    151
SEQUENCE    180
SYNONYM    27
TABLE    1443
TRIGGER    13
VIEW    5742
Итог:    9854


Конвертация значений нескольких столбцов:

select
case grouping(own)
    when 1 then 'Все own : '
    else own
end as own,
case grouping(type)
    when 1 then 'Все type : '
    else type
end as type,
count(text) from t
group by rollup(own, type)
order by  own, type;

SYS    FUNCTION    113
SYS    INDEX    1224
SYS    PACKAGE    720
SYS    PROCEDURE    150
SYS    SEQUENCE    158
SYS    SYNONYM    19
SYS    TABLE    1265
SYS    TRIGGER    11
SYS    VIEW    5728
SYS    Все type :     9388
SYSTEM    FUNCTION    4
SYSTEM    INDEX    236
SYSTEM    PACKAGE    1
SYSTEM    PROCEDURE    1
SYSTEM    SEQUENCE    22
SYSTEM    SYNONYM    8
SYSTEM    TABLE    178
SYSTEM    TRIGGER    2
SYSTEM    VIEW    14
SYSTEM    Все type :     466
Все own :     Все type :     9854


С использованием фразы cube:

select
case grouping(own)
    when 1 then 'Все own : '
    else own
end as own,
case grouping(type)
    when 1 then 'Все type : '
    else type
end as type,
count(text) from t
group by cube(own, type)
order by  own, type;

SYS    FUNCTION    113
SYS    INDEX    1224
SYS    PACKAGE    720
SYS    PROCEDURE    150
SYS    SEQUENCE    158
SYS    SYNONYM    19
SYS    TABLE    1265
SYS    TRIGGER    11
SYS    VIEW    5728
SYS    Все type :     9388
SYSTEM    FUNCTION    4
SYSTEM    INDEX    236
SYSTEM    PACKAGE    1
SYSTEM    PROCEDURE    1
SYSTEM    SEQUENCE    22
SYSTEM    SYNONYM    8
SYSTEM    TABLE    178
SYSTEM    TRIGGER    2
SYSTEM    VIEW    14
SYSTEM    Все type :     466
Все own :     FUNCTION    117
Все own :     INDEX    1460
Все own :     PACKAGE    721
Все own :     PROCEDURE    151
Все own :     SEQUENCE    180
Все own :     SYNONYM    27
Все own :     TABLE    1443
Все own :     TRIGGER    13
Все own :     VIEW    5742
Все own :     Все type :     9854



Если необходимо получить только промежуточные итоги, используйте фразу
GROUPING SETS

select own, type, count(text) from t
group by grouping sets(own, type)
order by  own, type;

SYS        9388
SYSTEM        466
    FUNCTION    117
    INDEX    1460
    PACKAGE    721
    PROCEDURE    151
    SEQUENCE    180
    SYNONYM    27
    TABLE    1443
    TRIGGER    13
    VIEW    5742


Ранее мы рассмотрели функцию grouping()
она работала так:

grouping(null)      ->  1
grouping(not null)  ->  0

Существует функция grouping_id()
она принимает два аргумента и возвращает следующие значения:

grouping_id(null, null)          ->  0
grouping_id(null, not null)      ->  1
grouping_id(not null, null)      ->  2
grouping_id(not null, not null)  ->  3


Где это может быть полезно ?


select own, type,
grouping(own) as own_grp,
grouping(type) as type_grp,
grouping_id(own, type)  as grp_id,
count(text) from t
group by cube(own, type)
order by  own, type;



SYS    FUNCTION    0    0    0    113
SYS    INDEX    0    0    0    1224
SYS    PACKAGE    0    0    0    720
SYS    PROCEDURE    0    0    0    150
SYS    SEQUENCE    0    0    0    158
SYS    SYNONYM    0    0    0    19
SYS    TABLE    0    0    0    1265
SYS    TRIGGER    0    0    0    11
SYS    VIEW    0    0    0    5728
SYS        0    1    1    9388
SYSTEM    FUNCTION    0    0    0    4
SYSTEM    INDEX    0    0    0    236
SYSTEM    PACKAGE    0    0    0    1
SYSTEM    PROCEDURE    0    0    0    1
SYSTEM    SEQUENCE    0    0    0    22
SYSTEM    SYNONYM    0    0    0    8
SYSTEM    TABLE    0    0    0    178
SYSTEM    TRIGGER    0    0    0    2
SYSTEM    VIEW    0    0    0    14
SYSTEM        0    1    1    466
    FUNCTION    1    0    2    117
    INDEX    1    0    2    1460
    PACKAGE    1    0    2    721
    PROCEDURE    1    0    2    151
    SEQUENCE    1    0    2    180
    SYNONYM    1    0    2    27
    TABLE    1    0    2    1443
    TRIGGER    1    0    2    13
    VIEW    1    0    2    5742
        1    1    3    9854




Одно из полезных применений функции grouping_id() - это фильтрация строк при помощи фразы HAVING.

select own, type,
grouping_id(own, type)  as grp_id,
count(text) from t
group by cube(own, type)
having grouping_id(own, type)  > 0
order by  own, type;

SYS        1    9388
SYSTEM        1    466
    FUNCTION    2    117
    INDEX    2    1460
    PACKAGE    2    721
    PROCEDURE    2    151
    SEQUENCE    2    180
    SYNONYM    2    27
    TABLE    2    1443
    TRIGGER    2    13
    VIEW    2    5742
        3    9854




Еще примеры:

Рассмотрим такой запрос:

select own, type, count(text) from t
group by own, rollup(own, type);

SYS    VIEW    5728
SYS    INDEX    1224
SYS    TABLE    1265
SYS    PACKAGE    720
SYS    SYNONYM    19
SYS    TRIGGER    11
SYS    FUNCTION    113
SYS    SEQUENCE    158
SYS    PROCEDURE    150
SYSTEM    VIEW    14
SYSTEM    INDEX    236
SYSTEM    TABLE    178
SYSTEM    PACKAGE    1
SYSTEM    SYNONYM    8
SYSTEM    TRIGGER    2
SYSTEM    FUNCTION    4
SYSTEM    SEQUENCE    22
SYSTEM    PROCEDURE    1
SYS        9388
SYSTEM        466
SYS        9388
SYSTEM        466


Здесь мы во фразе group by дважды сгруппировали
сначала по столбцу own, а затем по фразе rollup()

Но последние две строки выходных данных, дублируют предыдущие.

Чтобы это исправить, воспользуемся функцией group_id()

Функция group_id() не принимает никаких параметров.
Если в какой либо конкретной группировке есть n дубликатов, group_id() возвратит числа в диапазоне от 0 до n-1.

select own, type, group_id(), count(text) from t
group by own, rollup(own, type);

SYS    VIEW    0    5728
SYS    INDEX    0    1224
SYS    TABLE    0    1265
SYS    PACKAGE    0    720
SYS    SYNONYM    0    19
SYS    TRIGGER    0    11
SYS    FUNCTION    0    113
SYS    SEQUENCE    0    158
SYS    PROCEDURE    0    150
SYSTEM    VIEW    0    14
SYSTEM    INDEX    0    236
SYSTEM    TABLE    0    178
SYSTEM    PACKAGE    0    1
SYSTEM    SYNONYM    0    8
SYSTEM    TRIGGER    0    2
SYSTEM    FUNCTION    0    4
SYSTEM    SEQUENCE    0    22
SYSTEM    PROCEDURE    0    1
SYS        0    9388
SYSTEM        0    466
SYS        1    9388
SYSTEM        1    466


Теперь две последние строки можно отфильтровать во фразе having:


select own, type, group_id(), count(text) from t
group by own, rollup(own, type)
having group_id() = 0;


SYS    VIEW    0    5728
SYS    INDEX    0    1224
SYS    TABLE    0    1265
SYS    PACKAGE    0    720
SYS    SYNONYM    0    19
SYS    TRIGGER    0    11
SYS    FUNCTION    0    113
SYS    SEQUENCE    0    158
SYS    PROCEDURE    0    150
SYSTEM    VIEW    0    14
SYSTEM    INDEX    0    236
SYSTEM    TABLE    0    178
SYSTEM    PACKAGE    0    1
SYSTEM    SYNONYM    0    8
SYSTEM    TRIGGER    0    2
SYSTEM    FUNCTION    0    4
SYSTEM    SEQUENCE    0    22
SYSTEM    PROCEDURE    0    1
SYS        0    9388
SYSTEM        0    466