키워드와 식별자

키워드와 식별자 (Keywords and Identifiers)

다른 SQL 방언과 프로그래밍 언어와 마찬가지로, DuckDB SQL의 식별자(identifier)에는 여러 규칙이 적용돼요.

출처: 문서

본문

식별자 (Identifiers)

  • 따옴표 없는(unquoted) 식별자는 여러 규칙을 따라야 해요.
    • 예약 키워드(reserved keyword)여선 안 돼요 (duckdb_keywords() 참고). 예: SELECT 123 AS SELECT는 실패해요.
    • 숫자나 특수 문자로 시작하면 안 돼요. 예: SELECT 123 AS 1col은 유효하지 않아요.
    • 공백(탭과 줄바꿈 포함)을 포함할 수 없어요.
  • 식별자는 큰따옴표(")로 감싸서 인용할 수 있어요. 인용된 식별자는 어떤 키워드, 공백, 특수 문자도 사용할 수 있어요. 예: "SELECT"" § 🦆 ¶ "는 유효한 식별자예요.
  • 큰따옴표는 따옴표 문자를 반복해서 이스케이프할 수 있어요. 예: IDENTIFIER "X"라는 식별자를 만들려면 "IDENTIFIER ""X"""를 사용해요.

식별자 중복 제거 (Deduplicating Identifiers)

경우에 따라 중복 식별자가 발생할 수 있어요. 예를 들어 중첩 데이터 구조를 unnest할 때 컬럼 이름이 충돌할 수 있어요. 이런 경우 DuckDB는 다음 규칙에 따라 컬럼 이름을 다시 지정해 자동으로 중복을 제거해요.

  • ⟨name⟩ 컬럼의 첫 번째 인스턴스는 이름을 바꾸지 않아요.
  • 이후 인스턴스는 ⟨name⟩_⟨count⟩로 바꾸는데, ⟨count⟩는 1부터 시작해요.

예를 들어:

SELECT *
FROM (SELECT unnest({'a': 42, 'b': {'a': 88, 'b': 99}}, recursive := true));
a a_1 b
42 88 99

데이터베이스 이름 (Database Names)

데이터베이스 이름은 식별자 규칙을 따르지만, 추가적으로 DuckDB의 두 내부 데이터베이스 스키마 이름, systemtemp를 피하는 것이 모범 사례예요. 기본적으로 영속 데이터베이스는 확장자를 뺀 파일 이름으로 이름 지어져요. 그래서 system.dbtemp.db(그리고 system.duckdbtemp.duckdb) 파일 이름은 각각 systemtemp 데이터베이스 이름이 돼요. 이런 이름 중 하나를 가진 데이터베이스에 attach해야 한다면 별칭을 사용해요.

ATTACH 'temp.db' AS temp2;
USE temp2;

대소문자 규칙 (Rules for Case-Sensitivity)

키워드와 함수 이름 (Keywords and Function Names)

SQL 키워드와 함수 이름은 DuckDB에서 대소문자를 구분하지 않아요.

예를 들어 다음 두 쿼리는 동일해요.

select COS(Pi()) as CosineOfPi;
SELECT cos(pi()) AS CosineOfPi;
CosineOfPi
-1.0

식별자의 대소문자 구분 (Case-Sensitivity of Identifiers)

DuckDB의 식별자는 PostgreSQL과 비슷하게 항상 대소문자를 구분하지 않아요. 그러나 PostgreSQL(그리고 일부 다른 주요 SQL 구현)과 달리 DuckDB는 인용된 식별자도 대소문자를 구분하지 않아요.

식별자 비교: 대소문자 비구분은 ASCII 기반 비교로 구현돼요. col_Acol_a는 같지만 col_á는 그것들과 같지 않아요.

SELECT col_A FROM (SELECT 'x' AS col_a); -- 성공
SELECT col_á FROM (SELECT 'x' AS col_a); -- 실패

대소문자 보존: DuckDB는 식별자를 대소문자 비구분으로 취급하지만, 식별자의 대소문자는 보존해요. 즉, 쿼리가 식별자를 참조할 때 다른 대소문자를 사용해도 각 문자의 대소문자(대문자/소문자)는 사용자가 원래 지정한 대로 유지돼요.

예를 들어:

CREATE TABLE tbl AS SELECT cos(pi()) AS CosineOfPi;
SELECT cosineofpi FROM tbl;
CosineOfPi
-1.0

이 동작을 바꾸려면 preserve_identifier_case 설정 옵션false로 설정해요.

중첩 데이터 구조의 키 대소문자 구분 (Case-Sensitivity of Keys in Nested Data Structures)

MAP의 키는 대소문자를 구분해요.

SELECT MAP(['key1'], [1]) = MAP(['KEY1'], [1]) AS equal;
false

UNIONSTRUCT의 키는 대소문자를 구분하지 않아요.

SELECT {'key1': 1} = {'KEY1': 1} AS equal;
true
SELECT union_value(key1 := 1) = union_value(KEY1 := 1) as equal;
true
충돌 처리 (Handling Conflicts)

충돌 시, 같은 식별자가 다른 대소문자로 표기되면 하나가 무작위로 선택돼요. 예를 들어:

CREATE TABLE t1 (idfield INTEGER, x INTEGER);
CREATE TABLE t2 (IdField INTEGER, y INTEGER);
INSERT INTO t1 VALUES (1, 123);
INSERT INTO t2 VALUES (1, 456);
SELECT * FROM t1 NATURAL JOIN t2;
idfield x y
1 123 456
대소문자 보존 비활성화 (Disabling Preserving Cases)

preserve_identifier_case 설정 옵션false로 설정하면 모든 식별자가 소문자로 바뀌어요.

SET preserve_identifier_case = false;
CREATE TABLE tbl AS SELECT cos(pi()) AS CosineOfPi;
SELECT CosineOfPi FROM tbl;
cosineofpi
-1.0

더 알아보기 (Learn more)