database.sarang.net
UserID
Passwd
Database
DBMS
MySQL
ㆍPostgreSQL
Firebird
Oracle
Informix
Sybase
MS-SQL
DB2
Cache
CUBRID
LDAP
ALTIBASE
Tibero
DB 문서들
스터디
Community
공지사항
자유게시판
구인|구직
DSN 갤러리
도움주신분들
Admin
운영게시판
최근게시물
PostgreSQL Tutorials 3845 게시물 읽기
 News | Q&A | Columns | Tutorials | Devel | Files | Links
No. 3845
PL/pgSQL - SQL procedural language (2)
작성자
정재익(advance)
작성일
2002-01-07 03:45
조회수
10,070

24.2 세부 사항

 

24.2.1. PL/pgSQL 구조

 

PL/pgSQL는 블록 구조의 언어다. 모은 키워드나 식별자는 대.소문자를 섞어 사용할 수 있다. 한 블록은 다음과 같이 정의한다.

[<

블록의 '실행할 문장' 부분에는 얼마든지 하위-블록이 올 수 있다. 하위-블록은 외부 블록의 문장으로부터 변수를 숨기는데 사용될 수 있다.

 

변수는 'DECLARE' 부분에서 선언되고 초기화되는데 그 초기값을 줄 수 있고, 해당 블럭을 진입할 때마다 초기화 되는데 이것은 함수가 호출될 때 단 1번 수행되는 것이 아니다. 예를 들면...

CREATE FUNCTION somefunc() RETURNS INTEGER AS '
DECLARE
   quantity INTEGER := 30;
BEGIN
   RAISE NOTICE ''Quantity here is %'',quantity;  -- Quantity here is 30
   quantity := 50;
   --
   -- 하위-블럭의 생성
   --
   DECLARE
      quantity INTEGER := 80;
   BEGIN
      RAISE NOTICE ''Quantity here is %'',quantity;  -- Quantity here is 80
   END;

   RAISE NOTICE ''Quantity here is %'',quantity;  -- Quantity here is 50
END;
' LANGUAGE 'plpgsql';

위의 내용을 잘 이해해서 BEGIN/END 사용에 혼선이 없기를 바란다. BEGIN/END는 트랜젝션을 제어하는 데이터베이스 명령과 PL/pgSQL의 문장을 한 데 묶는 역할을 한다. PL/pgSQL의 BEGIN/END 는 오로지 문장을 한데 묶는데 사용한다. 그것들은 실제 트랜젝션의 시작과 끝이 아니다. function과 trigger procedure는 항상 외부 질의(outer query)에 의해 제공된 트렌젝션 내에서만 수행된다. 그 말은 Postgres는 중첩된 트렌젝션 기능이 없어서 스스로 트렌젝션을 시작하고, commit할 수 없다는 뜻이기도 하다.

 

24.2.2. 주석문

 

PL/pgSQL에는 두가지 형태의 주석문이 있다.

이중 빼기표시 '--'는 현재 위치부터 문장의 끝까지가 주석문이라는 의미이다.

그리고 '/*' 표시는 '*/'을 만날 때까지가 주석문이라는 의미이다.

주석문은 중첩될 수 없으나 이중 빼기표시 '--'는 이중빼기 표시 주석문의 내부나 '/*' '*/' 주석문 사이에 놓일 수 있다.

 

24.2.3. 변수와 상수

 

블럭 내에서 혹은 하위-블럭 내에서 사용할 모든 변수, row와 record는 반드시 'DECLARE'부 에서 선언되어야 한다. 그러나 FOR 반복문에서 사용되는 정수범위를 가진 반복용 변수는 예외이다.

 

PL/pgSQL 변수는 INTEGER, VARCHAR, CHAR와 같은 어떠한 SQL 자료형을 가질 수 있다. 모든 변수는 SQL NULL 값을 초기값으로 갖는다.

 

아래는 변수 선언의 몇가지 예를 들어본 것이다.

 

user_id INTEGER;

quantity NUMBER(5);

url VARCHAR;

 

24.2.3.1. 상수와 초기치를 갖는 변수

선언은 다음과 같은 문법을 가진다.

 

변수이름 [ CONSTANT ] 변수형 [ NOT NULL ] [ { DEFAULT | := } 값 ];

 

CONSTANT를 사용하여 정의된 변수의 값은 변경할 수 없다. 만일 변수를 선언할 때 NOT NULL을 사용했을 때 그 변수에 NULL값을 할당하게 되면 실행시간 에러가 발생한다. 변수를 NOT NULL로 선언하였으면 반드시 초기값을 명시해야 한다. 그 이유는 모든 변수의 초기값은 SQL NULL이기 때문이다.

 

변수에 할당된 초기값은 함수가 호출될 때마다 평가된다. 그래서 timestamp형의 변수에 'now'라는 것을 할당하는 것은 함수가 실제 호출될 순간의 시간이 저장되는 것이지 함수가 바이트코드로 해석될 때의 시간이 저장되는 것이 아니다.

 

예)

 

