지원되는 PL/pgSQL 문 - Amazon Redshift

지원되는 PL/pgSQL 문

PL/pgSQL 문은 루프 및 조건 표현식을 비롯한 프로시저 구문으로 SQL 명령을 보완하여 논리 흐름을 제어합니다. COPY, UNLOAD, INSERT 등의 데이터 조작 언어(DML)와 CREATE TABLE 등의 데이터 정의 언어(DDL)를 포함한 대부분의 SQL 명령을 사용할 수 있습니다. 포괄적인 SQL 명령 목록은 SQL 명령 섹션을 참조하세요. 또한 Amazon Redshift에서 다음 PL/pgSQL 문이 지원됩니다.

대입

대입문은 값을 변수에 할당합니다. 표현식은 단일 값을 반환해야 합니다.

identifier := expression;

:= 대신에 대입에 표준이 아닌 =를 사용하는 것도 허용됩니다.

표현식의 데이터 형식이 변수의 데이터 형식과 일치하지 않거나 변수에 크기 또는 정밀도가 있는 경우, 결과 값이 암시적으로 변환됩니다.

다음에 예가 나와 있습니다.

customer_number := 20; tip := subtotal * 0.15;

SELECT INTO

SELECT INTO 문은 여러 열(단 행은 하나만)의 결과를 레코드 변수 또는 스칼라 변수 목록에 할당합니다.

SELECT INTO target select_expressions FROM ...;

앞의 구문에서 target는 레코드 변수이거나 간단한 변수 및 레코드 필드의 쉼표로 구분된 목록일 수 있습니다. select_expressions 목록과 명령의 나머지는 정규 SQL에서와 동일합니다.

변수 목록이 target로 사용되는 경우, 선택한 값이 대상의 구조와 정확히 일치해야 합니다. 그렇지 않으면 실행 시간 오류가 발생합니다. 레코드 변수가 대상인 경우, 쿼리 결과 열의 행 형식으로 자동 구성됩니다.

INTO 절은 SELECT 문의 거의 모든 곳에서 나타날 수 있습니다. 보통 SELECT 절 바로 뒤나 FROM 절 바로 앞에 나타납니다. 즉, select_expressions 목록 바로 앞이나 바로 뒤에 나타납니다.

쿼리가 행을 반환하지 않으면 NULL 값이 target에 할당됩니다. 쿼리가 행을 여러 개 반환하면 첫 번째 행이 target에 할당되고 나머지는 삭제됩니다. 명령문에 ORDER BY가 포함되어 있지 않으면 첫 번째 행은 결정적이지 않습니다.

대입이 행을 하나 이상 반환했는지 여부를 확인하려면 특수 FOUND 변수를 사용합니다.

SELECT INTO customer_rec * FROM cust WHERE custname = lname; IF NOT FOUND THEN RAISE EXCEPTION 'employee % not found', lname; END IF;

레코드 결과가 null인지 여부를 테스트하려면 IS NULL 조건부를 사용하면 됩니다. 추가 행이 삭제되었는지 확인할 수 있는 방법은 없습니다. 다음 예제에서는 행이 반환되지 않은 경우를 처리합니다.

CREATE OR REPLACE PROCEDURE select_into_null(return_webpage OUT varchar(256)) AS $$ DECLARE customer_rec RECORD; BEGIN SELECT INTO customer_rec * FROM users WHERE user_id=3; IF customer_rec.webpage IS NULL THEN -- user entered no webpage, return "http://" return_webpage = 'http://'; END IF; END; $$ LANGUAGE plpgsql;

No-op

no-op 문(NULL;)은 아무 것도 수행하지 않는 자리 표시자 문입니다. no-op 문은 IF-THEN-ELSE 체인의 한 브랜치가 비어 있음을 나타낼 수 있습니다.

NULL;

Dynamic SQL

PL/pgSQL 저장 프로시저에서 실행될 때마다 다른 테이블 또는 다른 데이터 형식을 포함할 수 있는 동적 명령을 생성하려면 EXECUTE 문을 사용합니다.

EXECUTE command-string [ INTO target ];

