Oracle

게시글 보기
작성자 유건데이타 등록일 2015-05-16
제목 DYNAMIC SQL 이란?
DYNAMIC SQL 이란?
==================
PL/SQL은 binding이 compile 시에 일어나므로 database object의 name이
compile 시에 고정되어야 하는 등의 제한이 있다. PL/SQL 2.1(RDBMS 7.1) 이후
version에서는 DBMS_SQL package로 Dynamic SQL statement의 사용을 가능하게
한다.
이는 database object의 name을 runtime에 줄 수 있을 뿐만 아니라,
DDL 문장을 기술할 수도 있는 장점이 있다.

Function open_cursor
: SQL문의 실행에 필요한 새로운 cursor를 열고 cursor ID number를 return한다.

Function is_open
: 주어진 cursor가 현재 open되어 있으면 TRUE를, 아니면 FALSE를 return한다.

Procedure parse
: statement를 check하고 cursor와 결합시킨다.

Procedure bind_variable
: program 내에서 data를 저장한 placeholder의 값을 제공하는 역할을 한다.

Procedure define_column
: cursor로부터 select된 column의 값을 받는 변수를 지정한다.

Function execute
: SQL문을 실행하고 처리된 Row의 수를 return한다.
(insert, update, delete인 경우에만 해당)

Function fetch_rows
: cursor로부터 row를 fetch하고 실제로 fetch된 row의 수를 return 한다.
이 row들은 buffer에 들어가며, column_value를 호출하여 읽어들여야 한다.

Function execute_and_fetch
: execute와 fetch row를 동시에 수행하고 실제로 fetch된 row의 수를 return
한다.

Procedure variable_value
: 주어진 변수의 값을 return한다.

Procedure column_value
: fetch_rows에 의해 fetch된 data의 값을 return한다.

Procedure close_cursor
: cursor를 닫는다.


Using the DBMS_SQL Package to Execute DDL statements:
-----------------------------------------------------

< Example 1: >

이 procedure는 table을 create하는 것으로 table name과 column, type을
parameter로 받는다.


CREATE OR REPLACE PROCEDURE ddlproc (tablename varchar2, cols varchar2)
AS
cursor1 INTEGER;
BEGIN
cursor1 := dbms_sql.open_cursor;
dbms_sql.parse(cursor1, 'CREATE TABLE ' || tablename ||
' ( ' || cols || ' )', dbms_sql.v7);
dbms_sql.close_cursor(cursor1);
end;
/

SQL> execute ddlproc ('MYTABLE', 'COL1 NUMBER, COL2 VARCHAR2(10)');

PL/SQL procedure successfully completed.

SQL> desc mytable;
Name Null? Type
------------------------------- -------- ----
COL1 NUMBER
COL2 VARCHAR2(10)

DDL statement는 parse command에 의해 수행된다.
그러므로, DDL statement에서는 bind variable을 사용할 수가 없다.

-------------------------------------------------------------------
위의 예에서 CREATE command 시 SQL*Plus에서는 잘 되지만 dbms_sql 내에서
수행 시 privilege가 없다는 error가 나면 system user가 해당 User에게
CREATE TABLE (VIEW ..) privilege를 주어야 한다.

SQL> GRANT CREATE TABLE TO scott;
-------------------------------------------------------------------


다음은 DDL statement 내에 bind variable을 사용한 잘못된 예이다.

CREATE OR REPLACE PROCEDURE ddlproc (tablename VARCHAR2,
colname VARCHAR2, coltype VARCHAR2)
AS
cursor1 INTEGER;
ignore INTEGER;
BEGIN
cursor1 := dbms_sql.open_cursor;
dbms_sql.parse(cursor1, 'CREATE TABLE :x1 (:y1 :z1)',
dbms_sql.v7);
dbms_sql.bind_variable(cursor1, ':x1', tablename);
dbms_sql.bind_variable(cursor1, ':y1', colname);
dbms_sql.bind_variable(cursor1, ':z1', coltype);
ignore := dbms_sql.execute(cursor1);
dbms_sql.close_cursor(cursor1);
end;
/

Procedure를 create했을 때에는 error를 만나지 않았지만, runtime에는
"ORA-00903: invalid table name" 이라는 error가 난다.

SQL> execute ddlproc ('MYTABLE', 'COL1', 'NUMBER');
begin ddlproc ('MYTABLE', 'COL1', 'NUMBER'); end;

*
ERROR at line 1:
ORA-00903: invalid table name
ORA-06512: at "SYS.DBMS_SYS_SQL", line 239
ORA-06512: at "SYS.DBMS_SQL", line 25
ORA-06512: at "SCOTT.DDLPROC", line 8
ORA-06512: at line 1


