ANSI 호환성

ANSI 호환성 (ANSI Compliance)

Spark SQL에서 SQL 표준을 준수하도록 하는 두 가지 옵션이 있어요: spark.sql.ansi.enabledspark.sql.storeAssignmentPolicy예요. 기본적으로 Spark는 ANSI 호환 모드를 사용해서, 잘못된 연산이 있을 때 null 대신 런타임 예외를 던지고, ANSI 방식의 타입 변환 규칙을 적용해요. 이 문서에서 ANSI 모드가 산술 연산, 타입 변환, SQL 파싱에 주는 영향을 차근차근 살펴볼게요.

출처: 문서

본문

Spark SQL에서는 SQL 표준을 준수하기 위한 두 가지 옵션이 있어요: spark.sql.ansi.enabledspark.sql.storeAssignmentPolicy (자세한 내용은 아래 표를 참고하세요).

기본적으로 spark.sql.ansi.enabledtrue이고, Spark SQL은 Hive 호환 방식 대신 ANSI 호환(dialect) 방식을 사용해요. 예를 들어 SQL 연산자/함수에 대한 입력이 유효하지 않으면, Spark는 null 결과를 반환하는 대신 런타임에 예외를 던져요. 일부 ANSI dialect 기능은 ANSI SQL 표준에서 직접 온 것이 아닐 수 있지만, 그 동작 방식은 ANSI SQL의 스타일과 일치해요.

또한 Spark SQL은 테이블에 행을 삽입할 때 암시적 캐스팅 동작을 제어하는 별도의 옵션을 가지고 있어요. 이 캐스팅 동작은 표준에서 store assignment 규칙으로 정의돼요.

기본적으로 spark.sql.storeAssignmentPolicyANSI이고, Spark SQL은 ANSI store assignment 규칙을 준수해요.

속성 이름 (Property Name) 기본값 (Default) 의미 (Meaning) 도입 버전 (Since Version)
spark.sql.ansi.enabled true true일 때 Spark는 ANSI SQL 사양을 따르려 해요: 1. Spark SQL은 정수 오버플로 오류, 문자열 파싱 오류 등을 포함한 잘못된 연산에 대해 런타임 예외를 던져요. 2. Spark는 데이터 타입 간 충돌을 해결하기 위해 다른 타입 강제 변환(type coercion) 규칙을 사용해요. 그 규칙은 데이터 타입 우선순위에 일관되게 기반해요. 3.0.0
spark.sql.storeAssignmentPolicy ANSI 다른 데이터 타입의 컬럼에 값을 삽입할 때 Spark는 타입 변환을 수행해요. 현재 타입 강제 변환 규칙에 대해 3가지 정책을 지원해요: ANSI, legacy, strict. 1. ANSI 정책에서는 Spark가 ANSI SQL에 따라 타입 강제 변환을 수행해요. 실제로는 PostgreSQL과 대부분 동일하게 동작해요. string→int나 double→boolean 같은 비합리적인 타입 변환은 허용하지 않아요. 숫자 타입 컬럼에 삽입할 때 값이 대상 타입의 범위를 벗어나면 오버플로 오류가 발생해요. 2. legacy 정책에서는 유효한 Cast라면 타입 강제 변환을 허용해서, 매우 느슨해요. 예를 들어 string→int나 double→boolean 변환이 허용돼요. 이것은 Spark 2.x에서의 유일한 동작이며 Hive와 호환돼요. 3. strict 정책에서는 타입 강제 변환에서 어떤 정밀도 손실이나 데이터 잘림(truncation)도 허용하지 않아요. 예를 들어 double→int나 decimal→double 변환은 허용되지 않아요. 3.0.0

다음 절들에서는 ANSI 모드가 활성화됐을 때 산술 연산, 타입 변환, SQL 파싱의 동작 변화를 다룰게요. Spark SQL의 타입 변환에는 세 가지 종류가 있는데, 이 문서에서 하나씩 소개할게요: cast, store assignment, type coercion.

산술 연산 (Arithmetic Operations)

Spark SQL에서 기본적으로 Spark는 interval 타입과 숫자 타입의 오버플로에 대해 런타임에 산술 예외(arithmetic exception)를 던져요. spark.sql.ansi.enabledfalse라면, decimal 타입은 null 값을 생성하고 다른 숫자 타입들은 Java/Scala 프로그램에서의 대응 연산과 같은 방식으로 동작해요 (예: 두 정수의 합이 표현 가능한 최대값을 넘으면 결과가 음수가 됨). 이것은 Spark 3 이전의 동작이에요.

-- `spark.sql.ansi.enabled=true`
SELECT 2147483647 + 1;
org.apache.spark.SparkArithmeticException: [ARITHMETIC_OVERFLOW] integer overflow. Use 'try_add' to tolerate overflow and return NULL instead. If necessary set spark.sql.ansi.enabled to "false" to bypass this error.
== SQL(line 1, position 8) ==
SELECT 2147483647 + 1
       ^^^^^^^^^^^^^^

SELECT abs(-2147483648);
org.apache.spark.SparkArithmeticException: [ARITHMETIC_OVERFLOW] integer overflow. If necessary set spark.sql.ansi.enabled to "false" to bypass this error.

-- `spark.sql.ansi.enabled=false`
SELECT 2147483647 + 1;
+----------------+
|(2147483647 + 1)|
+----------------+
|     -2147483648|
+----------------+

SELECT abs(-2147483648);
+----------------+
|abs(-2147483648)|
+----------------+
|     -2147483648|
+----------------+

Cast

spark.sql.ansi.enabledtrue로 설정되면, CAST 구문에 의한 명시적 캐스팅은 표준에 정의된 잘못된 캐스트 패턴(예: 문자열에서 정수로의 캐스트)에 대해 런타임 예외를 던져요.

또한 ANSI SQL 모드는 ANSI 모드가 꺼져 있을 때 허용되던 다음과 같은 타입 변환을 금지해요:

  • Numeric <=> Binary
  • Date <=> Boolean
  • Timestamp <=> Boolean
  • Date => Numeric