quantity INTEGER := 32;

url varchar := ''http://mysite.com'';

user_id CONSTANT INTEGER := 10;

 

24.2.3.2. 함수로 넘겨지는 변수

함수로 넘겨지는 변수는 $1, $2 등등으로 이름 지어진다. (최대 16) 몇가지 예를 보자.

CREATE FUNCTION sales_tax(REAL) RETURNS REAL AS '
DECLARE
    subtotal ALIAS FOR $1;
BEGIN
    return subtotal * 0.06;
END;
' LANGUAGE 'plpgsql';


CREATE FUNCTION instr(VARCHAR,INTEGER) RETURNS INTEGER AS '
DECLARE
    v_string ALIAS FOR $1;
    index ALIAS FOR $2;
BEGIN
    -- 계산식등이 온다.
END;
' LANGUAGE 'plpgsql';

 

24.2.3.3. 속성

%TYPE 과 %ROWTYPE 이라는 속성을 사용함으로써 사용자는 다른 데이터베이스의 자료형 또는 구조를 사용하여 변수를 정의 할 수 있다.

 

%TYPE

%TYPE을 이용하여 변수 또는 데이터베이스 칼럼의 자료형을 변수의 선언에 사용할 수 있다. 그래서 이 것을 이용하여 데이터베이스의 값을 갖는 변수를 선언할 수 있다. 예를 들면 사용자가 user_id라는 이름의 칼럼을 가지는 users라는 테이블이 있다고 하자. 이 때 테이블 users와 같은 자료형을 갖는 변수를 선언하려면...

 

user_id users.user_id%TYPE;

 

%TYPE을 사용함으로써 사용자가 사용하는 데이터베이스 구조체의 자료형을 알 필요가 없다. 그러나 이를 이용함으로써 얻을 수 있는 가장 큰 중요한 점은 만일 미래에라도 참조하고 있는 요소의 자료형이 바뀐다하더라도 사용자는 함수의 정의부분을 바꾸어줄 필요가 없다는 것이다.

 

변수이름 테이블이름%ROWTYPE;

 

위의 문장은 주어진 테이블의 구조와 함께 행(Row)을 선언하는 것이다. "테이블이름"은 반드시 데이터베이스에 존재하는 테이블이거나 Viwe의 이름이다. 그 행의 필드는 점 "."을 이용하여 참조를 할 수 있다. 함수의 매개변수는 복합형이 될 수 있다. (테이블 행 통째의) 이 경우, 연관된 식별자 $n은 행의 형일 것이지만 사용자는 사용할 때 ALIAS를 사용하여 이름을 변경해야 한다.

 

오로지 테이블 행의 사용자 속성은 그 행에서만 사용할 수 있다. OID 또는 다른 시스텡의 속성은 사용할 수 없다. (왜냐하면 행은 View에서도 올수 있으니까..) 이 행의 자료형(rowtype)의 필드는 테이블의 필드의 크기와 char()의 정밀도 등 자료형을 계승받는다.

DECLARE
    users_rec users%ROWTYPE;
    user_id users%TYPE;
    /*  바로 위의 코드는 잘못된듯.. user_id users.user_id%TYPE;
   가 되야 하지 않을까 */
BEGIN
    user_id := users_rec.user_id;
    ...

create function cs_refresh_one_mv(integer) returns integer as '
   DECLARE
        key ALIAS FOR $1;
        table_data cs_materialized_views%ROWTYPE;
   BEGIN
        SELECT INTO table_data * FROM cs_materialized_views
               WHERE sort_key=key;

        IF NOT FOUND THEN
           RAISE EXCEPTION ''View '' || key || '' not found'';
           RETURN 0;
        END IF;

        -- cs_materialized_views의 mv_name 
        -- 칼럼은 view의 이름을 저장한다.
 
        TRUNCATE TABLE table_data.mv_name;
        INSERT INTO table_data.mv_name || '' '' || table_data.mv_query;

        return 1;