< Example 2: >

이 procedure는 table을 drop하는 procedure로 table name을 parameter로 받는다.

create or replace procedure droptable (table_name varchar2) as
cursor1 integer;
begin
cursor1 := dbms_sql.open_cursor;
dbms_sql.parse(cursor1, 'DROP TABLE ' || table_name,
dbms_sql.v7);
dbms_sql.close_cursor(cursor1);
end;
/

SQL> begin
2 droptable('MYTABLE');
3 end;
4 /

PL/SQL procedure successfully completed.


< Example 3: >

이 procedure는 DDL statemenet를 수행하는 procedure로 DDL statement 자체를
parameter로 받는다.

create procedure anyddl (s1 varchar2) as
cursor1 integer;
begin
cursor1 := dbms_sql.open_cursor;
dbms_sql.parse(cursor1, s1, dbms_sql.v7);
dbms_sql.close_cursor(cursor1);
end;
/

SQL> execute anyddl('CREATE TABLE MYTABLE (COL1 NUMBER)');

PL/SQL procedure successfully completed.

SQL> desc mytable;
Name Null? Type
------------------------------- -------- ----
COL1 NUMBER

SQL> execute anyddl('drop table mytable');

PL/SQL procedure successfully completed.

Using the DBMS_SQL Package to Execute Dynamic SQL
Statements:
-------------------------------------------------------------

DBMS_SQL package dynamic SQL statement를 수행하는데 이용되어 질 수
있는데 이는 rumtime전에 statement의 일부분 혹은 전체를 알 수 없는 경우에
쓰여진다.


< Example 4: >

이 예제는 runtime 시에 주어진 number보다 더 높은 employee number를 가진
모든 employee의 이름과 employee number를 return한다.