CAST 표현식에서 소스와 대상 데이터 타입의 유효한 조합은 다음 표로 주어져요. “Y”는 해당 조합이 제약 없이 구문상 유효함을, “N”은 유효하지 않음을 나타내요.

Source\Target Numeric String Date Time Timestamp Timestamp_NTZ Interval Boolean Binary Array Map Struct
Numeric Y Y N N Y N Y Y N N N N
String Y Y Y Y Y Y Y Y Y N N N
Date N Y Y N Y Y N N N N N N
Time N Y N Y N N N N N N N N
Timestamp Y Y Y N Y Y N N N N N N
Timestamp_NTZ N Y Y N Y Y N N N N N N
Interval Y Y N N N N Y N N N N N
Boolean Y Y N N N N N Y N N N N
Binary N Y N N N N N N Y N N N
Array N Y N N N N N N N Y N N
Map N Y N N N N N N N N Y N
Struct N Y N N N N N N N N N Y

위 표에서 새 구문의 모든 CAST는 빨간색 Y로 표시되어 있어요:

  • CAST(Numeric AS Numeric): 값이 대상 타입의 범위를 벗어나면 오버플로 예외를 발생시켜요.
  • CAST(String AS (Numeric/Date/Timestamp/Timestamp_NTZ/Interval/Boolean)): 값이 대상 타입으로 파싱될 수 없으면 런타임 예외를 발생시켜요.
  • CAST(Timestamp AS Numeric): epoch 이후 초(seconds)가 대상 타입의 범위를 벗어나면 오버플로 예외를 발생시켜요.
  • CAST(Numeric AS Timestamp): 숫자 값에 1000000(초당 마이크로초)을 곱한 값이 Long 타입의 범위를 벗어나면 오버플로 예외를 발생시켜요.
  • CAST(Array AS Array): 요소 변환 중 오류가 있으면 예외를 발생시켜요.
  • CAST(Map AS Map): 키와 값 변환 중 오류가 있으면 예외를 발생시켜요.
  • CAST(Struct AS Struct): struct 필드 변환 중 오류가 있으면 예외를 발생시켜요.
  • CAST(Numeric AS String): decimal 값을 문자열로 캐스팅할 때 지수가 필요하면 과학적 표기법 대신 항상 일반 문자열 표현을 사용해요.
  • CAST(Interval AS Numeric): day-time interval의 마이크로초 수 또는 year-month interval의 월 수가 대상 타입의 범위를 벗어나면 오버플로 예외를 발생시켜요.
  • CAST(Numeric AS Interval): 숫자 값에 대상 interval의 끝 단위(end-unit)를 곱한 값이 year-month interval은 Int 타입, day-time interval은 Long 타입의 범위를 벗어나면 오버플로 예외를 발생시켜요.
-- Examples of explicit casting

-- `spark.sql.ansi.enabled=true` (This is a default behaviour)
SELECT CAST('a' AS INT);
org.apache.spark.SparkNumberFormatException: [CAST_INVALID_INPUT] The value 'a' of the type "STRING" cannot be cast to "INT" because it is malformed. Correct the value as per the syntax, or change its target type. Use `try_cast` to tolerate malformed input and return NULL instead.
== SQL(line 1, position 8) ==
SELECT CAST('a' AS INT)
       ^^^^^^^^^^^^^^^^

SELECT CAST(2147483648L AS INT);
org.apache.spark.SparkArithmeticException: [CAST_OVERFLOW] The value 2147483648L of the type "BIGINT" cannot be cast to "INT" due to an overflow. Use `try_cast` to tolerate overflow and return NULL instead.

SELECT CAST(DATE'2020-01-01' AS INT);
org.apache.spark.sql.AnalysisException: cannot resolve 'CAST(DATE '2020-01-01' AS INT)' due to data type mismatch: cannot cast date to int.
To convert values from date to int, you can use function UNIX_DATE instead.

-- `spark.sql.ansi.enabled=false`
SELECT CAST('a' AS INT);
+--------------+
|CAST(a AS INT)|
+--------------+
|          null|
+--------------+

SELECT CAST(2147483648L AS INT);
+-----------------------+
|CAST(2147483648 AS INT)|
+-----------------------+
|            -2147483648|
+-----------------------+

SELECT CAST(DATE'2020-01-01' AS INT)
+------------------------------+
|CAST(DATE '2020-01-01' AS INT)|
+------------------------------+
|                          null|
+------------------------------+

-- Examples of store assignment rules
CREATE TABLE t (v INT);

-- `spark.sql.storeAssignmentPolicy=ANSI`
INSERT INTO t VALUES ('1');
org.apache.spark.sql.AnalysisException: [INCOMPATIBLE_DATA_FOR_TABLE.CANNOT_SAFELY_CAST] Cannot write incompatible data for table `spark_catalog`.`default`.`t`: Cannot safely cast `v`: "STRING" to "INT".

-- `spark.sql.storeAssignmentPolicy=LEGACY` (This is a legacy behaviour until Spark 2.x)
INSERT INTO t VALUES ('1');
SELECT * FROM t;
+---+
|  v|
+---+
|  1|
+---+

캐스트에서의 반올림 (Rounding in cast)

소수부(fraction)가 있는 decimal을 INTERVAL HOUR TO SECOND처럼 SECOND를 끝 단위(end-unit)로 하는 interval 타입으로 캐스팅할 때, Spark는 두 이웃 값이 등거리인 경우를 제외하고는 "가장 가까운 이웃(nearest neighbor)" 쪽으로 반올림해요. 등거리인 경우에는 올림(round up)해요.

Store assignment

앞서 언급했듯이, spark.sql.storeAssignmentPolicyANSI(기본값)로 설정되면 Spark SQL은 테이블 삽입 시 ANSI store assignment 규칙을 준수해요. 테이블 삽입에서 소스와 대상 데이터 타입의 유효한 조합은 다음 표로 주어져요.