위의 command-string은 실행할 명령을 포함하는 문자열(텍스트 형식)을 생성하는 표현식입니다. 이 command-string 값은 SQL 엔진으로 전송됩니다. PL/pgSQL 변수의 대체는 명령 문자열에서 수행되지 않습니다. 변수 값은 생성될 때 명령 문자열에 삽입되어야 합니다.

참고

동적 SQL 내에서 COMMIT 및 ROLLBACK 문을 사용할 수 없습니다. 저장 프로시저 내의 COMMIT 및 ROLLBACK 문 사용에 대한 자세한 내용은 트랜잭션 관리를 참조하십시오.

동적 명령을 작업할 경우 보통 작은따옴표의 이스케이핑을 처리해야 합니다. 달러 인용을 사용하여 함수 본문에서 고정 텍스트를 따옴표로 묶는 것이 좋습니다. 생성된 쿼리에 삽입할 동적 값에는 따옴표가 포함될 수 있으므로 특별한 처리가 필요합니다. 다음 예제에서는 함수의 달러 인용을 전체로 가정하므로, 따옴표가 큰따옴표일 필요가 없습니다.

EXECUTE 'UPDATE tbl SET ' || quote_ident(colname) || ' = ' || quote_literal(newvalue) || ' WHERE key = ' || quote_literal(keyvalue);

앞의 예제에서는 함수 quote_ident(text)quote_literal(text)을 보여 줍니다. 이 예제에서는 열 및 테이블 식별자가 포함된 변수를 quote_ident 함수로 전달합니다. 또한 생성된 명령에 리터럴 문자열이 포함된 변수를 quote_literal 함수로 전달합니다. 두 함수 모두 각각 큰따옴표 또는 작은따옴표로 묶인 입력 텍스트를 반환하기 위한 적절한 단계를 거치며, 포함된 특수 문자는 적절하게 이스케이프됩니다.

달러 인용은 고정 텍스트 인용 시에만 유용합니다. 앞의 예제를 다음 형식으로 작성하지 마십시오.

EXECUTE 'UPDATE tbl SET ' || quote_ident(colname) || ' = $$' || newvalue || '$$ WHERE key = ' || quote_literal(keyvalue);

newvalue의 내용에 $$가 포함되면 이 예제가 중단되기 때문입니다. 선택할 수 있는 다른 달러 인용 구분 기호에도 동일한 문제가 적용됩니다. 미리 알려지지 않은 텍스트를 안전하게 인용하려면 quote_literal 함수를 사용합니다.

반환

RETURN 문은 저장 프로시저에서 호출자에게 다시 반환합니다.

RETURN;

다음은 그 한 예입니다.

CREATE OR REPLACE PROCEDURE return_example(a int) AS $$ BEGIN FOR b in 1..10 LOOP IF b < a THEN RAISE INFO 'b = %', b; ELSE RETURN; END IF; END LOOP; END; $$ LANGUAGE plpgsql;

조건부: IF

IF 조건부 문은 Amazon Redshift가 사용하는 PL/pgSQL 언어에서 다음 형식을 취할 수 있습니다.

  • IF ... THEN

    IF boolean-expression THEN statements END IF;

    다음은 그 한 예입니다.

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

    IF boolean-expression THEN statements ELSE statements END IF;

    다음은 그 한 예입니다.

    IF parentid IS NULL OR parentid = '' THEN return_name = fullname; RETURN; ELSE return_name = hp_true_filename(parentid) || '/' || fullname; RETURN; END IF;
  • IF ... THEN ... ELSIF ... THEN ... ELSE

    키워드 ELSIF는 ELSEIF로 표기할 수도 있습니다.

    IF boolean-expression THEN statements [ ELSIF boolean-expression THEN statements [ ELSIF boolean-expression THEN statements ...] ] [ ELSE statements ] END IF;

    다음은 그 한 예입니다.

    IF number = 0 THEN result := 'zero'; ELSIF number > 0 THEN result := 'positive'; ELSIF number < 0 THEN result := 'negative'; ELSE -- the only other possibility is that number is null result := 'NULL'; END IF;

조건부: CASE