CREATE or REPLACE PROCEDURE rows_greater_than (low_value number)
AS
cursor1 integer;
rows_processed integer;
myempno number;
myename varchar2(20);
BEGIN
cursor1 := dbms_sql.open_cursor;
dbms_sql.parse (cursor1, 'select empno, ename from emp
where empno > :x', dbms_sql.v7);
dbms_sql.bind_variable(cursor1, 'x', low_value);
dbms_sql.define_column (cursor1, 1, myempno);
dbms_sql.define_column (cursor1, 2, myename, 20);
rows_processed := dbms_sql.execute (cursor1);
loop
if dbms_sql.fetch_rows (cursor1) > 0 then
dbms_sql.column_value (cursor1, 1, myempno);
dbms_sql.column_value (cursor1, 2, myename);
dbms_output.put_line(to_char(myempno) || ' ' || myename);
else
exit;
end if;
end loop;
dbms_sql.close_cursor (cursor1);
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(sqlerrm);
if dbms_sql.is_open (cursor1) then
dbms_sql.close_cursor (cursor1);
end if;
END;
/

DBMS_OUTPUT package을 사용하기 전에 먼저 SET SERVEROUTPUT ON command를
사용하여야 한다.

SQL> set serveroutput on
SQL> execute rows_greater_than(7500);
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
8100 MILLER

PL/SQL procedure successfully completed.

SQL> execute rows_greater_than(8000);
8100 MILLER

PL/SQL procedure successfully completed.


< Example 5: >

이 예제는 where clause의 parameter를 column_name과 operator(<,<=,=,>=,>),
new_value로 받아서 employee 이름과 number를 return한다.


CREATE or REPLACE PROCEDURE get_rows (column_name varchar2,
comparison_type varchar2, new_value number)
AS
cursor1 integer;
rows_processed integer;
myempno number;
myename varchar2(20);
BEGIN
cursor1 := dbms_sql.open_cursor;
dbms_sql.parse (cursor1, 'select empno, ename from emp
where ' || column_name ||
' ' || comparison_type || ' :x',
dbms_sql.v7);
dbms_sql.bind_variable(cursor1, 'x', new_value);
dbms_sql.define_column (cursor1, 1, myempno);
dbms_sql.define_column (cursor1, 2, myename, 20);
rows_processed := dbms_sql.execute (cursor1);
loop
if dbms_sql.fetch_rows (cursor1) > 0 then
dbms_sql.column_value (cursor1, 1, myempno);
dbms_sql.column_value (cursor1, 2, myename);
dbms_output.put_line(to_char(myempno) || ' ' || myename);
else
exit;
end if;
end loop;
dbms_sql.close_cursor (cursor1);
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(sqlerrm);
if dbms_sql.is_open (cursor1) then
dbms_sql.close_cursor (cursor1);
end if;
END;
/

SQL> begin
2 get_rows('EMPNO', '<', 2000);
3 end;
4 /
1111

PL/SQL procedure successfully completed.

SQL> execute get_rows('SAL', '>', 3000);
7566 JONES
7788 SCOTT
7839 KING
7902 FORD

PL/SQL procedure successfully completed.

SQL> begin
2 get_rows('DEPTNO', '>=', 20);
3 end;
4 /
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7788 SCOTT
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD

PL/SQL procedure successfully completed.


< Example 6: >

이 예제는 where clause 전체를 runtime 시에 parameter로 받는다.

CREATE or REPLACE PROCEDURE get_rows (where_clause varchar2) AS
cursor1 integer;
rows_processed integer;
myempno number;
myename varchar2(20);
BEGIN
cursor1 := dbms_sql.open_cursor;
dbms_sql.parse (cursor1, 'select empno, ename from emp where '
|| where_clause, dbms_sql.v7);
dbms_sql.define_column (cursor1, 1, myempno);
dbms_sql.define_column (cursor1, 2, myename, 20);
rows_processed := dbms_sql.execute (cursor1);
loop
if dbms_sql.fetch_rows (cursor1) > 0 then
dbms_sql.column_value (cursor1, 1, myempno);
dbms_sql.column_value (cursor1, 2, myename);
dbms_output.put_line(to_char(myempno) || ' ' || myename);
else
exit;
end if;
end loop;
dbms_sql.close_cursor (cursor1);
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(sqlerrm);
if dbms_sql.is_open (cursor1) then
dbms_sql.close_cursor (cursor1);
end if;
END;
/

SQL> execute get_rows('ENAME = ''KING'' ');
7839 KING

PL/SQL procedure successfully completed.

SQL> execute get_rows('SAL > 1000 AND DEPTNO = 10');
7782 CLARK
7839 KING
8100 MILLER

PL/SQL procedure successfully completed.


< Example 7: >

이 예제는 non-query SQL statement를 실행한다.

create procedure anysql (s1 varchar2) as
cursor1 integer;
return_value integer;
begin
cursor1 := dbms_sql.open_cursor;
dbms_sql.parse(cursor1, s1, dbms_sql.v7);
return_value := dbms_sql.execute(cursor1);
dbms_sql.close_cursor(cursor1);
end;
/


SQL> execute anysql('CREATE TABLE MYTABLE (COL1 number, col2 varchar2(3))');

PL/SQL procedure successfully completed.

SQL
SQL> desc mytable;
Name Null? Type
------------------------------- -------- ----
COL1 NUMBER
COL2 VARCHAR2(3)

SQL> execute anysql('INSERT INTO MYTABLE VALUES(1, ''ABC'')');

PL/SQL procedure successfully completed.

SQL> begin
2 anysql(
3 'declare
4 var1 varchar2(3);
5 begin
6 select col2
7 into var1
8 from mytable
9 where col1 = 1;
10 dbms_output.put_line(''var1 = '' || var1);
11 end;');
12 end;
13 /
var1 = ABC

PL/SQL procedure successfully completed.


이 예제는 procedure를 생성하는 예제이다

create or replace procedure test(txt varchar2) as
c integer;
rows integer;
begin
c := dbms_sql.open_cursor;
dbms_sql.parse(c,'create or replace '|| txt ,dbms_sql.native);
rows := dbms_sql.execute(c);
dbms_sql.close_cursor(c);
end;

SQL>exec test('procedure ttum(aa number) AS kk number; BEGIN kk:= 0 ;
kk := kk + aa ; END ; ');

SQL> exec test ('procedure ttt1(aa number) as kk varchar2(10);
begin select 1 into kk from dual; end;');

***********************************************************************
보다 자세한 사항은 Oracle7 Server Documentation Addendum을 참조.
***********************************************************************

Forms 3.0은 pl/sql 1.1만을 지원하므로 dynamic SQL을 쓸 수 없으며 stored
procedure를 만들어 호출해서 사용해야 한다.
이 때 forms가 부른 stored procedure나 function은 그 안에서 commit을 사용할
수 없게 되어 있기 때문에 COMMIT이나 ROLLBACK을 만나면

ORA-00034: Commit and Rollback from PL/SQL disabled for this session.

이란 error가 발생한다.

그러므로, DDL statement도 사용할 수 없다.

만약, ORA-1031 에러가 발생하면 다음과 같이 해 주십시오.

sys user connect
@?/rdbms/admin/dbmssql.sql
grant create any table to [user_name];
grant execute on dbms_sql to [user_name];
CREATE ANY TABLE 권한은 DBA ROLE에도 포함되어 있지 않습니다.
Comment
등록된 코멘트가 없습니다.