end;
' LANGUAGE 'plpgsql';

24.2.3.4. 변수이름 변경

RENAME을 사용하여 사용자는 변수, 레코드 또는 행의 이름을 바꿀 수 있다. 이것은 NEW 또는 OLD가 trigger procedure 내에서 다른 이름에 의해 참조될 때 유용하다.

 

문법과 예:

 

RENAME oldname TO newname;

 

RENAME id TO user_id;

RENAME this_var TO that_var;

 

24.2.4. 표현

 

PL/pgSQL 문장에서 사용하는 모든 표현은 다른 배후 실행기(backend executor)에 의해 처리된다. 상수를 포함한 표현은 실제로 실행시간 중에 평가가 필요하다. (예: timestamp형의 'now'와 같은 경우) 그래서 PL/pgSQL 분석기(parser)로서는 NULL 키워드와 real의 상수를 구분할 방법이 없다.

모든 표현은 내부적으로 SPI 관리자를 통하여 다음과 같은 쿼리를 실행함으로써 평가된다.

 

SELECT 표현식

 

위의 '표현식'에서 사용되는 변수는 매개변수로 치환이 되고 매개변수 배열에 있는 실제 값이 실행기로 넘겨진다. PL/pgSQL 함수에서 사용되는 표현식은 오직 한번 번역되고 저장된다. 오직 예외가 있다면 쿼리가 매번 바뀔경우에 사용되는 EXECUTE 명령어를 사용할 때이다.

 

형의 검사는 Postgres의 주 분석기에 의해 이루어지는데 이에는 상수값의 해석에 영향을 끼친다. 자세하게 설명하자면 아래의 두 함수에는 차이점이 있다.

CREATE FUNCTION logfunc1 (text) RETURNS timestamp AS '
    DECLARE
        logtxt ALIAS FOR $1;
    BEGIN
        INSERT INTO logtable VALUES (logtxt, ''now'');
        RETURN ''now'';
    END;
' LANGUAGE 'plpgsql';
and 
CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
    DECLARE
        logtxt ALIAS FOR $1;
        curtime timestamp;
    BEGIN
        curtime := ''now'';
        INSERT INTO logtable VALUES (logtxt, curtime);
        RETURN curtime;
    END;
' LANGUAGE 'plpgsql';

logfunc1()의 경우, Postgres 주 분석기는 INSERT를 해석할 때, logtable의 해당 필드가 timestamp형이므로 문자열 'now'가 timestamp형으로서 해석되어야 한다는 것을 안다. 그래서 주 분석기는 처음 logfunc1()이 호출되는 시점에서 이를 상수로 치환하고 이렇게 해석된 값이 이 함수가 살아있는 동안 유지가 된다.

말할 필요도 없이 이것은 프로그래머가 원하는 방향이 아니다.

 

logfunc2()의 경우, Postgres 주 분석기는 'now'가 무엇이 되어야 하는지 알 수 없다.

그래서 주 분석기는 'now'를 문자열로 인식하고 있다가 지역변수인 curtime에 할당될 때 PL/pgSQL 해석기는 'now'를 text_out() 과 timestamp_in() 을 호출하여 timestamp 형으로 변경한다.

 

이러한 Postgres 주 분석기의 형검사는 PL/pgSQL가 거의 완성된 후에 구현이 되었다. 그래서 6.3과 6.4 버젼 사이에 차이가 있으며 SPI 관리자를 사용하여 해석기능을 이용하는 모든 함수들에 영향을 끼친다. 현재의 PL/pgSQL에서는 지역변수를 사용하는 위의 경우에서만 정확하게 해석할 수 있는 유일한 방법이다.

 

만일 표현식이나 문장 내에서 레코드의 필드가 사용될 경우, 각각의 호출이 이루어지는 동안 필드의 자료형은 변경되어서는 안된다. 만일 trigger procedures를 만들 때와 같이 복수개의 테이블을 위한 이벤트를 다루는 경우에는 이점을 유념해야 할 것이다.

 