CASE 조건부 문은 Amazon Redshift가 사용하는 PL/pgSQL 언어에서 다음 형식을 취할 수 있습니다.

  • 단순 CASE

    CASE search-expression WHEN expression [, expression [ ... ]] THEN statements [ WHEN expression [, expression [ ... ]] THEN statements ... ] [ ELSE statements ] END CASE;

    단순 CASE 문은 피연산자의 동등성을 기반으로 조건부 실행을 제공합니다.

    search-expression 값은 한 번 평가되고 WHEN 절의 각 expression과 연속적으로 비교됩니다. 일치하는 항목이 있으면 해당 statements가 실행된 후 제어가 END CASE 뒤의 다음 문으로 전달됩니다. 후속 WHEN 표현식은 평가되지 않습니다. 일치하는 항목이 없으면 ELSE statements가 실행됩니다. 그러나 ELSE가 없으면 CASE_NOT_FOUND 예외가 발생합니다.

    다음은 그 한 예입니다.

    CASE x WHEN 1, 2 THEN msg := 'one or two'; ELSE msg := 'other value than one or two'; END CASE;
  • 검색 CASE

    CASE WHEN boolean-expression THEN statements [ WHEN boolean-expression THEN statements ... ] [ ELSE statements ] END CASE;

    검색된 형태의 CASE는 부울 표현식의 사실성을 기반으로 조건부 실행을 제공합니다.

    각 WHEN 절의 boolean-expression은 true를 생성하는 항목이 발견될 때까지 평가됩니다. 그런 다음 해당 문이 실행되고, 제어가 END CASE 뒤의 다음 문으로 전달됩니다. 후속 WHEN expressions는 평가되지 않습니다. true 결과가 없으면 ELSE statements가 실행됩니다. 그러나 ELSE가 없으면 CASE_NOT_FOUND 예외가 발생합니다.

    다음은 그 한 예입니다.

    CASE WHEN x BETWEEN 0 AND 10 THEN msg := 'value is between zero and ten'; WHEN x BETWEEN 11 AND 20 THEN msg := 'value is between eleven and twenty'; END CASE;

Loops

