소개
PostgreSQL에서 SQL 쿼리를 효과적으로 작성하고 이해하기 위해서는 SQL 구문의 기본적인 개념을 명확히 파악하는 것이 필수적입니다. SQL 구문은 데이터베이스와 소통하는 언어의 문법과 같아서, 이를 정확히 알아야 원하는 데이터를 올바르게 조회하고 조작할 수 있습니다.
이 게시물에서는 PostgreSQL 17.4 공식 문서를 기준으로 SQL 구문의 세 가지 핵심 구성 요소인 어휘 구조(Lexical Structure), 값 표현식(Value Expressions), 그리고 함수 호출(Calling Functions)에 대해 자세히 알아보겠습니다.
1. 어휘 구조 (Lexical Structure): SQL 구문의 기본 단위
개요
모든 SQL 명령은 토큰(Token) 이라고 불리는 기본적인 구성 요소들의 연속으로 이루어집니다. 토큰은 SQL 언어에서 의미를 가지는 최소 단위이며, 키워드, 식별자, 상수, 연산자, 특수 문자 등이 있습니다. 토큰들은 일반적으로 공백(스페이스, 탭, 줄 바꿈)으로 구분되지만, 모호성이 없는 경우에는 공백 없이 붙여 쓸 수도 있습니다. 예를 들어, 특수 문자가 다른 유형의 토큰 옆에 오는 경우가 그렇습니다. 주석은 토큰으로 취급되지 않으며 공백과 동일하게 처리됩니다.
식별자(Identifiers)와 키워드(Keywords)
- 식별자(Identifiers): 테이블, 뷰, 컬럼, 함수 등 데이터베이스 객체를 식별하는 데 사용되는 이름입니다. 예를 들어 users, order_id, calculate_total 등이 식별자에 해당합니다.
- 키워드(Keywords): SELECT, UPDATE, INSERT, WHERE, FROM 등과 같이 SQL 언어 자체에서 특별하고 고정된 의미를 가지는 단어들입니다. 어떤 토큰이 식별자인지 키워드인지는 SQL 언어의 문맥을 이해해야 구분할 수 있습니다. PostgreSQL의 모든 키워드 목록은 다음 링크에서 확인할 수 있습니다.
https://www.postgresql.org/docs/current/sql-keywords-appendix.html
Appendix C. SQL Key Words
Appendix C. SQL Key Words Table C.1 lists all tokens that are key words in the SQL standard and in PostgreSQL 17.4. Background …
www.postgresql.org
- 작성 규칙:
- 식별자와 키워드는 반드시 문자(a-z, 발음 구별 부호가 있는 문자, 비라틴 문자 포함) 또는 밑줄(_)로 시작해야 합니다.
- 두 번째 문자부터는 문자, 밑줄, 숫자(0-9), 또는 달러 기호($)를 사용할 수 있습니다. 단, 달러 기호는 SQL 표준에서는 허용하지 않으므로 이식성을 고려해야 합니다.
- 식별자의 최대 길이는 시스템 설정(NAMEDATALEN)에 따라 결정되며, 기본값은 64로 설정되어 있어 실제 사용 가능한 최대 길이는 63바이트입니다. 이보다 긴 이름은 시스템에 의해 자동으로 잘립니다.
- 따옴표 없는 식별자 (Unquoted Identifiers):
- 일반적으로 사용하는 식별자입니다 (예: my_table, user_id).
- 대소문자를 구분하지 않습니다. PostgreSQL은 내부적으로 따옴표 없는 식별자를 모두 소문자로 변환하여 처리합니다. 따라서 MY_TABLE, my_table, My_Table은 모두 동일한 mytable로 인식됩니다.
- SQL 표준에서는 따옴표 없는 식별자를 대문자로 변환하도록 규정하고 있어, PostgreSQL의 방식과는 차이가 있습니다. 이로 인해 다른 데이터베이스 시스템과의 이식성 문제가 발생할 수 있습니다. 따라서 프로젝트 내에서 일관된 명명 규칙(예: 항상 소문자와 밑줄 사용)을 따르거나, 대소문자 구분이 필요한 경우 항상 따옴표 있는 식별자를 사용하는 것이 권장됩니다. 예를 들어, CREATE TABLE MyTable (...) 쿼리를 실행하면 실제로는 mytable 테이블이 생성됩니다. 이후 SELECT * FROM "MyTable" 로 조회하면 오류가 발생합니다. 대소문자를 유지하려면 "MyTable"로 생성해야 합니다.
- 따옴표 있는 식별자 (Quoted Identifiers):
- 큰따옴표(")로 묶어서 표현합니다 (예: "My Table", "user id").
- 대소문자를 구분합니다. "My_Table"과 "my_table"은 서로 다른 식별자로 취급됩니다.
- SQL 키워드도 따옴표로 묶으면 식별자로 사용할 수 있습니다 (예: "select", "table").
- 공백이나 SQL에서 특수한 의미를 가지는 문자(*, - 등)를 식별자에 포함시킬 수 있습니다.
- 식별자 내부에 큰따옴표를 포함시키려면 두 개의 큰따옴표("")를 연속해서 사용합니다.
- 유니코드 이스케이프를 사용하여 식별자를 정의할 수도 있습니다 (U&"..." 구문).
상수 (Constants)
상수는 SQL 문 내에서 고정된 값을 나타냅니다. PostgreSQL은 다양한 타입의 상수를 지원합니다.
- 문자열 상수(String Constants):
- 작은따옴표(')로 묶어서 표현합니다. 예: 'Hello World', 'PostgreSQL'.
- 문자열 내부에 작은따옴표를 포함하려면 두 개의 작은따옴표('')를 사용합니다. 예: 'Dianne''s horse'는 Dianne's horse를 의미합니다.
- C 스타일 이스케이프: 문자열 앞에 E를 붙여(E'...') C 언어 스타일의 백슬래시 이스케이프 시퀀스를 사용할 수 있습니다. 예를 들어, E'\n'은 줄 바꿈, E'\t'는 탭, E'\\'는 백슬래시 자체, E'\''는 작은따옴표를 나타냅니다. 8진수(\ooo)나 16진수(\xhh)로 바이트 값을 직접 지정할 수도 있습니다.
- 유니코드 이스케이프: 문자열 앞에 U&를 붙여(U&'...') 유니코드 문자를 코드 포인트로 지정할 수 있습니다. \xxxx (4자리 16진수) 또는 \+xxxxxx (6자리 16진수) 형식을 사용합니다. 예를 들어, U&'d\0061t\+000061'는 'data'와 동일합니다. UESCAPE 절을 사용하여 기본 이스케이프 문자인 백슬래시(\) 대신 다른 문자를 지정할 수도 있습니다.
- 달러-따옴표 문자열(Dollar-Quoted Strings): 복잡한 문자열, 특히 작은따옴표나 백슬래시가 많이 포함된 문자열(예: 함수 본문, XML/JSON 조각)을 쉽게 표현하는 방법입니다. $$문자열 내용$$ 형식이나, 태그를 사용하여 $tag$문자열 내용$tag$ 형식으로 작성합니다. 태그는 $ 기호로 시작하지 않는 임의의 식별자입니다. 달러-따옴표 내부의 모든 문자는 리터럴로 취급되므로, 이스케이프 처리가 전혀 필요 없습니다. 예를 들어, $$Dianne's horse$$는 'Dianne''s horse'와 동일합니다. 태그를 사용하면 달러-따옴표 문자열을 중첩하여 사용할 수도 있습니다. 함수 본문처럼 작은따옴표가 많이 사용되는 텍스트에서는 달러-따옴표를 사용하는 것이 C 스타일 이스케이프보다 훨씬 가독성이 높고 유지보수가 용이합니다.
- 숫자 상수(Numeric Constants):
- 정수(예: 42, -100), 부동 소수점 수(예: 3.14159, -0.5, .001, 5e2 (5 * 10^2), 1.925e-3 (1.925 * 10^-3))를 포함합니다.
- 16진수(0x), 8진수(0o), 2진수(0b) 접두사를 사용하여 비십진 정수 상수를 표현할 수 있습니다 (예: 0xFF, 0o77, 0b1111).
- 가독성을 위해 숫자 사이에 밑줄(_)을 사용할 수 있습니다 (예: 1_000_000).
- 타입 추론: 소수점이나 지수가 없는 숫자는 기본적으로 integer 타입으로 간주됩니다. 값이 integer 범위를 넘어서면 bigint로, bigint 범위를 넘어서면 numeric으로 처리됩니다. 소수점이나 지수가 있는 숫자는 numeric 타입으로 간주됩니다.
- 불리언 상수(Boolean Constants):
- 참(True)은 TRUE, 거짓(False)은 FALSE, 알 수 없는 값은 NULL 키워드로 표현합니다. ` 예시를 통해 추론 가능)
- 비트 문자열 상수(Bit-String Constants):
- 이진수 형태는 B 다음에 0과 1로 구성된 문자열을 작은따옴표로 묶어 표현합니다 (예: B'1001').
- 16진수 형태는 X 다음에 16진수 문자(0-9, A-F)열을 작은따옴표로 묶어 표현합니다 (예: X'1FF'). 각 16진수 문자는 4개의 비트를 나타냅니다.
- 기타 타입 상수:
- 다른 특정 데이터 타입의 상수를 표현하려면 명시적인 타입 캐스트(Type Cast)를 사용해야 합니다. PostgreSQL은 여러 캐스트 구문을 지원합니다 :
- type 'string' (표준 SQL 구문): 예) DATE '2024-07-15', INTERVAL '1 day', BOOLEAN 'true'
- 'string'::type (PostgreSQL 고유 구문): 예) '1.23'::REAL, `'
- 다른 특정 데이터 타입의 상수를 표현하려면 명시적인 타입 캐스트(Type Cast)를 사용해야 합니다. PostgreSQL은 여러 캐스트 구문을 지원합니다 :
- 몇 가지 제약 조건이 있습니다:
- 주석 시작 기호인 -- 와 /* 는 연산자 이름에 포함될 수 없습니다.
- 여러 문자로 구성된 연산자 이름은 + 또는 - 로 끝날 수 없습니다. 단, 이름에 ~! @ # % ^ & | \?문자 중 하나 이상이 포함된 경우는 예외입니다 (예:@-는 허용되지만*-`는 허용되지 않음). 이는 SQL 호환 쿼리를 파싱할 때 토큰 사이에 공백이 없어도 모호성을 피하기 위함입니다.
- 우선순위(Precedence):
- 연산자들은 고정된 우선순위(Precedence)와 결합성(Associativity) 규칙을 따릅니다. 이 규칙은 여러 연산자가 함께 사용될 때 어떤 연산이 먼저 수행될지를 결정합니다.
- 예를 들어, 곱셈(*)은 덧셈(+)보다 우선순위가 높고, 대부분의 연산자는 왼쪽 결합성(left-associative)을 가집니다.
- 괄호 ()를 사용하여 연산의 우선순위를 명시적으로 지정하거나 기본 우선순위를 변경할 수 있습니다. 복잡한 표현식에서는 괄호를 사용하여 의도를 명확히 하는 것이 좋습니다.
- 다음은 PostgreSQL 17의 연산자 우선순위 표입니다 (높은 순서에서 낮은 순서로) :
연산자/요소 | 결합성 | 설명 |
. | 좌 | 테이블/컬럼 이름 구분자 |
:: | 좌 | PostgreSQL 스타일 타입 캐스트 |
[ ] | 좌 | 배열 요소 선택 |
+ - | 우 | 단항 플러스, 단항 마이너스 |
COLLATE | 좌 | 콜레이션 선택 |
AT | 좌 | AT TIME ZONE, AT LOCAL |
^ | 좌 | 거듭제곱 |
* / % | 좌 | 곱셈, 나눗셈, 나머지 |
+ - | 좌 | 덧셈, 뺄셈 |
(다른 모든 연산자) | 좌 | 다른 모든 내장 및 사용자 정의 연산자 |
BETWEEN IN LIKE ILIKE SIMILAR | 범위 포함, 집합 멤버십, 문자열 매칭 | |
< > = <= >= <> | 비교 연산자 | |
IS ISNULL NOTNULL | IS TRUE, IS FALSE, IS NULL, IS DISTINCT FROM 등 | |
NOT | 우 | 논리 부정 |
AND | 좌 | 논리 곱 (conjunction) |
OR | 좌 | 논리 합 (disjunction) |
특수 문자 (Special Characters)
연산자 외에도 몇몇 비영문숫자 문자들은 SQL 구문에서 특별한 의미를 가집니다 :
- 괄호 (): 표현식을 그룹화하고 우선순위를 지정하며, 특정 SQL 명령의 고정된 구문의 일부로 사용됩니다.
- 대괄호 ``: 배열의 특정 요소를 선택하는 데 사용됩니다 (예: my_array).
- 쉼표 ,: 리스트의 요소들을 구분하는 데 사용됩니다 (예: INSERT INTO table (col1, col2)...).
- 세미콜론 ;: SQL 명령의 끝을 나타냅니다. 문자열 상수나 따옴표 있는 식별자 내부를 제외하고는 명령 중간에 나타날 수 없습니다.
- 콜론 :: 배열의 "슬라이스(slice)"를 선택하는 데 사용됩니다 (예: my_array[2:4]). 일부 SQL 방언(예: 임베디드 SQL)에서는 변수 이름 앞에 붙여 사용하기도 합니다.
- 별표 *: SELECT * 구문에서 테이블의 모든 컬럼을 나타내거나, COUNT(*)와 같이 집계 함수에서 특별한 의미로 사용됩니다.
- 마침표 .: 숫자 상수에서 소수점을 나타내거나, 스키마, 테이블, 컬럼 이름을 구분하는 데 사용됩니다 (예: my_schema.my_table.my_column).
- 달러 기호 $: 숫자와 함께 사용되어 함수 정의나 준비된 구문(prepared statement) 본문 내에서 위치 지정 파라미터(positional parameter)를 나타냅니다 (예: $1, $2). 다른 문맥에서는 식별자의 일부나 달러-따옴표 문자열 상수의 일부로 사용될 수 있습니다.
주석 (Comments)
SQL 코드 내에 설명을 추가하기 위해 주석을 사용할 수 있습니다. 주석은 쿼리 실행에 영향을 주지 않으며, 파싱 전에 제거되어 공백처럼 취급됩니다. PostgreSQL은 두 가지 형태의 주석을 지원합니다 :
- 한 줄 주석: 두 개의 하이픈(--)으로 시작하며, 해당 줄의 끝까지 모든 텍스트가 주석으로 처리됩니다.
SELECT * FROM users; -- 사용자의 모든 정보를 조회합니다.
- 블록 주석: /*로 시작하여 */로 끝납니다. 여러 줄에 걸쳐 작성할 수 있으며, 중첩하여 사용할 수도 있습니다 (C 언어와 달리 중첩이 가능합니다).
/*
이것은 여러 줄에 걸친
블록 주석입니다.
/* 중첩된 블록 주석도 가능합니다. */
*/
SELECT name FROM products WHERE price > 100;
2. 값 표현식 (Value Expressions): SQL에서 값 계산하기
정의
값 표현식(Value Expression) 또는 스칼라 표현식(Scalar Expression) 은 평가될 때 단일 값(예: 숫자, 문자열, 날짜, 불리언 등)을 결과로 내는 SQL 구문입니다. 이는 여러 행과 열로 구성된 테이블을 결과로 내는 테이블 표현식(Table Expression)과 대조됩니다. 값 표현식은 SQL 쿼리의 다양한 부분에서 사용됩니다. 예를 들어, SELECT 문의 결과 목록(target list)에서 출력될 값을 계산하거나, INSERT 또는 UPDATE 문에서 새로운 컬럼 값을 지정하거나, WHERE 절이나 HAVING 절에서 검색 조건을 정의하는 데 사용됩니다.
표현식 종류 카탈로그
PostgreSQL은 다양한 종류의 값 표현식을 지원합니다 :
- 상수/리터럴 (Constants/Literals): 100, 'Hello World', TRUE, DATE '2024-07-15' 와 같이 고정된 값 자체입니다.
- 컬럼 참조 (Column References): 테이블의 특정 컬럼 값을 나타냅니다. table_name.column_name 또는 alias.column_name 형태로 사용하며, 쿼리 내에서 컬럼 이름이 고유하다면 테이블 이름이나 별칭(alias)을 생략할 수 있습니다 (예: user_id).
- 위치 지정 파라미터 (Positional Parameters): 함수 정의나 준비된 구문(prepared statement) 내에서 외부로부터 전달되는 값을 참조하는 데 사용됩니다. $1, $2 와 같이 표현합니다.
- 연산자 (Operators): 하나 또는 두 개의 피연산자에 적용되어 새로운 값을 계산합니다. 산술 연산자 (+, -, *, /), 비교 연산자 (=, >, <, LIKE, BETWEEN), 논리 연산자 (AND, OR, NOT) 등이 있습니다.
- 함수 호출 (Function Calls): 내장 함수나 사용자 정의 함수를 호출하여 그 반환 값을 사용합니다. 예: sqrt(numeric_column), lower(text_column), now().
- 집계 표현식 (Aggregate Expressions): 여러 행의 값을 기반으로 단일 결과 값을 계산합니다. count(*), sum(amount), avg(score) 등이 있으며, GROUP BY 절과 함께 사용되는 경우가 많습니다. FILTER 절을 사용하여 집계 대상 행을 제한할 수도 있습니다.
- 윈도우 함수 호출 (Window Function Calls): 현재 행과 관련된 행들의 집합(윈도우)에 대해 계산을 수행합니다. 집계 함수와 유사하지만, 행들을 그룹으로 축약하지 않습니다. rank() OVER (ORDER BY score DESC), sum(salary) OVER (PARTITION BY department) 와 같이 OVER 절과 함께 사용됩니다.
- 타입 캐스트 (Type Casts): 한 데이터 타입의 값을 다른 데이터 타입으로 명시적으로 변환합니다. expression::type (예: count::float), CAST(expression AS type) (예: CAST(price AS numeric(10,2))) 구문을 사용합니다.
- 콜레이션 표현식 (Collation Expressions): 표현식의 정렬 규칙(collation)을 지정합니다. 주로 문자열 비교나 정렬 시 특정 로케일 규칙을 적용할 때 사용됩니다 (예: column_name COLLATE "fr_FR").
- 스칼라 서브쿼리 (Scalar Subqueries): 단일 행, 단일 컬럼을 반환하는 SELECT 문을 괄호로 묶어 사용합니다. 서브쿼리의 결과 값이 표현식의 값이 됩니다. 만약 서브쿼리가 행을 반환하지 않으면 결과는 NULL이 됩니다. 예: (SELECT max(price) FROM products).
- 배열 생성자 (Array Constructors): 배열 값을 생성합니다. ARRAY[element1, element2,...] 또는 ARRAY(subquery) 구문을 사용합니다. 예: ARRAY, ARRAY(SELECT user_id FROM active_users).
- 배열 첨자 (Subscripts): 배열의 특정 요소나 부분(슬라이스)에 접근합니다. array_expression[index] 또는 array_expression[lower_index:upper_index] 구문을 사용합니다. 예: score , names[1:5].
- 로우 생성자 (Row Constructors): 로우(row) 또는 복합(composite) 타입의 값을 생성합니다. ROW(field1, field2,...) 또는 괄호만 사용하여 (field1, field2,...) 형태로 표현합니다. 예: ROW(1, 'apple', 1.50), (user_id, user_name).
- 필드 선택 (Field Selection): 복합 타입 값에서 특정 필드의 값을 추출합니다. composite_value.field_name 구문을 사용합니다. 컬럼 참조(table.column)도 필드 선택의 한 형태입니다. table_alias.* 와 같이 사용하여 모든 필드를 선택할 수도 있습니다.
- 괄호로 묶인 표현식 (Parenthesized Expressions): 표현식의 일부를 괄호로 묶어 평가 순서를 명시적으로 제어하거나 가독성을 높입니다. 예: (price + tax) * quantity.
- 조건 표현식 (Conditional Expressions): 조건에 따라 다른 값을 반환합니다.
- CASE WHEN condition THEN result [WHEN...] END: 가장 일반적인 조건 분기.
- CASE expression WHEN value THEN result [WHEN...] END: 특정 값과 비교하는 간단한 형태.
- COALESCE(value1, value2,...): 인자 목록 중 첫 번째로 NULL이 아닌 값을 반환.
- NULLIF(value1, value2): 두 값이 같으면 NULL을, 다르면 value1을 반환.
- GREATEST(value1, value2,...): 인자 목록 중 가장 큰 값을 반환.
- LEAST(value1, value2,...): 인자 목록 중 가장 작은 값을 반환.
표현식 평가 규칙 및 우선순위
- 복잡한 표현식의 평가 순서는 섹션 1 에서 설명한 연산자 우선순위 규칙과 결합성에 의해 결정됩니다.
- 예를 들어, a + b * c는 곱셈(*)이 덧셈(+)보다 우선순위가 높으므로 a + (b * c)와 같이 평가됩니다.
- 괄호 () 를 사용하면 이러한 기본 평가 순서를 변경할 수 있습니다. (a + b) * c는 덧셈을 먼저 수행합니다. 괄호는 또한 복잡한 표현식의 가독성을 높이는 데 중요합니다.
- 값 표현식과 테이블 표현식의 구분은 매우 중요합니다. SELECT 목록이나 WHERE 절과 같이 스칼라 값을 기대하는 곳에서는 값 표현식을 사용해야 하고, FROM이나 JOIN 절과 같이 테이블(행 집합)을 기대하는 곳에서는 테이블 표현식을 사용해야 합니다. 스칼라 서브쿼리는 테이블 표현식이지만 단일 스칼라 값을 반환하도록 설계되어 값 표현식이 필요한 곳에 사용할 수 있는 예외적인 경우입니다.
- PostgreSQL은 종종 데이터 타입이 다른 피연산자 간의 연산을 위해 암시적 타입 변환을 수행합니다 (예: integer + numeric). 하지만 이러한 암시적 변환에 의존하는 것은 잠재적인 오류나 성능 저하를 유발할 수 있습니다. 예를 들어, 5 / 2는 정수 나눗셈으로 2를 반환하지만, 5::numeric / 2는 2.5를 반환합니다. 따라서 의도한 타입과 연산을 명확히 하기 위해 명시적 타입 캐스트(:: 또는 CAST)를 사용하는 것이 좋습니다. 이는 코드의 정확성, 가독성 및 유지보수성을 향상시킵니다.
3. 함수 및 프로시저 호출 (Calling Functions and Procedures)
함수 호출 구문 개요
PostgreSQL에서 함수를 호출하는 기본 구문은 다음과 같습니다 :
function_name ( [ argument [,...] )
여기서 function_name은 호출할 함수의 이름이며, 괄호 안에는 0개 이상의 인자(argument)를 쉼표로 구분하여 전달합니다.
인자 전달 방식
PostgreSQL은 함수 인자를 전달하는 세 가지 표기법을 지원합니다 :
- 위치 지정 표기법 (Positional Notation):
- 가장 전통적인 방식으로, 함수 선언 시 정의된 파라미터 순서대로 인자 값을 전달합니다.
- 예: concat_lower_or_upper('Hello', 'World', true)
- 함수 정의 시 파라미터에 기본값(DEFAULT)이 설정되어 있다면, 오른쪽부터 순서대로 해당 인자를 생략할 수 있습니다. 생략된 파라미터는 기본값을 사용합니다.
- 예: concat_lower_or_upper('Hello', 'World') (세 번째 파라미터 uppercase의 기본값 false 사용)
- 이름 지정 표기법 (Named Notation):
- 각 인자 값을 해당 파라미터 이름과 함께 param_name => value 형식으로 전달합니다.
- 인자의 순서는 중요하지 않으며, 어떤 파라미터에 어떤 값이 전달되는지 명확하게 보여줍니다.
- 기본값이 있는 파라미터는 자유롭게 생략할 수 있습니다. 이는 파라미터가 많거나 기본값이 많은 함수를 호출할 때 매우 유용합니다.
- 예: concat_lower_or_upper(a => 'Hello', uppercase => true, b => 'World')
- 하위 호환성을 위해 param_name := value 구문도 지원됩니다.
- 파라미터 수가 많거나, 동일 타입의 파라미터가 여러 개 있거나, 기본값을 활용해야 하는 경우, 이름 지정 표기법은 위치 지정 표기법보다 코드 가독성과 유지보수성을 크게 향상시킵니다. 위치 지정 방식은 함수 시그니처 변경 시 오류를 유발하기 쉽지만, 이름 지정 방식은 파라미터 이름으로 명시적으로 연결되므로 이러한 변경에 더 강건합니다.
- 혼합 표기법 (Mixed Notation):
- 위치 지정 표기법과 이름 지정 표기법을 함께 사용합니다.
- 단, 위치 지정 인자들이 반드시 이름 지정 인자들보다 먼저 나와야 합니다.
- 예: concat_lower_or_upper('Hello', 'World', uppercase => true) ('Hello'와 'World'는 위치로, uppercase => true는 이름으로 전달)
- 제한 사항: 현재 집계 함수(Aggregate Function)를 직접 호출할 때는 이름 지정 또는 혼합 표기법을 사용할 수 없습니다. 하지만 집계 함수를 윈도우 함수(Window Function)로 사용할 때는 가능합니다.
함수 결정 (Function Resolution - 오버로딩)
PostgreSQL에서는 함수의 이름이 같더라도 인자의 개수나 데이터 타입이 다르면 서로 다른 함수로 정의할 수 있습니다. 이를 함수 오버로딩(Function Overloading)이라고 합니다. 특정 함수 호출이 있을 때, PostgreSQL은 어떤 함수를 실행할지 결정하기 위해 다음과 같은 함수 결정(Function Resolution) 과정을 따릅니다 :
- 후보 함수 선정: 호출된 함수 이름과 인자 개수에 맞는 함수들을 시스템 카탈로그(pg_proc)에서 찾습니다. 스키마가 지정되지 않았다면 현재 search_path 내의 함수들을 고려하며, 동일 시그니처 함수가 여러 스키마에 있다면 경로상 가장 먼저 나오는 함수만 고려합니다. 스키마가 지정되었다면 해당 스키마 내의 함수만 고려합니다.
- VARIADIC 함수 처리: 가변 인자(VARIADIC) 함수가 후보에 있고, 호출 시 VARIADIC 키워드가 사용되지 않았다면, 가변 인자 파라미터를 실제 전달된 인자 타입들로 확장하여 고려합니다.
- 기본값 고려: 기본값이 있는 파라미터를 가진 함수들도 후보로 고려합니다 (호출 시 해당 인자가 생략된 경우).
- 정확한 타입 일치 확인: 호출 시 전달된 인자들의 데이터 타입과 정확히 일치하는 시그니처를 가진 함수가 있는지 확인합니다. 있다면 해당 함수를 선택하고 결정 과정을 종료합니다.
- 특수 타입 변환 구문 확인: 정확한 일치가 없고, 함수 호출이 단일 인자를 가지며 함수 이름이 특정 데이터 타입의 이름과 같다면, 타입 캐스트(CAST) 구문으로 처리될 수 있는지 확인합니다.
- 최적 일치 찾기 (Best Match): 정확한 일치가 없다면, 다음 규칙에 따라 가장 적합한 함수를 찾습니다:
- 입력 인자 타입으로 암시적 변환이 불가능한 함수는 후보에서 제외합니다.
- 도메인 타입 인자는 기본 타입으로 간주합니다.
- 입력 타입과 정확히 일치하는 파라미터가 가장 많은 후보를 선호합니다.
- 타입 변환이 필요한 경우, 해당 타입 카테고리 내의 "선호 타입(preferred type)"을 받아들이는 후보를 선호합니다.
- unknown 타입 인자가 있다면, 다른 인자들의 타입이나 후보 함수들이 받아들이는 타입 카테고리(주로 string 선호)를 기반으로 타입을 추론하여 최적 후보를 좁힙니다.
- 모호성 오류: 위의 과정을 거쳐도 유일한 최적 함수를 결정할 수 없다면, "ambiguous function call" 오류가 발생합니다.
함수 호출 시 "function does not exist" 또는 "ambiguous function call" 오류가 발생하는 경우, 이는 단순히 함수가 없어서가 아니라 제공된 인자 타입이 정확히 일치하는 시그니처가 없고, 암시적 변환 및 최적 일치 규칙으로도 고유한 후보를 찾지 못했기 때문일 가능성이 높습니다. 예를 들어 my_func(123) 호출이 실패했다면, my_func(bigint)는 있지만 integer에서 bigint로의 암시적 변환이 해당 컨텍스트에서 허용되지 않거나 선호되지 않기 때문일 수 있습니다. 이 경우 my_func(123::bigint)와 같이 명시적으로 인자 타입을 캐스팅하면 문제를 해결하고 PostgreSQL이 정확한 함수를 찾도록 도울 수 있습니다.
프로시저 호출
- 함수(CREATE FUNCTION)와 달리, 프로시저(CREATE PROCEDURE)는 값을 반환하지 않으며(물론 OUT 파라미터를 가질 수는 있음) 트랜잭션 제어에 대한 규칙이 다릅니다.
- 프로시저를 호출할 때는 SELECT 문이 아닌 CALL 명령을 사용해야 합니다.
CALL my_procedure(argument1, argument2);
결론
지금까지 PostgreSQL 17.4 SQL 구문의 세 가지 핵심 요소인 어휘 구조, 값 표현식, 함수 호출에 대해 살펴보았습니다. 어휘 구조는 SQL을 구성하는 가장 기본적인 벽돌(식별자, 키워드, 상수 등)이며, 값 표현식은 이러한 벽돌을 조합하여 의미 있는 값을 계산하는 방법입니다. 마지막으로 함수 호출은 강력한 재사용 가능 코드 블록을 실행하는 메커니즘을 제공하며, PostgreSQL의 오버로딩과 타입 시스템은 유연한 함수 사용을 가능하게 합니다.
'Database' 카테고리의 다른 글
[Database] PostgreSQL - 권한, 스키마, 상속, 파티셔닝 (0) | 2025.04.23 |
---|---|
[Database] PostgreSQL - Table Management (DDL) (0) | 2025.04.21 |
[Database] PostgreSQL - Transaction (0) | 2024.07.18 |
[Database] PostgreSQL - Index (0) | 2024.07.18 |
[Database] PostgreSQL Commands - DCL (0) | 2024.07.18 |