Source\Target Numeric String Date Time Timestamp Timestamp_NTZ Interval Boolean Binary Array Map Struct
Numeric Y Y N N N N N N N N N N
String N Y N Y N N N N N N N N
Date N Y Y N Y Y N N N N N N
Time N Y N Y N N N N N N N N
Timestamp N Y Y N Y Y N N N N N N
Timestamp_NTZ N Y Y N Y Y N N N N N N
Interval N Y N N N N N* N N N N N
Boolean N Y N N N N N Y N N N N
Binary N Y N N N N N N Y N N N
Array N N N N N N N N N Y** N N
Map N N N N N N N N N N Y** N
Struct N N N N N N N N N N N Y**

* Spark는 interval 타입 테이블 컬럼을 지원하지 않아요.

** Array/Map/Struct 타입의 경우 데이터 타입 검사 규칙이 그 구성 요소(element)에도 재귀적으로 적용돼요.

테이블 삽입 중에 숫자 값 오버플로가 발생하면 Spark는 예외를 던져요.

CREATE TABLE test(i INT);

INSERT INTO test VALUES (2147483648L);
org.apache.spark.SparkArithmeticException: [CAST_OVERFLOW_IN_TABLE_INSERT] Fail to insert a value of "BIGINT" type into the "INT" type column `i` due to an overflow. Use `try_cast` on the input value to tolerate overflow and return NULL instead.

타입 강제 변환 (Type coercion)

타입 승격과 우선순위 (Type Promotion and Precedence)

spark.sql.ansi.enabledtrue로 설정되면, Spark SQL은 데이터 타입 간의 충돌을 해결하는 방법을 지배하는 여러 규칙을 사용해요. 이 충돌 해결의 핵심은 타입 우선순위 목록(Type Precedence List)인데, 주어진 데이터 타입의 값이 다른 데이터 타입으로 암시적으로 승격될 수 있는지를 정의해요.

데이터 타입 (Data type) 우선순위 목록 (precedence list, 가장 좁은 것부터 가장 넓은 것까지)
Byte Byte -> Short -> Int -> Long -> Decimal -> Float* -> Double
Short Short -> Int -> Long -> Decimal-> Float* -> Double
Int Int -> Long -> Decimal -> Float* -> Double
Long Long -> Decimal -> Float* -> Double
Decimal Decimal -> Float* -> Double
Float Float -> Double
Double Double
Date Date -> Timestamp_NTZ -> Timestamp
Time Time
Timestamp Timestamp
String String, Long -> Double, Date -> Timestamp_NTZ -> Timestamp , Boolean, Binary **
Binary Binary
Boolean Boolean
Interval Interval
Map Map***
Array Array***
Struct Struct***

* 최소 공통 타입(least common type) 해석에서 정밀도 손실을 피하기 위해 float는 건너뛰어요.

** String은 여러 종류의 데이터 타입으로 승격될 수 있어요. Byte/Short/Int/Decimal/Float는 이 우선순위 목록에 없다는 점에 주의하세요. Byte/Short/Int와 String 사이의 최소 공통 타입은 Long이고, Decimal/Float와 String 사이의 최소 공통 타입은 Double이에요.

*** 복합 타입(complex type)의 경우 우선순위 규칙이 그 구성 요소(element)에 재귀적으로 적용돼요.

타입이 없는(untyped) NULL에는 특별한 규칙이 적용돼요. NULL은 다른 어떤 타입으로도 승격될 수 있어요.

이것은 우선순위 목록을 방향 트리로 그린 도표예요: Type Precedence List

최소 공통 타입 해석 (Least Common Type Resolution)

타입 집합에서의 최소 공통 타입(least common type)은 그 타입 집합의 모든 요소가 우선순위 목록을 통해 도달할 수 있는 가장 좁은 타입이에요.

최소 공통 타입 해석은 다음에 사용돼요:

  • coalesce, least, greatest처럼 여러 매개변수가 공통 인자 타입을 기대하는 함수의 인자 타입을 도출하는 데 사용돼요.
  • 산술 연산이나 비교 같은 연산자의 피연산자(operand) 타입을 도출하는 데 사용돼요.
  • case 표현식 같은 표현식의 결과 타입을 도출하는 데 사용돼요.
  • array와 map 생성자(constructor)의 element, key, value 타입을 도출하는 데 사용돼요. 최소 공통 타입이 FLOAT로 해석되면 특별한 규칙이 적용돼요. float 타입 값에서, 타입 중 하나라도 INT, BIGINT, DECIMAL이면 자릿수 손실을 피하기 위해 최소 공통 타입이 DOUBLE로 올려져요(pushed).

Decimal 타입은 여기서 좀 더 복잡한데, 단순한 타입이 아니라 precision과 scale이라는 매개변수를 가지기 때문이에요. decimal(precision, scale)은 값이 정수부(integral part)에 최대 precision - scale 자리, 소수부(fractional part)에 scale 자리를 가질 수 있음을 의미해요. decimal 타입들 사이의 최소 공통 타입은 모든 값을 표현하기 위해 정수부와 소수부 양쪽에 충분한 자릿수를 가져야 해요. 더 정확히는 decimal(p1, s1)decimal(p2, s2) 사이의 최소 공통 타입은 max(s1, s2)의 scale과 max(s1, s2) + max(p1 - s1, p2 - s2)의 precision을 가져요. 하지만 Spark의 decimal 타입은 최대 precision이 38이에요. 최종 decimal 타입이 더 많은 precision을 필요로 하면 잘림(truncation)을 해야 해요. 정수부의 자릿수가 더 중요하므로 Spark는 소수부의 자릿수를 먼저 잘라요. 예를 들어 decimal(48, 20)decimal(38, 10)으로 줄어들어요.

참고로 산술 연산은 decimal 입력에 대한 최소 공통 타입을 계산하는 특별한 규칙이 있어요:

연산 (Operation) 결과 precision (Result precision) 결과 scale (Result scale)
e1 + e2 max(s1, s2) + max(p1 - s1, p2 - s2) + 1 max(s1, s2)
e1 - e2 max(s1, s2) + max(p1 - s1, p2 - s2) + 1 max(s1, s2)
e1 * e2 p1 + p2 + 1 s1 + s2
e1 / e2 p1 - s1 + s2 + max(6, s1 + p2 + 1) max(6, s1 + p2 + 1)
e1 % e2 min(p1 - s1, p2 - s2) + max(s1, s2) max(s1, s2)

잘림 규칙도 산술 연산에서는 다르게 적용돼요. 소수부에 최소 6자리를 유지하는데, 이는 scale을 6까지만 줄일 수 있음을 의미해요. 이 경우 오버플로가 발생할 수 있어요.

-- The coalesce function accepts any set of argument types as long as they share a least common type.
-- The result type is the least common type of the arguments.
> SET spark.sql.ansi.enabled=true;
> SELECT typeof(coalesce(1Y, 1L, NULL));
BIGINT
> SELECT typeof(coalesce(1, DATE'2020-01-01'));
Error: Incompatible types [INT, DATE]

> SELECT typeof(coalesce(ARRAY(1Y), ARRAY(1L)));
ARRAY<BIGINT>
> SELECT typeof(coalesce(1, 1F));
DOUBLE
> SELECT typeof(coalesce(1L, 1F));
DOUBLE
> SELECT (typeof(coalesce(1BD, 1F)));
DOUBLE

> SELECT typeof(coalesce(1, '2147483648'))
BIGINT
> SELECT typeof(coalesce(1.0, '2147483648'))
DOUBLE
> SELECT typeof(coalesce(DATE'2021-01-01', '2022-01-01'))
DATE

SQL 함수 (SQL Functions)

함수 호출 (Function invocation)

ANSI 모드(spark.sql.ansi.enabled=true)에서 Spark SQL의 함수 호출은:

  • 일반적으로, 입력 값을 SQL 함수의 선언된 매개변수 타입으로 저장하는 것처럼 Store assignment 규칙을 따라요.
  • 타입이 없는 NULL에는 특별한 규칙이 적용돼요. NULL은 다른 어떤 타입으로도 승격될 수 있어요.
> SET spark.sql.ansi.enabled=true;
-- implicitly cast Int to String type
> SELECT concat('total number: ', 1);
total number: 1
-- implicitly cast Timestamp to Date type
> select datediff(now(), current_date);
0

-- implicitly cast String to Double type
> SELECT ceil('0.1');
1
-- special rule: implicitly cast NULL to Date type
> SELECT year(null);
NULL

> CREATE TABLE t(s string);
-- Can't store String column as Numeric types.
> SELECT ceil(s) from t;
Error in query: cannot resolve 'CEIL(spark_catalog.default.t.s)' due to data type mismatch
-- Can't store String column as Date type.
> select year(s) from t;
Error in query: cannot resolve 'year(spark_catalog.default.t.s)' due to data type mismatch

동작이 다른 함수들 (Functions with different behaviors)

일부 SQL 함수의 동작은 ANSI 모드(spark.sql.ansi.enabled=true)에서 달라질 수 있어요.

  • size: 이 함수는 null 입력에 대해 null을 반환해요.
  • element_at:
    • 이 함수는 유효하지 않은 인덱스를 사용하면 ArrayIndexOutOfBoundsException을 던져요.
  • elt: 이 함수는 유효하지 않은 인덱스를 사용하면 ArrayIndexOutOfBoundsException을 던져요.
  • parse_url: 이 함수는 입력 문자열이 유효한 url이 아니면 IllegalArgumentException을 던져요.
  • to_date: 이 함수는 입력 문자열을 파싱할 수 없거나 패턴 문자열이 유효하지 않으면 예외와 함께 실패해야 해요.
  • to_timestamp: 이 함수는 입력 문자열을 파싱할 수 없거나 패턴 문자열이 유효하지 않으면 예외와 함께 실패해야 해요.
  • unix_timestamp: 이 함수는 입력 문자열을 파싱할 수 없거나 패턴 문자열이 유효하지 않으면 예외와 함께 실패해야 해요.
  • to_unix_timestamp: 이 함수는 입력 문자열을 파싱할 수 없거나 패턴 문자열이 유효하지 않으면 예외와 함께 실패해야 해요.
  • make_date: 이 함수는 결과 날짜가 유효하지 않으면 예외와 함께 실패해야 해요.
  • make_timestamp: 이 함수는 결과 타임스탬프가 유효하지 않으면 예외와 함께 실패해야 해요.
  • make_interval: 이 함수는 결과 interval이 유효하지 않으면 예외와 함께 실패해야 해요.
  • next_day: 이 함수는 입력이 유효한 요일(day of week)이 아니면 IllegalArgumentException을 던져요.

SQL 연산자 (SQL Operators)

일부 SQL 연산자의 동작은 ANSI 모드(spark.sql.ansi.enabled=true)에서 달라질 수 있어요.

  • array_col[index]: 이 연산자는 유효하지 않은 인덱스를 사용하면 ArrayIndexOutOfBoundsException을 던져요.

ANSI 모드에 유용한 함수 (Useful Functions for ANSI Mode)

ANSI 모드가 켜져 있으면 잘못된 연산에 대해 예외가 발생해요. 다음과 같은 SQL 함수를 사용해 그러한 예외를 억제할 수 있어요.

  • try_cast: CAST와 동일하지만, 런타임 오류 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_add: 더하기 연산자 +와 동일하지만, 정수 값 오버플로 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_subtract: 빼기 연산자 -와 동일하지만, 정수 값 오버플로 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_multiply: 곱하기 연산자 *와 동일하지만, 정수 값 오버플로 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_divide: 나누기 연산자 /와 동일하지만, 0으로 나눌 때 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_mod: 나머지 연산자 %와 동일하지만, 0으로 나눌 때 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_sum: sum 함수와 동일하지만, 정수/decimal/interval 값 오버플로 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_avg: avg 함수와 동일하지만, decimal/interval 값 오버플로 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_element_at: element_at 함수와 동일하지만, 배열 인덱스가 범위를 벗어날 때 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_to_timestamp: to_timestamp 함수와 동일하지만, 문자열 파싱 오류 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_parse_url: parse_url 함수와 동일하지만, url 파싱 오류 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_make_timestamp: make_timestamp 함수와 동일하지만, 오류 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_make_timestamp_ltz: make_timestamp_ltz 함수와 동일하지만, 오류 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_make_timestamp_ntz: make_timestamp_ntz 함수와 동일하지만, 오류 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_make_interval: make_interval 함수와 동일하지만, 유효하지 않은 interval 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_to_time: to_time 함수와 동일하지만, 문자열 파싱 오류 시 예외를 던지는 대신 NULL 결과를 반환해요.
  • try_to_date: to_date 함수와 동일하지만, 문자열 파싱 오류 시 예외를 던지는 대신 NULL 결과를 반환해요.

