相关文章推荐
怕老婆的仙人球  ·  My monitor is going ...·  9 月前    · 
怕老婆的仙人球  ·  CodeIgniter4 Email ...·  9 月前    · 
怕老婆的仙人球  ·  libusb-win32 - ...·  9 月前    · 
怕老婆的仙人球  ·  Sysinternals Desktops ...·  10 月前    · 
怕老婆的仙人球  ·  使用 HttpClient ...·  10 月前    · 
挂过科的企鹅  ·  Java ...·  1小时前    · 
鼻子大的甘蔗  ·  epplus ...·  1小时前    · 
稳重的警车  ·  jquery beforeunload方法 ...·  3 小时前    · 

В этом учебном пособии вы узнаете, как использовать оператор WITH в Oracle PL/SQL с синтаксисом и примерами.

Описание

Oracle PL/SQL оператор WITH позволяет дать блоку подзапроса имя/псевдоним, на которое можно ссылаться в нескольких местах основного SQL-запроса. Имя, присвоенное подзапросу, обрабатывается так, как если бы оно было встроенным представлением или таблицей. SQL оператор WITH по сути является заменой обычному подзапросу.

Синтаксис

Cинтаксис Oracle PL/SQL WITH с одним подзапросом:

WITH query_name AS (SELECT expressions FROM table_A)
SELECT column_list
FROM query_name [,table_name]
[WHERE conditions]

Cинтаксис Oracle PL/SQL WITH с с несколькими подзапросами:

WITH query_name_A AS
(SELECT expressions FROM table_A),
query_name_B AS
([SELECT expressions FROM query_name_A] | [SELECT expressions FROM table_B])
SELECT column_list
FROM query_name_A, query_name_B [,table_X | Join table_Z]
[WHERE conditions]

expressions - поля или расчеты подзапроса.
column_list - поля или расчеты основного запроса.
table_A , table_B , table_X , table_Z - таблицы или соединения для подзапросов.
query_name_A , query_name_B - псевдоним подзапроса. Если подзапросов несколько, то они перечисляются через запятую.
WHERE conditions - условия которые должны быть выполнены для основных запросов.