loop 문은 Amazon Redshift가 사용하는 PL/pgSQL 언어에서 다음 형식을 취할 수 있습니다.

  • 단순 루프

    [<<label>>] LOOP statements END LOOP [ label ];

    단순 루프는 EXIT 또는 RETURN 문에 의해 종료될 때까지 무기한으로 반복되는 무조건부 루프를 정의합니다. 선택적 레이블은 중첩 루프 내의 EXIT 및 CONTINUE 문에서 EXIT 및 CONTINUE 문이 참조하는 루프를 지정하는 데 사용할 수 있습니다.

    다음은 그 한 예입니다.

    CREATE OR REPLACE PROCEDURE simple_loop() LANGUAGE plpgsql AS $$ BEGIN <<simple_while>> LOOP RAISE INFO 'I am raised once'; EXIT simple_while; RAISE INFO 'I am not raised'; END LOOP; RAISE INFO 'I am raised once as well'; END; $$;
  • 종료 루프

    EXIT [ label ] [ WHEN expression ];

    label이 없는 경우 가장 안쪽의 루프가 종료되고 END LOOP 다음의 문이 다음에 실행됩니다. label이 있는 경우 중첩 루프 또는 블록의 현재 또는 일부 외부 수준의 레이블이어야 합니다. 그런 다음 명명된 루프 또는 블록이 종료되고 제어가 해당 루프 또는 블록 END 다음에 있는 문으로 계속됩니다.

    WHEN이 지정된 경우 expression이 true인 경우에만 루프 종료가 발생합니다. 그렇지 않은 경우 제어가 EXIT 뒤의 문으로 전달됩니다.

    모든 형식의 루프에서 EXIT를 사용할 수 있으며, 무조건부 루프에서 사용하는 것에 국한되지 않습니다.

    BEGIN 블록과 함께 사용하는 경우, EXIT는 블록 종료 후 제어를 다음 문으로 전달합니다. 이 목적으로 레이블을 사용해야 합니다. 레이블이 지정되지 않은 EXIT는 BEGIN 블록과 일치하는 것으로 간주되지 않습니다.

    다음은 그 한 예입니다.

    CREATE OR REPLACE PROCEDURE simple_loop_when(x int) LANGUAGE plpgsql AS $$ DECLARE i INTEGER := 0; BEGIN <<simple_loop_when>> LOOP RAISE INFO 'i %', i; i := i + 1; EXIT simple_loop_when WHEN (i >= x); END LOOP; END; $$;
  • 계속 루프

    CONTINUE [ label ] [ WHEN expression ];

    label이 제공되지 않은 경우, 실행이 가장 안쪽에 있는 루프의 다음 반복으로 이동합니다. 즉, 루프 본문에 남아 있는 모든 문을 건너뜁니다. 그런 다음 제어는 루프 제어 표현식(있는 경우)으로 돌아가 다른 루프 반복이 필요한지 여부를 결정합니다. label이 있는 경우, 실행이 계속되는 루프의 레이블을 지정합니다.

    WHEN이 지정된 경우 expression이 true인 경우에만 루프의 다음 반복이 시작됩니다. 그렇지 않은 경우 제어가 CONTINUE 뒤의 문으로 전달됩니다.

    모든 형식의 루프에서 CONTINUE를 사용할 수 있으며, 무조건부 루프에서 사용하는 것에 국한되지 않습니다.

    CONTINUE mylabel;
  • WHILE 루프

    [<<label>>] WHILE expression LOOP statements END LOOP [ label ];

    WHILE 문은 boolean-expression이 true로 평가되는 한 일련의 문을 반복합니다. 표현식은 루프 본문의 각 항목 바로 앞에서 검사됩니다.

    다음은 그 한 예입니다.

    WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP -- some computations here END LOOP; WHILE NOT done LOOP -- some computations here END LOOP;
  • FOR 루프(정수 변형)

    [<<label>>] FOR name IN [ REVERSE ] expression .. expression LOOP statements END LOOP [ label ];

    FOR 루프(정수 변형)는 정수 값 범위를 반복하는 루프를 생성합니다. 변수 이름은 정수 유형으로 자동 정의되며 루프 내부에만 있습니다. 변수 이름의 모든 기존 정의는 루프 내에서 무시됩니다. 범위의 하한값과 상한값을 제공하는 두 표현식은 루프를 입력할 때 한 번 평가됩니다. REVERSE를 지정하면 각 반복 후에 단계 값이 추가되는 것이 아니라 빠집니다.

    하한값이 상한값보다 크면(REVERSE의 경우 작으면) 루프 본문이 실행되지 않습니다. 오류는 발생하지 않습니다.

    레이블이 FOR 루프에 연결되어 있는 경우, 해당 레이블을 사용하여 정규화된 이름이 있는 정수 루프 변수를 참조할 수 있습니다.

    다음은 그 한 예입니다.

    FOR i IN 1..10 LOOP -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop END LOOP; FOR i IN REVERSE 10..1 LOOP -- i will take on the values 10,9,8,7,6,5,4,3,2,1 within the loop END LOOP;
  • FOR 루프(결과 세트 변형)

    [<<label>>] FOR target IN query LOOP statements END LOOP [ label ];

    target는 레코드 변수 또는 스칼라 변수의 쉼표로 구분된 목록입니다. 대상은 쿼리에서 생성된 각 행에 연속적으로 할당되고, 행별로 루프 본문이 실행됩니다.

    FOR 루프(결과 세트 변형)를 통해 저장 프로시저는 쿼리 결과를 반복하고 그에 따라 해당 데이터를 조작할 수 있습니다.

    다음은 그 한 예입니다.

    CREATE PROCEDURE cs_refresh_reports() AS $$ DECLARE reports RECORD; BEGIN FOR reports IN SELECT * FROM cs_reports ORDER BY sort_key LOOP -- Now "reports" has one record from cs_reports EXECUTE 'INSERT INTO ' || quote_ident(reports.report_name) || ' ' || reports.report_query; END LOOP; RETURN; END; $$ LANGUAGE plpgsql;
  • 동적 SQL을 사용하는 FOR 루프

    [<<label>>] FOR record_or_row IN EXECUTE text_expression LOOP statements END LOOP;

    동적 SQL을 사용하는 FOR 루프를 통해 저장 프로시저는 동적 쿼리 결과를 반복하고 그에 따라 해당 데이터를 조작할 수 있습니다.

    다음은 그 한 예입니다.

    CREATE OR REPLACE PROCEDURE for_loop_dynamic_sql(x int) LANGUAGE plpgsql AS $$ DECLARE rec RECORD; query text; BEGIN query := 'SELECT * FROM tbl_dynamic_sql LIMIT ' || x; FOR rec IN EXECUTE query LOOP RAISE INFO 'a %', rec.a; END LOOP; END; $$;