24.2.5. 문장

 

다음에 나오는 경우와 같이 PL/pgSQL 분석기에 의해 해석되지 않는 부분은 쿼리에 놓여져 그대로 데이터베이스로 실행을 위해 보내진다. 쿼리의 결과는 어떤 값도 반환하지 않는다.

 

24.2.5.1. 값의 대입(할당)

변수 또는 행/레코드 필드로 값을 대입하는 것은 다음과 같이 쓴다.

 

식별자 := 표현식;

 

만일 표현식의 결과값이 변수의 자료형과 일치하지 않으면 또는 그 변수에 크기나 정밀도가 명시되어 있다면 (예를 들어 char(20)), 결과 값은 PL/pgSQL 바이트코드 번역기에 의해 강제적으로 변경된다. 이러한 경우는 내제적으로 실행시간 오류를 야기할 수 있다.

 

user_id := 20;

tax := subtotal * 0.06;

 

24.2.5.2. 다른 함수의 호출

Postgres 데이터베이스에 정의된 모든 함수는 값을 반환한다. 그렇기에 일반적으로 함수를 호출하는 것은 SELECT 쿼리를 실행하거나 값의 대입이다.

 

그렇지만 몇몇 사람은 함수의 결과를 필요로 하지 않는 경우도 있다. 이 경우엔 PERFORM 문을 사용한다.

 

PERFORM 쿼리문

 

위의 문장은 SPI manager를 통해 SELECT 쿼리를 실행하고 반환값을 삭제한다. 지역변수와 같은 식별자는 여전히 함수 파라메터로 치환된다.

PERFORM create_mv(''cs_session_page_requests_mv'',''
     select   session_id, page_id, count(*) as n_hits,
              sum(dwell_time) as dwell_time, count(dwell_time) as dwell_count
     from     cs_fact_table
     group by session_id, page_id '');

24.2.5.3. 동적 쿼리의 실행

때때로 PL/pgSQL함수 내에서 동적 쿼리를 생성하고자 할 때가 있다. 또는 다른 함수를 생성하는 함수를 가질 수도 있다. PL/pgSQL는 이러한 경우를 위해 EXECUTE 문을 제공한다.

 

EXECUTE query-string

 

위에서 "query-string"은 실행할 쿼리를 포함하는 문자열이다.

 

동적 쿼리를 이용하여 작업할 때에, PL/pgSQL에서 작은 따옴표에 대한 문제에 직면하게 되는데 이에 대한 수고를 덜어주기 위해 자세한 설명이 있는 "Oracle PL/SQL에서 PL/pgSQL로의 변환"에 있는 표를 참조하기 바란다.

 

PL/pgSQL의 다른 쿼리와는 다르게 EXECUTE문에 의해 수행되는 쿼리는 다른 일반 쿼리와 같이 처음 실행될 때 번역과 저장 작업이 이루어져 서버가 살아 있는 내내 존재하는 것이 아니다. 쿼리 문자열은 다양한 테이블과 필드에서 작업을 수행할 수 있도록 프로시져 내에서 생성된다.

 

SELECT 쿼리의 결과는 EXECUTE에 의해 버려진다. 그리고 현재까지는 EXECUTE내에서 SELECT INTO는 사용할 수 없다 그래서 동적으로 생성된 SELECT에서 결과는 뽑는 유일한 방법은 FOR...EXECUTE 형식을 사용하는 것인데 이는 추후에 설령을 하겠다.

 

예)

EXECUTE ''UPDATE tbl SET ''
        || quote_ident(fieldname)
        || '' = ''
        || quote_literal(newvalue)
        || '' WHERE ...'';

이 예에서 quote_ident(TEXT) 함수와 quote_literal(TEXT) 함수를 사용하였다. 문자열 변수가 필드나 테이블의 이름을 포함하고 있으면 반드시 그 내용을 quote_ident()로 넘겨야 한다. 또한 변수가 동적 쿼리의 문자열의 문자요소를 포함하고 있으면 반드시 그 내용을 quote_literal()로 넘겨야 한다. 앞의 두경우 모두 작은 따옴표 또는 큰 따옴표에 둘러싸인 문자열 그리고 특수문자와 함께 반환하기 위해서는 적절한 절차를 밟아야 한다.

 

다음은 좀 더 큰 동적쿼리와 EXECUTE를 사용한 예이다.

CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS '
DECLARE
    referrer_keys RECORD;  -- FOR에서 사용하기 위한 일반적인 record를 선언
    a_output varchar(4000);
BEGIN 
    a_output := ''CREATE FUNCTION cs_find_referrer_type(varchar,varchar,varchar) 
                  RETURNS varchar AS '''' 
                     DECLARE 
                         v_host ALIAS FOR $1; 
                         v_domain ALIAS FOR $2; 
                         v_url ALIAS FOR $3; ''; 

    -- 
    -- 어떻게 FOR 반복문을 통해 쿼리의 결과를 훑는지를 주목...
    -- 여기서는 FOR  생성을 이용하였다.
    --

    FOR referrer_keys IN select * from cs_referrer_keys order by try_order LOOP
        a_output := a_output || '' if v_'' || referrer_keys.kind || '' like '''''''''' 
                 || referrer_keys.key_string || '''''''''' then return '''''' 
                 || referrer_keys.referrer_type || ''''''; end if;''; 
    END LOOP; 
  
    a_output := a_output || '' return null; end; '''' language ''''plpgsql'''';''; 
 

-- 이 방법은 적법하다 왜냐하면 여기서는 어떠한 변수도 치환하지 않았기 때문이다.

-- 아니면 실패를 하게 된다. 함수를 시행하기 위한 다른 방법인 PERFORM을 보라

    
    EXECUTE a_output; 
end; 
' LANGUAGE 'plpgsql';

24.2.5.4. 다른 결과 상태 포함하기

GET DIAGNOSTICS 변수이름 = item [ , ... ]

 

이 명령은 시스템 상태를 추출할 때 사용한다. 각 item은 상태값을 나타내는 키워드인데 그 값은 명시된 '변수이름'에 할당된다. (당연히 '변수이름'의 자료형은 item의 자료형과 일치하여야 한다.) 현재 사용가능한 상태 item은 ...

 

ROW_COUNT: SQL 처리기에 보내진 마지막 SQL 쿼리에 의해 처리된 행의 갯수

RESULT_OID: 가장 최근의 SQL쿼리에 의해 테이블에 삽입된 행의 OID.

주지할 사항은 RESULT_OID는 INSERT 쿼리 이후에 사용가능하다.

 

24.2.5.5. 함수에서 값의 반환

 

RETURN 표현식

 

위의 명령은 함수를 종료하고 '표현식'의 값을 상위 실행기로 반환한다. 함수의 반환 값은 반드시 정의 돼야한다. 만일 프로그램이 함수의 최상위 레벨의 끝에 까지 다다랐는데도 RETURN문이 없으면 실행시간 오류가 발생한다.

 

'표현식'의 결과는 자동적으로 함수의 반환형으로 변환된다. 이는 할당에서 설명한 것과 같다.

 

24.2.6. 제어 구조

 

이 제어 구조가 아마도 PL/SQL에서 가중 유용한 (또 중요한) 부분이 아닐까 한다. PL/pgSQL의 제어구조를 이용하여 사용자는 PostgreSQL의 데이터를 다양하고 강력하게 다룰 수 있다.

 

24.2.6.1. 조건 제어: IF 문

IF문은 특정 조건에 따라 주어진 일을 하는 문장이다. PL/pgSQL는 세가지 형태를 가지는데 그 것들은 IF: IF-THEN, IF-THEN-ELSE, IF-THEN-ELSE IF 이다.

주목: PL/pgSQL의 IF문은 짝지워진 END IF 가 필요한다. ELSE-IF의 경우는 2개의 END IF가 필요한데 하나는 첫번째 IF를 위해, 그리고 다른 하나는 ELSE IF를 위해서 이다.

 

IF-THEN

IF-THEN 문은 IF문의 가장 간단한 형태이다. THEN 과 END IF사이의 문장들은 주어진 조건이 true일때 수행된다. 그렇지 않으면 END IF 다음에 나오는 문장들로 실행이 넘어간다.

 

예)

F v_user_id <> 0 THEN
    UPDATE users SET email = v_email WHERE user_id = v_user_id;
END IF;

IF-THEN-ELSE

IF-THEN-ELSE 문은 IF-THEN 문에 해당 조건이 FALSE가 되었을 경우 실행될 문장들을 추가한 형태이다.

 

예1)