Примечание

  • Формально предложение WITH называется факторингом подзапроса.
  • Предложение SQL WITH используется, когда подзапрос выполняется несколько раз.
  • Подзапросы в WITH перечисляются через запятую.
  • Применение

  • SQL-предложение WITH было введено Oracle в базе данных Oracle 9i выпуск 2.
  • Начиная с Oracle Database 12c Release 1 (12.1) в операторе WITH можно определять функции и процедуры.
  • Примеры Oracle PL/SQL оператор WITH

  • Пример WITH с одним подзапросом
  • Пример WITH с двумя подзапросами
  • Пример WITH с функцией
  • Пример WITH с процедурой
  • Пример PRAGMA UDF
  • Для примера использования оператор WITH, с одним и двумя подзапросами, создадим несколько таблиц с данными.

    --удалим таблицы из базы данных, если существуют -- DROP TABLE EMP PURGE; -- DROP TABLE DEPT PURGE; --Создадим две таблицы EMP и DEPT: CREATE TABLE DEPT ( DEPTNO NUMBER(2) CONSTRAINT PK_DEPT PRIMARY KEY, DNAME VARCHAR2(14), CITY VARCHAR2(15) CREATE TABLE EMP ( EMPNO NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY, ENAME VARCHAR2(10), JOB VARCHAR2(9), MGR NUMBER(4), HIREDATE DATE, SAL NUMBER(7,2), COMM NUMBER(7,2), DEPTNO NUMBER(2) CONSTRAINT FK_DEPTNO REFERENCES DEPT --И добавим в таблицы EMP и DEPT следующие данные: --DEPT INSERT INTO DEPT VALUES (10,'ACCOUNTING','NEW YORK'); INSERT INTO DEPT VALUES (20,'RESEARCH','DALLAS'); INSERT INTO DEPT VALUES (30,'SALES','CHICAGO'); INSERT INTO DEPT VALUES (40,'OPERATIONS','BOSTON'); --EMP INSERT INTO EMP VALUES (7369,'SMITH','CLERK',7902,to_date('17.12.1980','dd.mm.yyyy'),800,NULL,20); INSERT INTO EMP VALUES (7499,'ALLEN','SALESMAN',7698,to_date('20.2.1981','dd.mm.yyyy'),1600,300,30); INSERT INTO EMP VALUES (7521,'WARD','SALESMAN',7698,to_date('22.2.1981','dd.mm.yyyy'),1250,500,30); INSERT INTO EMP VALUES (7566,'JONES','MANAGER',7839,to_date('2.4.1981','dd.mm.yyyy'),2975,NULL,20); INSERT INTO EMP VALUES (7654,'MARTIN','SALESMAN',7698,to_date('28.9.1981','dd.mm.yyyy'),1250,1400,30); INSERT INTO EMP VALUES (7698,'BLAKE','MANAGER',7839,to_date('1.5.1981','dd.mm.yyyy'),2850,NULL,30); INSERT INTO EMP VALUES (7782,'CLARK','MANAGER',7839,to_date('9.6.1981','dd.mm.yyyy'),2450,NULL,10); INSERT INTO EMP VALUES (7788,'SCOTT','ANALYST',7566,to_date('13.7.87','dd.mm.rr').85,3000,NULL,20); INSERT INTO EMP VALUES (7839,'KING','PRESIDENT',NULL,to_date('17.11.1981','dd.mm.yyyy'),5000,NULL,10); INSERT INTO EMP VALUES (7844,'TURNER','SALESMAN',7698,to_date('8.9.1981','dd.mm.yyyy'),1500,0,30); INSERT INTO EMP VALUES (7876,'ADAMS','CLERK',7788,to_date('13.7.87', 'dd.mm.rr').51,1100,NULL,20); INSERT INTO EMP VALUES (7900,'JAMES','CLERK',7698,to_date('3.12.1981','dd.mm.yyyy'),950,NULL,30); INSERT INTO EMP VALUES (7902,'FORD','ANALYST',7566,to_date('3.12.1981','dd.mm.yyyy'),3000,NULL,20); INSERT INTO EMP VALUES (7934,'MILLER','CLERK',7782,to_date('23.1.1982','dd.mm.yyyy'),1300,NULL,10); COMMIT;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    --удалим таблицы из базы данных, если существуют
    -- DROP TABLE EMP PURGE;
    -- DROP TABLE DEPT PURGE;
    --Создадим две таблицы EMP и DEPT:
    CREATE TABLE DEPT (
    DEPTNO NUMBER (2) CONSTRAINT PK_DEPT PRIMARY KEY ,
    DNAME VARCHAR2 (14),
    CITY VARCHAR2 (15)
    ) ;
    CREATE TABLE EMP (
    EMPNO NUMBER (4) CONSTRAINT PK_EMP PRIMARY KEY ,
    ENAME VARCHAR2 (10),
    JOB VARCHAR2 (9),
    MGR NUMBER (4),
    HIREDATE DATE ,
    SAL NUMBER (7,2),
    COMM NUMBER (7,2),
    DEPTNO NUMBER (2) CONSTRAINT FK_DEPTNO REFERENCES DEPT
    );
    --И добавим в таблицы EMP и DEPT следующие данные:
    --DEPT
    INSERT INTO DEPT VALUES (10, 'ACCOUNTING' , 'NEW YORK' );
    INSERT INTO DEPT VALUES (20, 'RESEARCH' , 'DALLAS' );
    INSERT INTO DEPT VALUES (30, 'SALES' , 'CHICAGO' );
    INSERT INTO DEPT VALUES (40, 'OPERATIONS' , 'BOSTON' );
    --EMP
    INSERT INTO EMP VALUES (7369, 'SMITH' , 'CLERK' ,7902, to_date ( '17.12.1980' , 'dd.mm.yyyy' ),800, NULL ,20);
    INSERT INTO EMP VALUES (7499, 'ALLEN' , 'SALESMAN' ,7698, to_date ( '20.2.1981' , 'dd.mm.yyyy' ),1600,300,30);
    INSERT INTO EMP VALUES (7521, 'WARD' , 'SALESMAN' ,7698, to_date ( '22.2.1981' , 'dd.mm.yyyy' ),1250,500,30);
    INSERT INTO EMP VALUES (7566, 'JONES' , 'MANAGER' ,7839, to_date ( '2.4.1981' , 'dd.mm.yyyy' ),2975, NULL ,20);
    INSERT INTO EMP VALUES (7654, 'MARTIN' , 'SALESMAN' ,7698, to_date ( '28.9.1981' , 'dd.mm.yyyy' ),1250,1400,30);
    INSERT INTO EMP VALUES (7698, 'BLAKE' , 'MANAGER' ,7839, to_date ( '1.5.1981' , 'dd.mm.yyyy' ),2850, NULL ,30);
    INSERT INTO EMP VALUES (7782, 'CLARK' , 'MANAGER' ,7839, to_date ( '9.6.1981' , 'dd.mm.yyyy' ),2450, NULL ,10);
    INSERT INTO EMP VALUES (7788, 'SCOTT' , 'ANALYST' ,7566, to_date ( '13.7.87' , 'dd.mm.rr' ).85,3000, NULL ,20);
    INSERT INTO EMP VALUES (7839, 'KING' , 'PRESIDENT' , NULL , to_date ( '17.11.1981' , 'dd.mm.yyyy' ),5000, NULL ,10);
    INSERT INTO EMP VALUES (7844, 'TURNER' , 'SALESMAN' ,7698, to_date ( '8.9.1981' , 'dd.mm.yyyy' ),1500,0,30);
    INSERT INTO EMP VALUES (7876, 'ADAMS' , 'CLERK' ,7788, to_date ( '13.7.87' , 'dd.mm.rr' ).51,1100, NULL ,20);
    INSERT INTO EMP VALUES (7900, 'JAMES' , 'CLERK' ,7698, to_date ( '3.12.1981' , 'dd.mm.yyyy' ),950, NULL ,30);
    INSERT INTO EMP VALUES (7902, 'FORD' , 'ANALYST' ,7566, to_date ( '3.12.1981' , 'dd.mm.yyyy' ),3000, NULL ,20);
    INSERT INTO EMP VALUES (7934, 'MILLER' , 'CLERK' ,7782, to_date ( '23.1.1982' , 'dd.mm.yyyy' ),1300, NULL ,10);
    COMMIT ;

    Пример WITH с одним подзапросом

    Рассмотрим пример sql оператора with c одним подзапросом, чтобы понять как использовать оператор with в Oracle PL/SQL.
    Например:

    В этом примере мы создали подзапрос с псевдонимом employee из таблицы Employees (сотрудников). Затем с помощью двух основных запросов и оператора UNION ALL запросили данные всех сотрудников у которых empno > 7900 и всех сотрудников у которых sal > 3000.

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

    Пример WITH с двумя подзапросами

    Для примера использования оператор WITH, создадим несколько таблиц с данными.

    Например нам может понадобится выбрать отделы с заработной платой выше среднего. Для этого сначала в первом подзапросе dept_costs определим сумму зарплат по отделам. Затем во втором подзапросе avg_cost определим среднюю зарплату по отделам.
    Например.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    WITH
    dept_costs AS (
    SELECT dname, SUM (sal) dept_total
    FROM emp e, dept d
    WHERE e.deptno = d.deptno
    GROUP BY dname
    ),
    avg_cost AS (
    SELECT SUM (dept_total)/ COUNT (*) avg
    FROM dept_costs
    )
    SELECT *
    FROM dept_costs
    WHERE dept_total > ( SELECT avg FROM avg_cost)
    ORDER BY dname;
    -- Сбор статистики на CTAS больше не требуется в 12c, -- при условии, что запрос выдается не-SYS пользователем. -- EXEC DBMS_STATS.gather_table_stats (USER, 't1');

    С точки зрения разрешения имен функций, определенных в разделе объявлений PL/SQL оператора WITH, имеют приоритет над объектами с тем же именем, определенным на уровне схемы.

    Пример WITH с процедурой

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    WITH
    PROCEDURE with_procedure(p_id IN NUMBER ) IS
    BEGIN
    DBMS_OUTPUT.put_line( 'p_id=' || p_id);
    END ;
    FUNCTION with_function(p_id IN NUMBER ) RETURN NUMBER IS
    BEGIN
    with_procedure(p_id);
    RETURN p_id;
    END ;
    SELECT with_function(id)
    FROM t1
    WHERE rownum = 1
    --результат
    WITH_FUNCTION(ID)
    -----------------
    1
    p_id = 1
    ORA-06550: line 3, column 30: PL/SQL: ORA-00905: missing keyword ORA-06550: line 2, column 19: PL/SQL: SQL Statement ignored ORA-06550: line 5, column 34: PLS-00103: Encountered the symbol ";" when expecting one of the following:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    BEGIN
    FOR cur_rec IN ( WITH
    FUNCTION with_function(p_id IN NUMBER ) RETURN NUMBER IS
    BEGIN
    RETURN p_id;
    END ;
    SELECT with_function(id)
    FROM t1
    WHERE rownum = 1)
    LOOP
    NULL ;
    END LOOP;
    END ;
    FUNCTION with_function(p_id IN NUMBER ) RETURN NUMBER IS
    *
    ERROR at line 3:
    ORA - 06550: line 3, column 30:
    PL / SQL : ORA - 00905: missing keyword
    ORA - 06550: line 2, column 19:
    PL / SQL : SQL Statement ignored
    ORA - 06550: line 5, column 34:
    PLS - 00103: Encountered the symbol ";" when expecting one of the following:
    loop
    OPEN l_cursor FOR l_sql; FETCH l_cursor INTO l_value; DBMS_OUTPUT.put_line('l_value=' || l_value); CLOSE l_cursor; l_value=1 PL/SQL procedure successfully completed.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    SET SERVEROUTPUT ON
    DECLARE
    l_sql VARCHAR2 (32767);
    l_cursor SYS_REFCURSOR;
    l_value NUMBER ;
    BEGIN
    l_sql := 'WITH
    FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
    BEGIN
    RETURN p_id;
    END;
    SELECT with_function(id)
    FROM   t1
    WHERE  rownum = 1' ;
    OPEN l_cursor FOR l_sql;
    FETCH l_cursor INTO l_value;
    DBMS_OUTPUT.put_line( 'l_value=' || l_value);
    CLOSE l_cursor;
    END ;
    l_value = 1
    PL / SQL procedure successfully completed.

    Поддержка этой функции с использованием статического SQL внутри PL/SQL ожидается в следующем релизе Oracle.

    Преимущества производительности

    Вся причина определения встроенного кода PL/SQL заключается в повышении производительности.
    Создайте обычную функцию для использования в качестве сравнения.

    CREATE OR REPLACE FUNCTION normal_function(p_id IN NUMBER) RETURN NUMBER IS BEGIN RETURN p_id; DBMS_OUTPUT.put_line('WITH_FUNCTION : ' || 'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' || 'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs '); l_time := DBMS_UTILITY.get_time; l_cpu := DBMS_UTILITY.get_cpu_time; l_sql := 'SELECT normal_function(id) FROM t1'; OPEN l_cursor FOR l_sql; FETCH l_cursor BULK COLLECT INTO l_tab; CLOSE l_cursor; DBMS_OUTPUT.put_line('NORMAL_FUNCTION: ' || 'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' || 'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs '); WITH_FUNCTION : Time=45 hsecs CPU Time=39 hsecs NORMAL_FUNCTION: Time=129 hsecs CPU Time=113 hsecs PL/SQL procedure successfully completed.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    DECLARE
    l_time PLS_INTEGER ;
    l_cpu PLS_INTEGER ;
    l_sql VARCHAR2 (32767);
    l_cursor SYS_REFCURSOR;
    TYPE t_tab IS TABLE OF NUMBER ;
    l_tab t_tab;
    BEGIN
    l_time := DBMS_UTILITY.get_time;
    l_cpu := DBMS_UTILITY.get_cpu_time;
    l_sql := 'WITH
    FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
    BEGIN
    RETURN p_id;
    END;
    SELECT with_function(id)
    FROM   t1' ;
    OPEN l_cursor FOR l_sql;
    FETCH l_cursor
    BULK COLLECT INTO l_tab;
    CLOSE l_cursor;
    DBMS_OUTPUT.put_line( 'WITH_FUNCTION  : ' ||
    'Time=' || TO_CHAR (DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
    'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs ' );
    l_time := DBMS_UTILITY.get_time;
    l_cpu := DBMS_UTILITY.get_cpu_time;
    l_sql := 'SELECT normal_function(id)
    FROM   t1' ;
    OPEN l_cursor FOR l_sql;
    FETCH l_cursor
    BULK COLLECT INTO l_tab;
    CLOSE l_cursor;
    DBMS_OUTPUT.put_line( 'NORMAL_FUNCTION: ' ||
    'Time=' || TO_CHAR (DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
    'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs ' );
    END ;
    WITH_FUNCTION : Time = 45 hsecs CPU Time = 39 hsecs
    NORMAL_FUNCTION: Time = 129 hsecs CPU Time = 113 hsecs
    PL / SQL procedure successfully completed.

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

    Пример PRAGMA UDF

    В ряде презентаций, предшествовавших официальному выпуску 12c, выступавшие упоминали PRAGMA UDF(User Defined Function), которая предположительно дает вам преимущества производительности встроенного PL/SQL, в то же время позволяя вам определять объект PL/SQL вне оператора SQL. Следующий код переопределяет предыдущую обычную функцию для использования этой прагмы.

    CREATE OR REPLACE FUNCTION normal_function(p_id IN NUMBER) RETURN NUMBER IS PRAGMA UDF; BEGIN RETURN p_id; DBMS_OUTPUT.put_line('WITH_FUNCTION : ' || 'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' || 'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs '); l_time := DBMS_UTILITY.get_time; l_cpu := DBMS_UTILITY.get_cpu_time; l_sql := 'SELECT normal_function(id) FROM t1'; OPEN l_cursor FOR l_sql; FETCH l_cursor BULK COLLECT INTO l_tab; CLOSE l_cursor; DBMS_OUTPUT.put_line('NORMAL_FUNCTION: ' || 'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' || 'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs '); WITH_FUNCTION : Time=44 hsecs CPU Time=40 hsecs NORMAL_FUNCTION: Time=33 hsecs CPU Time=29 hsecs PL/SQL procedure successfully completed.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    SET SERVEROUTPUT ON
    DECLARE
    l_time PLS_INTEGER ;
    l_cpu PLS_INTEGER ;
    l_sql VARCHAR2 (32767);
    l_cursor SYS_REFCURSOR;
    TYPE t_tab IS TABLE OF NUMBER ;
    l_tab t_tab;
    BEGIN
    l_time := DBMS_UTILITY.get_time;
    l_cpu := DBMS_UTILITY.get_cpu_time;
    l_sql := 'WITH
    FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
    BEGIN
    RETURN p_id;
    END;
    SELECT with_function(id)
    FROM   t1' ;
    OPEN l_cursor FOR l_sql;
    FETCH l_cursor
    BULK COLLECT INTO l_tab;
    CLOSE l_cursor;
    DBMS_OUTPUT.put_line( 'WITH_FUNCTION  : ' ||
    'Time=' || TO_CHAR (DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
    'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs ' );
    l_time := DBMS_UTILITY.get_time;
    l_cpu := DBMS_UTILITY.get_cpu_time;
    l_sql := 'SELECT normal_function(id)
    FROM   t1' ;
    OPEN l_cursor FOR l_sql;
    FETCH l_cursor
    BULK COLLECT INTO l_tab;
    CLOSE l_cursor;
    DBMS_OUTPUT.put_line( 'NORMAL_FUNCTION: ' ||
    'Time=' || TO_CHAR (DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
    'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs ' );
    END ;
    WITH_FUNCTION : Time = 44 hsecs CPU Time = 40 hsecs
    NORMAL_FUNCTION: Time = 33 hsecs CPU Time = 29 hsecs
    PL / SQL procedure successfully completed.

    Кажется, что автономная функция, использующая PRAGMA UDF, последовательно выполняет встроенную функцию.

    У меня сложилось впечатление, что вызов функции, определенной с помощью PRAGMA UDF напрямую из PL / SQL, не удастся. Это не похоже на случайность.

     
    推荐文章