SQL 키워드 (SQL Keywords, 선택 사항, 기본적으로 비활성화)

spark.sql.ansi.enabledspark.sql.ansi.enforceReservedKeywords가 둘 다 true일 때 Spark SQL은 ANSI 모드 파서(parser)를 사용해요.

ANSI 모드 파서에서 Spark SQL은 두 종류의 키워드를 가져요:

  • 비예약 키워드(Non-reserved keywords): 특정 문맥에서만 특별한 의미를 갖고, 다른 문맥에서는 식별자로 사용될 수 있는 키워드예요. 예를 들어 EXPLAIN SELECT ...는 명령이지만, EXPLAIN은 다른 곳에서는 식별자로 사용될 수 있어요.
  • 예약 키워드(Reserved keywords): 예약되어 있어서 테이블, 뷰, 컬럼, 함수, 별칭(alias) 등의 식별자로 사용할 수 없는 키워드예요.

기본 파서로는 Spark SQL은 두 종류의 키워드를 가져요:

  • 비예약 키워드(Non-reserved keywords): ANSI 모드가 활성화됐을 때와 같은 정의예요.
  • 엄격한 비예약 키워드(Strict-non-reserved keywords): 비예약 키워드의 엄격한 버전으로, 테이블 별칭으로 사용할 수 없어요.

기본적으로 spark.sql.ansi.enforceReservedKeywords는 false예요.

아래는 Spark SQL의 모든 키워드 목록이에요.