IF parentid IS NULL or parentid = ''''
THEN 
    return fullname;
ELSE
    return hp_true_filename(parentid) || ''/'' || fullname;
END IF;

예2)

IF v_count > 0 THEN 
    INSERT INTO users_count(count) VALUES(v_count);
    return ''t'';
ELSE 
    return ''f'';
END IF;

다음에 나오는 예처럼 IF문은 중첩되어 사용될 수 있다.

IF demo_row.sex = ''m'' THEN
  pretty_sex := ''man'';
ELSE
  IF demo_row.sex = ''f'' THEN
    pretty_sex := ''woman'';
  END IF;
END IF;

IF-THEN-ELSE IF

"ELSE IF"문이 사용되는 것은 실제로 ELSE문 안에 IF문을 중첩하는 것과 같다. 그렇기 때문에 END IF문이 각 중첩된 IF와 상위 IF-ELSE를 위해 필요한 것이다.

 

예)

IF demo_row.sex = ''m'' THEN
   pretty_sex := ''man'';
ELSE IF demo_row.sex = ''f'' THEN
        pretty_sex := ''woman'';
     END IF;  /* <-- 요기를 주목하시라... 요게 일반적인 언어와 다른 부분이다. */
END IF;

24.2.6.2. 반복 제어: LOOP, WHILE, FOR 그리고 EXIT

LOOP, WHILE, FOR 그리고 EXIT 문을 이용하여 PL/pgSQL 프로그램을 반복적으로 실행하는 흐름제어를 할 수 있다.

 

LOOP 문...

[<

위와같은 조건이 없는 반복문은 반드시 명백하게 EXIT을 사용하여 종료되야 한다. 선택적으로 사용할 수 있는 label은 중복된 반복문에서 빠져나오고자 할 때 EXIT문에 의해 사용된다.

 

EXIT 문...

 

EXIT [ label ] [ WHEN 표현식 ];

 

만약에 label이 명시되지 않으면 중첩된 반복문에서 내부의 반복문이 끝나면 내부 반복문의 END LOOP 다음에 나오는 문장을 수행한다. 그렇지 않고 label이 명시되면 주어진 label이 붙은 반복문 또는 블럭을 빠져나가게 된다. 그리고 반복문 또는 블럭의 해당되는 END 문 다음 문장을 처리한다. 그 label은 중첩된 반복문에서 현재 수행중인 내부 반복문의 label이거나 상위 반복문의 label이어야한다.

 

예)

LOOP
    -- 실행할 계산문
    IF count > 0 THEN
        EXIT;  -- 반복문 탈출
    END IF;
END LOOP;

LOOP
    -- 실행할 계산문
    EXIT WHEN count > 0;
END LOOP;

BEGIN
    -- 실행할 계산문
    IF stocks > 100000 THEN
        EXIT;  -- 오류! LOOP 블럭 밖에서 EXIT을 사용할 수 없다.
    END IF;
END;

WHILE 문...

WHILE문을 사용하여 주어진 조건을 검사하여 그 조건이 참인 동안 일을 수행하는 반복문을 만들 수 있다.

[<

예)

WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
    -- 실행할 계산식
END LOOP;

WHILE NOT boolean_expression LOOP
    -- 실행할 계산식
END LOOP;

FOR 문...

[<

이 반복문은 주어진 정수형의 범위 내에서 반복 수행한다. 변수 '변수이름'은 자동적으로 정수형으로 생성이 되고 오로지 반복문 내에서만 존재를 한다. 그리고 두 '표현식'은 최소와 최대값의 범위를 나타내는 표현식이다. 그리고 반복 될 때마다 변수의 값은 항상 1씩 증가/감소된다.

 

다음은 FOR 문에 대한 예이다. (레코드를 반복하는 FOR에 대한 예는 24.2.7를 참조)

FOR i IN 1..10 LOOP
  -- 실행할 표현식

    RAISE NOTICE 'i is %',i;
END LOOP;

FOR i IN REVERSE 1..10 LOOP
    -- 실행할 표현식
END LOOP;

24.2.7. RECORD를 이용한 작업

 

RECORD는 간단한 데이터베이스 행의 자료형 (rowtype) 이긴 하지만 사전에 구조가 정의 되어있지 않는다. 이는 선택(selection)과 FOR 반복문에서 SELECT 작업을 통해 실제 데이터베이스의 한 행의 값을 갖는다.

 

24.2.7.1. 선언

RECORD 형의 변수는 다른 선택(selection)에 사용될 수 있다. 한 레코드를 다루거나 값을 레코드 필드로 대입을 하려고 할 때 만일 실제 행(row)이 없다면 실행시간 오류를 야기한다. 변수선언은 다음과 같이 한다.

 

변수이름 RECORD;

 

24.2.7.2. 값의 할당

RECORD 또는 Row으로 완전한 선택(selection)의 할당은 다음과 같이 해서 이루어진다.

 

SELECT INTO 대입받을_변수 표현식 FROM ...;

 

'대입받을_변수'는 RECORD 또는 ROW 형의 변수이거나 쉼표 ','를 사용하여 구분된 레코드/행의 필드와 변수의 목록이다. 위의 것은 Postgres가 보통 수행하는 SELECT INTO와는 완전히 다른 것이다. Postgres가 보통 수행하는 SELECT INTO에서는 SELECT결과를 이용하여 새로운 테이블을 만들 때 사용하는 것이다. (만일 PL/pgSQL에서 SELECT의 결과를 이용하여 새로이 테이블을 만들려면 CREATE TABLE AS SELECT를 사용해야 한다.)

 

만약에 row또는 변수목록(','로 구분된)을 '대입받을_변수'로 사용하고자 한다면 선택된 값들은 반드시 "대입받을_변수'와 구조가 일치되어야 한다. 그렇지 않으면 실행시간 오류가 발생한다. FROM 키워드 다음에는 조건, 그룹, 정렬 같은 것들이 따라올 수 있는데 이것들은 SELECT문에도 쓸 수 있는 것들이다.

 

레코드 또는 행의 값이 RECORD 변수로 할당이 되면 마침표 '.'를 사용하여 해당 필드로 접근할 수 있다.

DECLARE
    users_rec RECORD;
    full_name varchar;
BEGIN
    SELECT INTO users_rec * FROM users WHERE user_id=3;

  full_name := users_rec.first_name || '' '' || users_rec.last_name;

FOUND라고 하는 boolean형의 특별한 변수가 있는데, 이 것은 SELECT INTO 바로 다음에 사용되어 할당이 성공적이었는지를 확인 할 수 있다.

SELECT INTO myrec * FROM EMP WHERE empname = myname;
IF NOT FOUND THEN
    RAISE EXCEPTION ''employee % not found'', myname;
END IF;

또한 IS NULL (또는 ISNULL)을 사용하여 RECORD/ROW가 NULL인지 여부를 검사할 수 있다. 만일 선택이 복수개의 행을 반환하였다면 첫번째만이 '대입받을_변수'에 옮겨진다. 나머지 행들은 누락되어져 버린다.

DECLARE
    users_rec RECORD;
    full_name varchar;
BEGIN
    SELECT INTO users_rec * FROM users WHERE user_id=3;

    IF users_rec.homepage IS NULL THEN
        -- 사용자가 없는 홈페이지에 들어왔다, "http://"를 반환한다.

        return ''http://'';
    END IF;
END;

24.2.7.3. Record를 통한 반복

특별한 FOR 반복문을 통하여 사용자는 쿼리의 결과를 통해 반복을 할 수 있고, 데이터를 사용할 수 있다. 다음은 그 문법이다.

[<

레코드 또는 행은 'select문장'으로부터 모든 반환된 행을 할당받고 반복문의 문장은 각각의 행을 처리한다. 다음은 그 예이다.

create function cs_refresh_mviews () returns integer as '
DECLARE
     mviews RECORD;

     -- 위의 선언 대신에...
     -- mviews  cs_materialized_views%ROWTYPE;
     -- 이렇게 했다면 이 변수는 오로지 cs_materialized_views 테이블에서만 
     -- 사용할 수 있다. 

BEGIN
     PERFORM cs_log(''Refreshing materialized views...'');

     FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP

         -- 이제 "mviews" 는 cs_materialized_views의 한 레코드를 받았다.

         PERFORM cs_log(''Refreshing materialized view '' || mview.mv_name || ''...'');
         TRUNCATE TABLE mview.mv_name;
         INSERT INTO mview.mv_name || '' '' || mview.mv_query;
     END LOOP;

     PERFORM cs_log(''Done refreshing materialized views.'');
     return 1;
end;
' language 'plpgsql';

만일 위의 반복문이 EXIT으로 빠져나왔다면 마지막에 할당받은 행은 반복문 밖에서도 여전히 사용가능하다.

 

FOR-IN EXECUTE 문은 결과 레코드 사이를 반복할 수 있는 다른 방법이다.

[<

이의 형태는 앞의 예와 비슷한데, 이 경우에는 SELECT문이 표현식을 가지는 문자열로 대체되었다. 이것은 FOR문에 진입할 때마다 평가되고 해석된다. 여기서 한가지의 선택을 해야하는데 미리 해석된 쿼리를 통한 나은 성능을 택하느냐 아니면 일반적인 EXECUTE문과 같이 동적 쿼리의 유연성을 택하느냐이다.

 

 

24.2.8. 처리 중단과 메세지

 

RAISE문을 사용하여 Postgres elog 매커니즘으로 출력 메세지를 보낼 수 있다.

 

RAISE 출력수준 '출력형식' [, identifier [...]];

 

출력형식에서 뒤 따라오는 identifier의 출력형식을 정할 수 있는 %를 사용할 수 있다. '출력수준'으로 쓸 수 있는 키워드는 다음의 3가지이다.

 

DEBUG - 제품수준 환경의 데이터베이스에서 사용되는 조용한 수준의 메세지

NOTICE - 데이터베이스 log에 기록이 되고 클라이언트로 보내지는 수준의 메세지

EXCEPTIOM - 데이터베이스 log에 기록이 되고 트랜젝션을 중단하는 수준의 메세지

RAISE NOTICE ''Id number '' || key || '' not found!'';

RAISE NOTICE ''Calling cs_create_job(%)'',v_job_id;

 

바로 위의 2번째 예에서 v_job_id은 %의 자리에 문자열로 출력된다.

 

RAISE EXCEPTION ''Inexistent ID --> %'',user_id;

 

위의 예는 트랜젝션을 중단하고 데이터베이스 log에 기록한다.

 

24.2.9. 예외

 

Postgres는 그리 똑똑한 예외처리 모델을 가지고 있지 않다. 분석기(parser), 번역기/최적화 (planner/optimizer) 또는 실행기(executor)에서 더이상 요청된 처리를 수행하지 못할 경우 모든 트랜젝션은 중단되고 main loop로 건너뛰고나서 클라이언트 프로그램에서 다음 쿼리를 수행한다.

 

이같은 상황을 알리기 위한 메세지를 오류 메커니즘으로 넣는 것이 가능하다. 그러나 현재로는 왜 그러한 상황이 발생하였는지 아는 것은 불가능하다. (입/출력 변환 오류, floating point오류, parse 오류 등등). 그래서 데이터베이스 처리가 오류의 시점에서 자료의 불일치가 발생할 수 있기 때문에 상위 처리부로 귀환한다거나 계속된 명령의 처리는 전체 데이터베이스를 망가뜨릴 수 있다. 그렇기 때문에 처리가 중단이 됐고 이의 사실이 클라이언트에 알려진다 하더라도 계속되는 처리는 무의미하다.

 

그렇기 때문에 PL/pgSQL이 현재 할 수 있는 것은 함수나 trigger procedure가 처리되는 과정에서 중단(abort)되면 어떤 함수의 어디서 (line number와 문장의 종류) 발생했는지 추가적인 DEBUG lebel의 로그 메세지를 기록해주는 것이 다이다.

[Top]
No.
제목
작성자
작성일
조회
3910DB data directory 를 여러군데 이용하기.
정재익
2002-01-21
6264
3871pgbash 사용법
정재익
2002-01-11
8638
3846PL/pgSQL - SQL procedural language (3)
정재익
2002-01-07
15418
3845PL/pgSQL - SQL procedural language (2)
정재익
2002-01-07
10070
3844PL/pgSQL - SQL procedural language (1)
정재익
2002-01-07
9479
3831부분 인덱스 사용법
정재익
2002-01-01
6549
3822Quick HOWTO - connecting StarOffice Win or Lin to PostGreSQL using ODBC
정재익
2001-12-30
5767
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2024 DSN, All rights reserved.
작업시간: 0.017초, 이곳 서비스는
	PostgreSQL v16.4로 자료를 관리합니다