커서

한 번에 전체 쿼리를 실행하는 대신에 커서를 설정할 수 있습니다. 커서는 쿼리를 캡슐화하고 쿼리 결과를 한 번에 몇 행씩 읽습니다. 이렇게 하는 이유는 결과에 다수의 행이 포함된 경우 메모리 오버런을 방지하기 위함입니다. 또 다른 이유는 호출자가 행을 읽을 수 있도록 저장 프로시저가 생성한 커서 참조를 반환하는 것입니다. 이를 통해 저장 프로시저에서 대량의 행 세트를 효율적으로 반환할 수 있습니다.

NONATOMIC 저장 프로시저에서 커서를 사용하려면 START TRANSACTION...COMMIT 사이에 커서 루프를 배치하세요.

커서를 설정하려면 먼저 커서 변수를 선언합니다. PL/pgSQL의 커서에 대한 모든 액세스는 항상 특수 데이터 형식 refcursor인 커서 변수를 통과합니다. refcursor 데이터 형식에는 커서 참조가 있습니다.

형식 refcursor의 변수로 선언하여 커서 변수를 생성할 수 있습니다. 또는 다음과 같은 커서 선언 구문을 사용할 수 있습니다.

name CURSOR [ ( arguments ) ] FOR query ;

앞의 예에서 인수(지정된 경우)는 쿼리에서 파라미터 값으로 대체될 이름을 각각 정의하는 쉼표로 구분된 이름 데이터 형식 페어 목록입니다. 이러한 이름을 대체할 실제 값은 커서가 열릴 때 나중에 지정됩니다.

다음에 예가 나와 있습니다.

DECLARE curs1 refcursor; curs2 CURSOR FOR SELECT * FROM tenk1; curs3 CURSOR (key integer) IS SELECT * FROM tenk1 WHERE unique1 = key;

이러한 세 변수는 모두 데이터 형식이 refcursor이지만, 첫 번째 변수를 모든 쿼리에 사용할 수 있습니다. 이와 달리 두 번째 변수에는 완전히 지정된 쿼리가 이미 바인딩되어 있고, 마지막 변수에는 파라미터가 있는 쿼리가 바인딩되어 있습니다. 커서가 열리면 key 값은 정수 파라미터 값으로 대체됩니다. 변수 curs1은 어떤 특정 쿼리에도 바인딩되어 있지 않으므로 언바운드되었다고 합니다.

커서를 사용하여 행을 검색하려면 먼저 커서를 열어야 합니다. PL/pgSQL에는 세 가지 형태의 OPEN 문이 있습니다. 이중 두 개는 바인딩되지 않은 커서 변수를 사용하고 세 번째는 바인딩된 커서 변수를 사용합니다.

  • 선택을 위해 열기: 커서 변수가 열리고 지정된 쿼리가 실행됩니다. 커서는 아직 열 수 없습니다. 또한 바인딩되지 않은 커서(즉, 단순 refcursor 변수로)로 선언되어 있어야 합니다. SELECT 쿼리는 PL/pgSQL의 다른 SELECT 문과 동일한 방식으로 취급됩니다.

    OPEN cursor_name FOR SELECT ...;

    다음은 그 한 예입니다.

    OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
  • 실행을 위해 열기: 커서 변수가 열리고 지정된 쿼리가 실행됩니다. 커서는 아직 열 수 없습니다. 또한 바인딩되지 않은 커서(즉, 단순 refcursor 변수로)로 선언되어 있어야 합니다. 쿼리가 EXECUTE 명령에서와 동일한 방식으로 문자열 표현식으로 지정됩니다. 쿼리가 실행마다 다를 수 있으므로 이 접근 방식은 유연성을 제공합니다.

    OPEN cursor_name FOR EXECUTE query_string;

    다음은 그 한 예입니다.

    OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1);
  • 바인딩된 커서 열기: 이 형태의 OPEN은 쿼리가 선언될 때 쿼리가 바인딩된 커서 변수를 여는 데 사용됩니다. 커서는 아직 열 수 없습니다. 실제 인수 값 표현식의 목록은 커서가 인수를 사용하도록 선언된 경우에만 나타나야 합니다. 이러한 값은 쿼리에서 대체됩니다.

    OPEN bound_cursor_name [ ( argument_values ) ];

    다음은 그 한 예입니다.

    OPEN curs2; OPEN curs3(42);