키워드 (Keyword) Spark SQL ANSI 모드 Spark SQL 비ANSI 모드 SQL-2016
ADD non-reserved non-reserved non-reserved
AFTER non-reserved non-reserved non-reserved
AGGREGATE non-reserved non-reserved non-reserved
ALL reserved non-reserved reserved
ALTER non-reserved non-reserved reserved
ALWAYS non-reserved non-reserved non-reserved
ANALYZE non-reserved non-reserved non-reserved
AND reserved non-reserved reserved
ANTI non-reserved strict-non-reserved non-reserved
ANY reserved non-reserved reserved
ANY_VALUE non-reserved non-reserved non-reserved
APPROX non-reserved non-reserved non-reserved
ARCHIVE non-reserved non-reserved non-reserved
ARRAY non-reserved non-reserved reserved
AS reserved non-reserved reserved
ASC non-reserved non-reserved non-reserved
ASENSITIVE non-reserved non-reserved non-reserved
AT non-reserved non-reserved reserved
ATOMIC non-reserved non-reserved non-reserved
AUTHORIZATION reserved non-reserved reserved
BEGIN non-reserved non-reserved non-reserved
BERNOULLI non-reserved non-reserved non-reserved
BETWEEN non-reserved non-reserved reserved
BIGINT non-reserved non-reserved reserved
BINARY non-reserved non-reserved reserved
BINDING non-reserved non-reserved non-reserved
BOOLEAN non-reserved non-reserved reserved
BOTH reserved non-reserved reserved
BUCKET non-reserved non-reserved non-reserved
BUCKETS non-reserved non-reserved non-reserved
BY non-reserved non-reserved reserved
BYTE non-reserved non-reserved non-reserved
CACHE non-reserved non-reserved non-reserved
CALL reserved non-reserved reserved
CALLED non-reserved non-reserved non-reserved
CASCADE non-reserved non-reserved non-reserved
CASE reserved non-reserved reserved
CAST reserved non-reserved reserved
CATALOG non-reserved non-reserved non-reserved
CATALOGS non-reserved non-reserved non-reserved
CHANGE non-reserved non-reserved non-reserved
CHANGES non-reserved non-reserved non-reserved
CHAR non-reserved non-reserved reserved
CHARACTER non-reserved non-reserved reserved
CHECK reserved non-reserved reserved
CLEAR non-reserved non-reserved non-reserved
CLOSE non-reserved non-reserved non-reserved
CLUSTER non-reserved non-reserved non-reserved
CLUSTERED non-reserved non-reserved non-reserved
CODEGEN non-reserved non-reserved non-reserved
COLLATE reserved non-reserved reserved
COLLATION reserved non-reserved reserved
COLLATIONS reserved non-reserved non-reserved
COLLECTION non-reserved non-reserved non-reserved
COLUMN reserved non-reserved reserved
COLUMNS non-reserved non-reserved non-reserved
COMMENT non-reserved non-reserved non-reserved
COMMIT non-reserved non-reserved reserved
COMPACT non-reserved non-reserved non-reserved
COMPACTIONS non-reserved non-reserved non-reserved
COMPENSATION non-reserved non-reserved non-reserved
COMPUTE non-reserved non-reserved non-reserved
CONCATENATE non-reserved non-reserved non-reserved
CONDITION non-reserved non-reserved non-reserved
CONSTRAINT reserved non-reserved reserved
CONTAINS non-reserved non-reserved non-reserved
CONTINUE non-reserved non-reserved non-reserved
COST non-reserved non-reserved non-reserved
CREATE reserved non-reserved reserved
CROSS reserved strict-non-reserved reserved
CUBE non-reserved non-reserved reserved
CURRENT non-reserved non-reserved reserved
CURRENT_DATABASE non-reserved non-reserved non-reserved
CURRENT_DATE reserved non-reserved reserved
CURRENT_PATH reserved non-reserved reserved
CURRENT_SCHEMA reserved non-reserved reserved
CURRENT_TIME reserved non-reserved reserved
CURRENT_TIMESTAMP reserved non-reserved reserved
CURRENT_USER reserved non-reserved reserved
CURSOR non-reserved non-reserved non-reserved
DATA non-reserved non-reserved non-reserved
DATE non-reserved non-reserved reserved
DATABASE non-reserved non-reserved non-reserved
DATABASES non-reserved non-reserved non-reserved
DATEADD non-reserved non-reserved non-reserved
DATE_ADD non-reserved non-reserved non-reserved
DATEDIFF non-reserved non-reserved non-reserved
DATE_DIFF non-reserved non-reserved non-reserved
DAY non-reserved non-reserved non-reserved
DAYS non-reserved non-reserved non-reserved
DAYOFYEAR non-reserved non-reserved non-reserved
DBPROPERTIES non-reserved non-reserved non-reserved
DEC non-reserved non-reserved reserved
DECIMAL non-reserved non-reserved reserved
DECLARE non-reserved non-reserved non-reserved
DEFAULT non-reserved non-reserved non-reserved
DEFINED non-reserved non-reserved non-reserved
DEFINER non-reserved non-reserved non-reserved
DEFAULT_PATH non-reserved non-reserved not a keyword
DELAY non-reserved non-reserved non-reserved
DELETE non-reserved non-reserved reserved
DELIMITED non-reserved non-reserved non-reserved
DESC non-reserved non-reserved non-reserved
DESCRIBE non-reserved non-reserved reserved
DETERMINISTIC non-reserved non-reserved reserved
DFS non-reserved non-reserved non-reserved
DIRECTORIES non-reserved non-reserved non-reserved
DIRECTORY non-reserved non-reserved non-reserved
DISTANCE non-reserved non-reserved non-reserved
DISTINCT reserved non-reserved reserved
DISTRIBUTE non-reserved non-reserved non-reserved
DIV non-reserved non-reserved not a keyword
DO non-reserved non-reserved non-reserved
DOUBLE non-reserved non-reserved reserved
DROP non-reserved non-reserved reserved
ELSE reserved non-reserved reserved
ELSEIF non-reserved non-reserved non-reserved
END reserved non-reserved reserved
ENFORCED non-reserved non-reserved non-reserved
ESCAPE reserved non-reserved reserved
ESCAPED non-reserved non-reserved non-reserved
EVOLUTION non-reserved non-reserved non-reserved
EXACT non-reserved non-reserved non-reserved
EXCEPT reserved strict-non-reserved reserved
EXCHANGE non-reserved non-reserved non-reserved
EXCLUDE non-reserved non-reserved non-reserved
EXCLUSIVE non-reserved non-reserved non-reserved
EXECUTE reserved non-reserved reserved
EXISTS non-reserved non-reserved reserved
EXIT non-reserved non-reserved non-reserved
EXPLAIN non-reserved non-reserved non-reserved
EXPORT non-reserved non-reserved non-reserved
EXTEND non-reserved non-reserved non-reserved
EXTENDED non-reserved non-reserved non-reserved
EXTERNAL non-reserved non-reserved reserved
EXTRACT non-reserved non-reserved reserved
FALSE reserved non-reserved reserved
FETCH reserved non-reserved reserved
FIELDS non-reserved non-reserved non-reserved
FILTER reserved non-reserved reserved
FILEFORMAT non-reserved non-reserved non-reserved
FIRST non-reserved non-reserved non-reserved
FLOAT non-reserved non-reserved reserved
FLOW non-reserved non-reserved non-reserved
FOLLOWING non-reserved non-reserved non-reserved
FOR reserved non-reserved reserved
FOREIGN reserved non-reserved reserved
FORMAT non-reserved non-reserved non-reserved
FORMATTED non-reserved non-reserved non-reserved
FOUND non-reserved non-reserved non-reserved
FROM reserved non-reserved reserved
FULL reserved strict-non-reserved reserved
FUNCTION non-reserved non-reserved reserved
FUNCTIONS non-reserved non-reserved non-reserved
GENERATED non-reserved non-reserved non-reserved
GEOGRAPHY non-reserved non-reserved non-reserved
GEOMETRY non-reserved non-reserved non-reserved
GLOBAL non-reserved non-reserved reserved
GRANT reserved non-reserved reserved
GROUP reserved non-reserved reserved
GROUPING non-reserved non-reserved reserved
HANDLER non-reserved non-reserved non-reserved
HAVING reserved non-reserved reserved
HOUR non-reserved non-reserved non-reserved
HOURS non-reserved non-reserved non-reserved
IDENTIFIER non-reserved non-reserved non-reserved
IDENTIFIED non-reserved non-reserved non-reserved
IDENTITY non-reserved non-reserved non-reserved
IF non-reserved non-reserved not a keyword
IGNORE non-reserved non-reserved non-reserved
IMMEDIATE non-reserved non-reserved non-reserved
IMPORT non-reserved non-reserved non-reserved
IN reserved non-reserved reserved
INCLUDE non-reserved non-reserved non-reserved
INCLUSIVE non-reserved non-reserved non-reserved
INCREMENT non-reserved non-reserved non-reserved
INDEX non-reserved non-reserved non-reserved
INDEXES non-reserved non-reserved non-reserved
INNER reserved strict-non-reserved reserved
INPATH non-reserved non-reserved non-reserved
INPUT non-reserved non-reserved non-reserved
INPUTFORMAT non-reserved non-reserved non-reserved
INSERT non-reserved non-reserved reserved
INSENSITIVE non-reserved non-reserved non-reserved
INT non-reserved non-reserved reserved
INTEGER non-reserved non-reserved reserved
INTERSECT reserved strict-non-reserved reserved
INTERVAL non-reserved non-reserved reserved
INTO reserved non-reserved reserved
INVOKER non-reserved non-reserved non-reserved
IS reserved non-reserved reserved
ITEMS non-reserved non-reserved non-reserved
ITERATE non-reserved non-reserved non-reserved
JOIN reserved strict-non-reserved reserved
JSON non-reserved non-reserved non-reserved
KEY non-reserved non-reserved non-reserved
KEYS non-reserved non-reserved non-reserved
LANGUAGE non-reserved non-reserved reserved
LAST non-reserved non-reserved non-reserved
LATERAL reserved strict-non-reserved reserved
LAZY non-reserved non-reserved non-reserved
LEADING reserved non-reserved reserved
LEAVE non-reserved non-reserved non-reserved
LEFT reserved strict-non-reserved reserved
LEVEL non-reserved non-reserved non-reserved
LIKE non-reserved non-reserved reserved
ILIKE non-reserved non-reserved non-reserved
LIMIT non-reserved non-reserved non-reserved
LINES non-reserved non-reserved non-reserved
LIST non-reserved non-reserved non-reserved
LOAD non-reserved non-reserved non-reserved
LOCAL non-reserved non-reserved reserved
LOCATION non-reserved non-reserved non-reserved
LOCK non-reserved non-reserved non-reserved
LOCKS non-reserved non-reserved non-reserved
LOGICAL non-reserved non-reserved non-reserved
LONG non-reserved non-reserved non-reserved
LOOP non-reserved non-reserved non-reserved
MACRO non-reserved non-reserved non-reserved
MAP non-reserved non-reserved non-reserved
MATCHED non-reserved non-reserved non-reserved
MATERIALIZED non-reserved non-reserved non-reserved
MAX non-reserved non-reserved non-reserved
MEASURE non-reserved non-reserved non-reserved
MERGE non-reserved non-reserved non-reserved
METRICS non-reserved non-reserved non-reserved
MICROSECOND non-reserved non-reserved non-reserved
MICROSECONDS non-reserved non-reserved non-reserved
MILLISECOND non-reserved non-reserved non-reserved
MILLISECONDS non-reserved non-reserved non-reserved
MINUTE non-reserved non-reserved non-reserved
MINUTES non-reserved non-reserved non-reserved
MINUS non-reserved strict-non-reserved non-reserved
MODIFIES non-reserved non-reserved non-reserved
MONTH non-reserved non-reserved non-reserved
MONTHS non-reserved non-reserved non-reserved
MSCK non-reserved non-reserved non-reserved
NAME non-reserved non-reserved non-reserved
NAMESPACE non-reserved non-reserved non-reserved
NAMESPACES non-reserved non-reserved non-reserved
NANOSECOND non-reserved non-reserved non-reserved
NANOSECONDS non-reserved non-reserved non-reserved
NATURAL reserved strict-non-reserved reserved
NEAREST non-reserved non-reserved non-reserved
NEXT non-reserved non-reserved non-reserved
NO non-reserved non-reserved reserved
NONE non-reserved non-reserved reserved
NORELY non-reserved non-reserved not a keyword
NOT reserved non-reserved reserved
NULL reserved non-reserved reserved
NULLS non-reserved non-reserved non-reserved
NUMERIC non-reserved non-reserved non-reserved
OF non-reserved non-reserved reserved
OFFSET reserved non-reserved reserved
ON reserved strict-non-reserved reserved
ONLY reserved non-reserved reserved
OPEN non-reserved non-reserved reserved
OPTION non-reserved non-reserved non-reserved
OPTIONS non-reserved non-reserved non-reserved
OR reserved non-reserved reserved
ORDER reserved non-reserved reserved
OUT non-reserved non-reserved reserved
OUTER reserved non-reserved reserved
OUTPUTFORMAT non-reserved non-reserved non-reserved
OVER non-reserved non-reserved non-reserved
OVERLAPS reserved non-reserved reserved
OVERLAY non-reserved non-reserved non-reserved
OVERWRITE non-reserved non-reserved non-reserved
PARTITION non-reserved non-reserved reserved
PARTITIONED non-reserved non-reserved non-reserved
PARTITIONS non-reserved non-reserved non-reserved
PATH non-reserved non-reserved not a keyword
PERCENT non-reserved non-reserved non-reserved
PIVOT non-reserved non-reserved non-reserved
PLACING non-reserved non-reserved non-reserved
POSITION non-reserved non-reserved reserved
PRECEDING non-reserved non-reserved non-reserved
PRIMARY reserved non-reserved reserved
PRINCIPALS non-reserved non-reserved non-reserved
PROCEDURE non-reserved non-reserved non-reserved
PROCEDURES non-reserved non-reserved non-reserved
PROPERTIES non-reserved non-reserved non-reserved
PURGE non-reserved non-reserved non-reserved
QUALIFY non-reserved non-reserved non-reserved
QUARTER non-reserved non-reserved non-reserved
QUERY non-reserved non-reserved non-reserved
RANGE non-reserved non-reserved reserved
READ non-reserved non-reserved non-reserved
READS non-reserved non-reserved non-reserved
REAL non-reserved non-reserved reserved
RECORDREADER non-reserved non-reserved non-reserved
RECORDWRITER non-reserved non-reserved non-reserved
RECOVER non-reserved non-reserved non-reserved
RECURSION non-reserved non-reserved non-reserved
RECURSIVE reserved non-reserved reserved
REDUCE non-reserved non-reserved non-reserved
REFERENCES reserved non-reserved reserved
REFRESH non-reserved non-reserved non-reserved
REGEXP non-reserved non-reserved not a keyword
RELY non-reserved non-reserved not a keyword
RENAME non-reserved non-reserved non-reserved
REPAIR non-reserved non-reserved non-reserved
REPEAT non-reserved non-reserved non-reserved
REPEATABLE non-reserved non-reserved non-reserved
REPLACE non-reserved non-reserved non-reserved
RESET non-reserved non-reserved non-reserved
RESPECT non-reserved non-reserved non-reserved
RESTRICT non-reserved non-reserved non-reserved
RETURN non-reserved non-reserved reserved
RETURNS non-reserved non-reserved reserved
REVOKE non-reserved non-reserved reserved
RIGHT reserved strict-non-reserved reserved
RLIKE non-reserved non-reserved non-reserved
ROLE non-reserved non-reserved non-reserved
ROLES non-reserved non-reserved non-reserved
ROLLBACK non-reserved non-reserved reserved
ROLLUP non-reserved non-reserved reserved
ROW non-reserved non-reserved reserved
ROWS non-reserved non-reserved reserved
SCHEMA non-reserved non-reserved non-reserved
SCHEMAS non-reserved non-reserved non-reserved
SECOND non-reserved non-reserved non-reserved
SECONDS non-reserved non-reserved non-reserved
SECURITY non-reserved non-reserved non-reserved
SELECT reserved non-reserved reserved
SEMI non-reserved strict-non-reserved non-reserved
SEPARATED non-reserved non-reserved non-reserved
SERDE non-reserved non-reserved non-reserved
SERDEPROPERTIES non-reserved non-reserved non-reserved
SESSION_USER reserved non-reserved reserved
SET non-reserved non-reserved reserved
SETS non-reserved non-reserved non-reserved
SHORT non-reserved non-reserved non-reserved
SHOW non-reserved non-reserved non-reserved
SIMILARITY non-reserved non-reserved non-reserved
SINGLE non-reserved non-reserved non-reserved
SKEWED non-reserved non-reserved non-reserved
SMALLINT non-reserved non-reserved reserved
SOME reserved non-reserved reserved
SORT non-reserved non-reserved non-reserved
SORTED non-reserved non-reserved non-reserved
SOURCE non-reserved non-reserved non-reserved
SPECIFIC non-reserved non-reserved reserved
SQL reserved non-reserved reserved
SQLEXCEPTION non-reserved non-reserved non-reserved
SQLSTATE non-reserved non-reserved non-reserved
START non-reserved non-reserved reserved
STATISTICS non-reserved non-reserved non-reserved
STORED non-reserved non-reserved non-reserved
STRATIFY non-reserved non-reserved non-reserved
STREAM non-reserved non-reserved non-reserved
STREAMING non-reserved non-reserved non-reserved
STRING non-reserved non-reserved non-reserved
STRUCT non-reserved non-reserved non-reserved
SUBSTR non-reserved non-reserved non-reserved
SUBSTRING non-reserved non-reserved non-reserved
SYNC non-reserved non-reserved non-reserved
SYSTEM non-reserved non-reserved reserved
SYSTEM_PATH non-reserved non-reserved not a keyword
SYSTEM_TIME non-reserved non-reserved non-reserved
SYSTEM_VERSION non-reserved non-reserved non-reserved
TABLE reserved non-reserved reserved
TABLES non-reserved non-reserved non-reserved
TABLESAMPLE non-reserved non-reserved reserved
TARGET non-reserved non-reserved non-reserved
TBLPROPERTIES non-reserved non-reserved non-reserved
TEMP non-reserved non-reserved not a keyword
TEMPORARY non-reserved non-reserved non-reserved
TERMINATED non-reserved non-reserved non-reserved
THEN reserved non-reserved reserved
TIME reserved non-reserved reserved
TIMEDIFF non-reserved non-reserved non-reserved
TIMESTAMP non-reserved non-reserved non-reserved
TIMESTAMP_LTZ non-reserved non-reserved non-reserved
TIMESTAMP_NTZ non-reserved non-reserved non-reserved
TIMESTAMPADD non-reserved non-reserved non-reserved
TIMESTAMPDIFF non-reserved non-reserved non-reserved
TINYINT non-reserved non-reserved non-reserved
TO reserved non-reserved reserved
TOUCH non-reserved non-reserved non-reserved
TRAILING reserved non-reserved reserved
TRANSACTION non-reserved non-reserved non-reserved
TRANSACTIONS non-reserved non-reserved non-reserved
TRANSFORM non-reserved non-reserved non-reserved
TRIM non-reserved non-reserved non-reserved
TRUE non-reserved non-reserved reserved
TRUNCATE non-reserved non-reserved reserved
TRY_CAST non-reserved non-reserved non-reserved
TYPE non-reserved non-reserved non-reserved
UNARCHIVE non-reserved non-reserved non-reserved
UNBOUNDED non-reserved non-reserved non-reserved
UNCACHE non-reserved non-reserved non-reserved
UNION reserved strict-non-reserved reserved
UNIQUE reserved non-reserved reserved
UNKNOWN reserved non-reserved reserved
UNLOCK non-reserved non-reserved non-reserved
UNPIVOT non-reserved non-reserved non-reserved
UNSET non-reserved non-reserved non-reserved
UNTIL non-reserved non-reserved non-reserved
UPDATE non-reserved non-reserved reserved
USE non-reserved non-reserved non-reserved
USER reserved non-reserved reserved
USING reserved strict-non-reserved reserved
VALUE non-reserved non-reserved non-reserved
VALUES non-reserved non-reserved reserved
VARCHAR non-reserved non-reserved reserved
VAR non-reserved non-reserved non-reserved
VARIABLE non-reserved non-reserved non-reserved
VARIANT non-reserved non-reserved reserved
VERSION non-reserved non-reserved non-reserved
VIEW non-reserved non-reserved non-reserved
VIEWS non-reserved non-reserved non-reserved
VOID non-reserved non-reserved non-reserved
WATERMARK non-reserved non-reserved non-reserved
WEEK non-reserved non-reserved non-reserved
WEEKS non-reserved non-reserved non-reserved
WHEN reserved non-reserved reserved
WHERE reserved non-reserved reserved
WHILE non-reserved non-reserved non-reserved
WINDOW non-reserved non-reserved reserved
WITH reserved non-reserved reserved
WITHIN reserved non-reserved reserved
WITHOUT non-reserved non-reserved non-reserved
X non-reserved non-reserved non-reserved
YEAR non-reserved non-reserved non-reserved
YEARS non-reserved non-reserved non-reserved
ZONE non-reserved non-reserved non-reserved

더 알아보기 (Learn more)