커서가 열린 후 다음에 설명된 문을 사용하여 작업할 수 있습니다. 이러한 문은 커서을 연 동일한 저장 프로시저에서 발생하지 않아도 됩니다. 저장 프로시저에서 refcursor 값을 반환하고 호출자가 커서에서 작업하도록 할 수 있습니다. 모든 포털은 트랜잭션 종료 시 암시적으로 닫힙니다. 따라서 트랜잭션이 종료되기 전까지만 refcursor 값을 사용하여 열린 커서를 참조할 수 있습니다.

  • FETCH는 다음 행을 커서에서 대상으로 가져옵니다. 이 대상은 SELECT INTO와 마찬가지로 행 변수, 레코드 변수 또는 간단한 변수의 쉼표로 구분된 목록이 될 수 있습니다. SELECT INTO와 마찬가지로 특수 변수 FOUND를 확인하여 행을 가져왔는지 여부를 알 수 있습니다.

    FETCH cursor INTO target;

    다음은 그 한 예입니다.

    FETCH curs1 INTO rowvar;
  • CLOSE는 열린 커서의 기본 포털을 닫습니다. 이 문을 사용하여 트랜잭션의 종료 이전에 리소스를 해제할 수 있습니다. 또한 이 문을 사용하여 커서 변수가 다시 열리도록 할 수 있습니다.

    CLOSE cursor;

    다음은 그 한 예입니다.

    CLOSE curs1;

RAISE

RAISE level 문을 사용하여 메시지를 보고하고 오류를 발생시킵니다.

RAISE level 'format' [, variable [, ...]];

가능한 수준은 NOTICE, INFO, LOG, WARNING, EXCEPTION입니다. EXCEPTION은 현재 트랜잭션을 정상적으로 취소하는 오류를 발생시킵니다. 다른 수준은 다른 우선 순위 수준의 메시지만 생성합니다.

형식 문자열 내부의 %는 다음 선택적 인수의 문자열 표현으로 대체됩니다. %%를 작성하여 리터럴 %를 내보냅니다. 현재 선택적 인수는 표현식이 아닌 간단한 변수여야 하고, 형식은 간단한 문자열 리터럴이어야 합니다.

다음 예제에서는 v_job_id 값이 문자열에서 %를 대체합니다.

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

RAISE 문을 사용하여 예외 처리 블록에서 포착된 예외를 다시 발생시킵니다. 이 문은 NONATOMIC 모드 저장 프로시저의 예외 처리 블록에서만 유효합니다.

RAISE;

트랜잭션 제어

Amazon Redshift가 사용하는 PL/pgSQL 언어의 트랜잭션 제어 문을 사용할 수 있습니다. 저장 프로시저 내의 COMMIT, ROLLBACK, TRUNCATE 사용에 대한 자세한 내용은 트랜잭션 관리를 참조하십시오.

NONATOMIC 모드 저장 프로시저에서는 트랜잭션 블록을 시작하는 데 START TRANSACTION을 사용합니다.

START TRANSACTION;
참고

PL/pgSQL 문 START TRANSACTION은 다음과 같은 점에서 SQL 문 START TRANSACTION과 다릅니다.

  • 저장 프로시저 내에서 START TRANSACTION은 BEGIN과 동의어가 아닙니다.

  • PL/pgSQL 문은 선택적 격리 수준 및 액세스 권한 키워드를 지원하지 않습니다.