Variant 데이터 타입
Variant 데이터 타입
서로 다른 데이터 타입들의 합집합(union)을 나타내는 타입이에요. Variant(T1, T2, ..., TN) 타입은 각 행이 T1, T2, …, TN 중 하나 또는 그 중 아무것도 아닌 값(NULL)을 가짐을 뜻해요. 중첩 타입의 순서는 중요하지 않아요.
출처: 문서
본문
이 타입은 다른 데이터 타입들의 합집합을 나타내요. 타입 Variant(T1, T2, ..., TN)은 이 타입의 각 행이 T1 또는 T2 또는 … 또는 TN 또는 그 중 어느 것도 아닌 값(NULL 값)을 가짐을 의미해요. 중첩 타입의 순서는 중요하지 않아요: Variant(T1, T2) = Variant(T2, T1). 중첩 타입은 Nullable(…), LowCardinality(Nullable(…)), Variant(…) 타입을 제외한 임의의 타입이 될 수 있어요. 유사한 타입을 variant로 사용하는 것(예: Variant(UInt32, Int64) 같은 서로 다른 숫자 타입이나 Variant(Date, DateTime) 같은 서로 다른 날짜 타입)은 권장되지 않아요. 그런 타입의 값으로 작업하면 모호함이 생길 수 있기 때문이에요. 기본적으로 그런 Variant 타입을 만들면 예외가 나지만, allow_suspicious_variant_types 설정으로 켤 수 있어요.
Variant 만들기 (Creating Variant)
테이블 컬럼 정의에 Variant 타입 사용하기:
CREATE TABLE test (v Variant(UInt64, String, Array(UInt64))) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('Hello, World!'), ([1, 2, 3]);
SELECT v FROM test;
┌─v─────────────┐
│ ᴺᵁᴸᴸ │
│ 42 │
│ Hello, World! │
│ [1,2,3] │
└───────────────┘
일반 컬럼에서 CAST 사용:
SELECT toTypeName(variant) AS type_name, 'Hello, World!'::Variant(UInt64, String, Array(UInt64)) as variant;
┌─type_name──────────────────────────────┬─variant───────┐
│ Variant(Array(UInt64), String, UInt64) │ Hello, World! │
└────────────────────────────────────────┴───────────────┘
인자가 공통 타입이 없을 때 함수 if/multiIf 사용(이를 위해 설정 use_variant_as_common_type을 켜야 해요):
SET use_variant_as_common_type = 1;
SELECT if(number % 2, number, range(number)) as variant FROM numbers(5);
┌─variant───┐
│ [] │
│ 1 │
│ [0,1] │
│ 3 │
│ [0,1,2,3] │
└───────────┘
SET use_variant_as_common_type = 1;
SELECT multiIf((number % 4) = 0, 42, (number % 4) = 1, [1, 2, 3], (number % 4) = 2, 'Hello, World!', NULL) AS variant FROM numbers(4);
┌─variant───────┐
│ 42 │
│ [1,2,3] │
│ Hello, World! │
│ ᴺᵁᴸᴸ │
└───────────────┘
배열 요소/map 값이 공통 타입이 없을 때 함수 'array/map' 사용(이를 위해 설정 use_variant_as_common_type을 켜야 해요):
SET use_variant_as_common_type = 1;
SELECT array(range(number), number, 'str_' || toString(number)) as array_of_variants FROM numbers(3);
┌─array_of_variants─┐
│ [[],0,'str_0'] │
│ [[0],1,'str_1'] │
│ [[0,1],2,'str_2'] │
└───────────────────┘
SET use_variant_as_common_type = 1;
SELECT map('a', range(number), 'b', number, 'c', 'str_' || toString(number)) as map_of_variants FROM numbers(3);
┌─map_of_variants───────────────┐
│ {'a':[],'b':0,'c':'str_0'} │
│ {'a':[0],'b':1,'c':'str_1'} │
│ {'a':[0,1],'b':2,'c':'str_2'} │
└───────────────────────────────┘
Variant 중첩 타입을 서브컬럼으로 읽기 (Reading Variant nested types as subcolumns)
Variant 타입은 타입 이름을 서브컬럼으로 사용해 Variant 컬럼에서 단일 중첩 타입을 읽는 것을 지원해요. 그래서 variant Variant(T1, T2, T3) 컬럼이 있다면 variant.T2 문법으로 타입 T2의 서브컬럼을 읽을 수 있어요. 이 서브컬럼은 T2가 Nullable 안에 들어갈 수 있으면 Nullable(T2), 그렇지 않으면 T2 타입이 돼요. 이 서브컬럼은 원래 Variant 컬럼과 같은 크기이며, 원래 Variant 컬럼이 타입 T2를 가지지 않는 모든 행에서 NULL 값(T2가 Nullable 안에 들어갈 수 없으면 빈 값)을 담아요. Variant 서브컬럼은 함수 variantElement(variant_column, type_name)으로도 읽을 수 있어요.
예시:
CREATE TABLE test (v Variant(UInt64, String, Array(UInt64))) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('Hello, World!'), ([1, 2, 3]);
SELECT v, v.String, v.UInt64, v.`Array(UInt64)` FROM test;
┌─v─────────────┬─v.String──────┬─v.UInt64─┬─v.Array(UInt64)─┐
│ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ [] │
│ 42 │ ᴺᵁᴸᴸ │ 42 │ [] │
│ Hello, World! │ Hello, World! │ ᴺᵁᴸᴸ │ [] │
│ [1,2,3] │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ [1,2,3] │
└───────────────┴───────────────┴──────────┴─────────────────┘
SELECT toTypeName(v.String), toTypeName(v.UInt64), toTypeName(v.`Array(UInt64)`) FROM test LIMIT 1;
┌─toTypeName(v.String)─┬─toTypeName(v.UInt64)─┬─toTypeName(v.Array(UInt64))─┐
│ Nullable(String) │ Nullable(UInt64) │ Array(UInt64) │
└──────────────────────┴──────────────────────┴─────────────────────────────┘
SELECT v, variantElement(v, 'String'), variantElement(v, 'UInt64'), variantElement(v, 'Array(UInt64)') FROM test;
┌─v─────────────┬─variantElement(v, 'String')─┬─variantElement(v, 'UInt64')─┬─variantElement(v, 'Array(UInt64)')─┐
│ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ [] │
│ 42 │ ᴺᵁᴸᴸ │ 42 │ [] │
│ Hello, World! │ Hello, World! │ ᴺᵁᴸᴸ │ [] │
│ [1,2,3] │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ [1,2,3] │
└───────────────┴─────────────────────────────┴─────────────────────────────┴────────────────────────────────────┘
각 행에 어떤 variant가 저장되어 있는지 알려면 함수 variantType(variant_column)을 사용할 수 있어요. 각 행에 variant 타입 이름의 Enum을 반환해요(행이 NULL이면 'None'). 예시:
CREATE TABLE test (v Variant(UInt64, String, Array(UInt64))) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('Hello, World!'), ([1, 2, 3]);
SELECT variantType(v) FROM test;
┌─variantType(v)─┐
│ None │
│ UInt64 │
│ String │
│ Array(UInt64) │
└────────────────┘
SELECT toTypeName(variantType(v)) FROM test LIMIT 1;
┌─toTypeName(variantType(v))──────────────────────────────────────────┐
│ Enum8('None' = -1, 'Array(UInt64)' = 0, 'String' = 1, 'UInt64' = 2) │
└─────────────────────────────────────────────────────────────────────┘
Variant 컬럼과 다른 컬럼 사이의 변환 (Conversion between a Variant column and other columns)
Variant 타입 컬럼으로 수행할 수 있는 변환은 4가지가 있어요.
String 컬럼을 Variant 컬럼으로 변환 (Converting a String column to a Variant column)
String에서 Variant로의 변환은 문자열 값에서 Variant 타입의 값을 파싱하여 수행돼요.
SELECT '42'::Variant(String, UInt64) AS variant, variantType(variant) AS variant_type
┌─variant─┬─variant_type─┐
│ 42 │ UInt64 │
└─────────┴──────────────┘
SELECT '[1, 2, 3]'::Variant(String, Array(UInt64)) as variant, variantType(variant) as variant_type
┌─variant─┬─variant_type──┐
│ [1,2,3] │ Array(UInt64) │
└─────────┴───────────────┘
SELECT CAST(map('key1', '42', 'key2', 'true', 'key3', '2020-01-01'), 'Map(String, Variant(UInt64, Bool, Date))') AS map_of_variants, mapApply((k, v) -> (k, variantType(v)), map_of_variants) AS map_of_variant_types
┌─map_of_variants─────────────────────────────┬─map_of_variant_types──────────────────────────┐
│ {'key1':42,'key2':true,'key3':'2020-01-01'} │ {'key1':'UInt64','key2':'Bool','key3':'Date'} │
└─────────────────────────────────────────────┴───────────────────────────────────────────────┘
String에서 Variant로의 변환 중 파싱을 비활성화하려면 설정 cast_string_to_dynamic_use_inference를 끌 수 있어요.
SET cast_string_to_variant_use_inference = 0;
SELECT '[1, 2, 3]'::Variant(String, Array(UInt64)) as variant, variantType(variant) as variant_type
┌─variant───┬─variant_type─┐
│ [1, 2, 3] │ String │
└───────────┴──────────────┘
일반 컬럼을 Variant 컬럼으로 변환 (Converting an ordinary column to a Variant column)
타입 T의 일반 컬럼을 이 타입을 담고 있는 Variant 컬럼으로 변환할 수 있어요.
SELECT toTypeName(variant) AS type_name, [1,2,3]::Array(UInt64)::Variant(UInt64, String, Array(UInt64)) as variant, variantType(variant) as variant_name
┌─type_name──────────────────────────────┬─variant─┬─variant_name──┐
│ Variant(Array(UInt64), String, UInt64) │ [1,2,3] │ Array(UInt64) │
└────────────────────────────────────────┴─────────┴───────────────┘
참고: String 타입에서의 변환은 항상 파싱을 통해 수행돼요. 파싱 없이 String 컬럼을 Variant의 String variant로 변환해야 한다면 다음을 할 수 있어요.
SELECT '[1, 2, 3]'::Variant(String)::Variant(String, Array(UInt64), UInt64) as variant, variantType(variant) as variant_type
┌─variant───┬─variant_type─┐
│ [1, 2, 3] │ String │
└───────────┴──────────────┘
Variant 컬럼을 일반 컬럼으로 변환 (Converting a Variant column to an ordinary column)
Variant 컬럼을 일반 컬럼으로 변환할 수 있어요. 이 경우 모든 중첩 variant가 대상 타입으로 변환돼요.
CREATE TABLE test (v Variant(UInt64, String)) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('42.42');
SELECT v::Nullable(Float64) FROM test;
┌─CAST(v, 'Nullable(Float64)')─┐
│ ᴺᵁᴸᴸ │
│ 42 │
│ 42.42 │
└──────────────────────────────┘
Variant를 다른 Variant로 변환 (Converting a Variant to another Variant)
Variant 컬럼을 다른 Variant 컬럼으로 변환할 수 있지만, 대상 Variant 컬럼이 원래 Variant의 모든 중첩 타입을 포함할 때만 가능해요.
CREATE TABLE test (v Variant(UInt64, String)) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('String');
SELECT v::Variant(UInt64, String, Array(UInt64)) FROM test;
┌─CAST(v, 'Variant(UInt64, String, Array(UInt64))')─┐
│ ᴺᵁᴸᴸ │
│ 42 │
│ String │
└───────────────────────────────────────────────────┘
데이터에서 Variant 타입 읽기 (Reading Variant type from the data)
모든 텍스트 형식(TSV, CSV, CustomSeparated, Values, JSONEachRow 등)이 Variant 타입 읽기를 지원해요. 데이터 파싱 중 ClickHouse는 값을 가장 적절한 variant 타입에 넣으려고 해요. 예시:
SELECT
v,
variantElement(v, 'String') AS str,
variantElement(v, 'UInt64') AS num,
variantElement(v, 'Float64') AS float,
variantElement(v, 'DateTime') AS date,
variantElement(v, 'Array(UInt64)') AS arr
FROM format(JSONEachRow, 'v Variant(String, UInt64, Float64, DateTime, Array(UInt64))', $$
{"v" : "Hello, World!"},
{"v" : 42},
{"v" : 42.42},
{"v" : "2020-01-01 00:00:00"},
{"v" : [1, 2, 3]}
$$)
┌─v───────────────────┬─str───────────┬──num─┬─float─┬────────────────date─┬─arr─────┐
│ Hello, World! │ Hello, World! │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ [] │
│ 42 │ ᴺᵁᴸᴸ │ 42 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ [] │
│ 42.42 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 42.42 │ ᴺᵁᴸᴸ │ [] │
│ 2020-01-01 00:00:00 │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ 2020-01-01 00:00:00 │ [] │
│ [1,2,3] │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ [1,2,3] │
└─────────────────────┴───────────────┴──────┴───────┴─────────────────────┴─────────┘
Variant 타입 값 비교 (Comparing values of Variant type)
Variant 타입의 값은 같은 Variant 타입의 값과만 비교할 수 있어요. 기본적으로 비교 연산자는 Variant 기본 구현을 사용해 각 variant 타입에 별도로 비교를 적용해요. 이것은 설정 use_variant_default_implementation_for_comparisons = 0으로 끄고 아래 설명된 네이티브 Variant 비교 규칙을 사용할 수 있어요. 참고로 ORDER BY는 항상 네이티브 비교를 사용해요.
네이티브 Variant 비교 규칙:
Variant(..., T1, ... T2, ...) 타입의 하부 타입 T1을 가진 v1과 하부 타입 T2를 가진 v2에 대한 연산자 <의 결과는 다음과 같이 정의돼요.
T1 = T2 = T이면 결과는v1.T < v2.T(하부 값이 비교돼요).T1 != T2이면 결과는T1 < T2(타입 이름이 비교돼요).
예시:
SET allow_suspicious_types_in_order_by = 1;
CREATE TABLE test (v1 Variant(String, UInt64, Array(UInt32)), v2 Variant(String, UInt64, Array(UInt32))) ENGINE=Memory;
INSERT INTO test VALUES (42, 42), (42, 43), (42, 'abc'), (42, [1, 2, 3]), (42, []), (42, NULL);
SET allow_suspicious_types_in_order_by = 1;
SELECT v2, variantType(v2) AS v2_type FROM test ORDER BY v2;
┌─v2──────┬─v2_type───────┐
│ [] │ Array(UInt32) │
│ [1,2,3] │ Array(UInt32) │
│ abc │ String │
│ 42 │ UInt64 │
│ 43 │ UInt64 │
│ ᴺᵁᴸᴸ │ None │
└─────────┴───────────────┘
SET use_variant_default_implementation_for_comparisons = 0;
SELECT v1, variantType(v1) AS v1_type, v2, variantType(v2) AS v2_type, v1 = v2, v1 < v2, v1 > v2 FROM test;
┌─v1─┬─v1_type─┬─v2──────┬─v2_type───────┬─equals(v1, v2)─┬─less(v1, v2)─┬─greater(v1, v2)─┐
│ 42 │ UInt64 │ 42 │ UInt64 │ 1 │ 0 │ 0 │
│ 42 │ UInt64 │ 43 │ UInt64 │ 0 │ 1 │ 0 │
│ 42 │ UInt64 │ abc │ String │ 0 │ 0 │ 1 │
│ 42 │ UInt64 │ [1,2,3] │ Array(UInt32) │ 0 │ 0 │ 1 │
│ 42 │ UInt64 │ [] │ Array(UInt32) │ 0 │ 0 │ 1 │
│ 42 │ UInt64 │ ᴺᵁᴸᴸ │ None │ 0 │ 1 │ 0 │
└────┴─────────┴─────────┴───────────────┴────────────────┴──────────────┴─────────────────┘
특정 Variant 값이 있는 행을 찾아야 한다면 다음 중 하나를 할 수 있어요.
- 값을 해당
Variant타입으로 캐스팅:
SET use_variant_default_implementation_for_comparisons = 0;
SELECT * FROM test WHERE v2 == [1,2,3]::Array(UInt32)::Variant(String, UInt64, Array(UInt32));
┌─v1─┬─v2──────┐
│ 42 │ [1,2,3] │
└────┴─────────┘
Variant서브컬럼과 필요한 타입을 비교:
SELECT * FROM test WHERE v2.`Array(UInt32)` == [1,2,3] -- or using variantElement(v2, 'Array(UInt32)')
┌─v1─┬─v2──────┐
│ 42 │ [1,2,3] │
└────┴─────────┘
때로는 Array/Map/Tuple 같은 복합 타입의 서브컬럼은 Nullable 안에 들어갈 수 없어 다른 타입의 행에서 NULL 대신 기본값을 갖게 되므로, variant 타입에 대한 추가 검사를 하는 것이 유용할 수 있어요.
SELECT v2, v2.`Array(UInt32)`, variantType(v2) FROM test WHERE v2.`Array(UInt32)` == [];
┌─v2───┬─v2.Array(UInt32)─┬─variantType(v2)─┐
│ 42 │ [] │ UInt64 │
│ 43 │ [] │ UInt64 │
│ abc │ [] │ String │
│ [] │ [] │ Array(UInt32) │
│ ᴺᵁᴸᴸ │ [] │ None │
└──────┴──────────────────┴─────────────────┘
SELECT v2, v2.`Array(UInt32)`, variantType(v2) FROM test WHERE variantType(v2) == 'Array(UInt32)' AND v2.`Array(UInt32)` == [];
┌─v2─┬─v2.Array(UInt32)─┬─variantType(v2)─┐
│ [] │ [] │ Array(UInt32) │
└────┴──────────────────┴─────────────────┘
참고: 서로 다른 숫자 타입의 variant 값들은 서로 다른 variant로 간주되어 서로 비교되지 않고, 그들의 타입 이름이 대신 비교돼요. 예시:
SET allow_suspicious_variant_types = 1;
SET allow_suspicious_types_in_order_by = 1;
CREATE TABLE test (v Variant(UInt32, Int64)) ENGINE=Memory;
INSERT INTO test VALUES (1::UInt32), (1::Int64), (100::UInt32), (100::Int64);
SELECT v, variantType(v) FROM test ORDER by v;
┌─v───┬─variantType(v)─┐
│ 1 │ Int64 │
│ 100 │ Int64 │
│ 1 │ UInt32 │
│ 100 │ UInt32 │
└─────┴────────────────┘
참고: 기본적으로 Variant 타입은 GROUP BY/ORDER BY 키에 허용되지 않아요. 사용하려면 특별한 비교 규칙을 고려하고 allow_suspicious_types_in_group_by/allow_suspicious_types_in_order_by 설정을 켜야 해요.
Variant와 JSONExtract 함수 (JSONExtract functions with Variant)
모든 JSONExtract* 함수가 Variant 타입을 지원해요.
SELECT JSONExtract('{"a" : [1, 2, 3]}', 'a', 'Variant(UInt32, String, Array(UInt32))') AS variant, variantType(variant) AS variant_type;
┌─variant─┬─variant_type──┐
│ [1,2,3] │ Array(UInt32) │
└─────────┴───────────────┘
SELECT JSONExtract('{"obj" : {"a" : 42, "b" : "Hello", "c" : [1,2,3]}}', 'obj', 'Map(String, Variant(UInt32, String, Array(UInt32)))') AS map_of_variants, mapApply((k, v) -> (k, variantType(v)), map_of_variants) AS map_of_variant_types
┌─map_of_variants──────────────────┬─map_of_variant_types────────────────────────────┐
│ {'a':42,'b':'Hello','c':[1,2,3]} │ {'a':'UInt32','b':'String','c':'Array(UInt32)'} │
└──────────────────────────────────┴─────────────────────────────────────────────────┘
SELECT JSONExtractKeysAndValues('{"a" : 42, "b" : "Hello", "c" : [1,2,3]}', 'Variant(UInt32, String, Array(UInt32))') AS variants, arrayMap(x -> (x.1, variantType(x.2)), variants) AS variant_types
┌─variants───────────────────────────────┬─variant_types─────────────────────────────────────────┐
│ [('a',42),('b','Hello'),('c',[1,2,3])] │ [('a','UInt32'),('b','String'),('c','Array(UInt32)')] │
└────────────────────────────────────────┴───────────────────────────────────────────────────────┘
Variant 인자를 가진 함수 (Functions with Variant arguments)
ClickHouse의 대부분 함수는 Variant 기본 구현(default implementation for Variant) 을 통해 Variant 타입 인자를 자동으로 지원해요. 26.1 버전부터, Variant 타입을 명시적으로 처리하지 않는 함수가 Variant 컬럼을 받으면 ClickHouse는:
- Variant 컬럼에서 각 variant 타입을 추출하고,
- 각 variant 타입에 대해 함수를 별도로 실행하며,
- 결과 타입에 따라 결과를 적절히 결합해요.
이것은 특별한 처리 없이 일반 함수를 Variant 컬럼에 사용할 수 있게 해줘요. 예시:
SET variant_throw_on_type_mismatch = 0;
CREATE TABLE test (v Variant(UInt32, String)) ENGINE = Memory;
INSERT INTO test VALUES (42), ('hello'), (NULL);
SELECT *, toTypeName(v) FROM test WHERE v = 42;
┌─v──┬─toTypeName(v)───────────┐
│ 42 │ Variant(String, UInt32) │
└────┴─────────────────────────┘
비교 연산자가 각 variant 타입에 자동으로 별도 적용되어 Variant 컬럼 필터링이 가능해요.
결과 타입 동작 (Result type behavior):
결과 타입은 함수가 각 variant에 대해 반환하는 것에 따라 달라져요.
- 다른 결과 타입:
Variant(T1, T2, ...)
SET variant_throw_on_type_mismatch = 0;
CREATE TABLE test2 (v Variant(UInt64, Float64)) ENGINE = Memory;
INSERT INTO test2 VALUES (42::UInt64), (42.42);
SELECT v + 1 AS result, toTypeName(result) FROM test2;
┌─result─┬─toTypeName(plus(v, 1))──┐
│ 43 │ Variant(Float64, UInt64) │
│ 43.42 │ Variant(Float64, UInt64) │
└────────┴─────────────────────────┘
- 타입 비호환: 호환되지 않는 variant에 대한
NULL
SET variant_throw_on_type_mismatch = 0;
CREATE TABLE test3 (v Variant(Array(UInt32), UInt32)) ENGINE = Memory;
INSERT INTO test3 VALUES ([1,2,3]), (42);
SELECT v + 10 AS result, toTypeName(result) FROM test3;
┌─result─┬─toTypeName(plus(v, 10))─┐
│ ᴺᵁᴸᴸ │ Nullable(UInt64) │
│ 52 │ Nullable(UInt64) │
└────────┴─────────────────────────┘
오류 처리 (Error handling): 함수가 variant 타입을 처리할 수 없으면 타입 관련 오류(ILLEGAL_TYPE_OF_ARGUMENT, TYPE_MISMATCH, CANNOT_CONVERT_TYPE, NO_COMMON_TYPE)만 잡혀 그 행들에 대해 NULL이 돼요. 0으로 나누기나 메모리 부족 같은 다른 오류는 실제 문제를 조용히 숨기지 않도록 정상적으로 발생해요.
타입 불일치 동작 (Type mismatch behavior)
설정 variant_throw_on_type_mismatch는 함수가 Variant 컬럼에 적용되었을 때 행의 실제 저장 타입이 그 함수와 호환되지 않는 경우 어떻게 할지를 제어해요.
true(기본값) — 첫 번째 호환되지 않는 행에서 예외(ILLEGAL_TYPE_OF_ARGUMENT)를 던져요.false— 호환되지 않는 행에는NULL을 반환하고, 호환되는 행에는 결과를 유지해요.
예시:
CREATE TABLE test (v Variant(String, UInt64)) ENGINE = Memory;
INSERT INTO test VALUES ('hello'), (42), ('foo');
-- Default (throw on mismatch): length() does not accept UInt64, so the query throws.
SELECT length(v) FROM test; -- throws ILLEGAL_TYPE_OF_ARGUMENT
-- With throw disabled: incompatible rows return NULL.
SET variant_throw_on_type_mismatch = false;
SELECT v, length(v) FROM test ORDER BY v::String NULLS LAST;
┌─v─────┬─length(v)─┐
│ foo │ 3 │
│ hello │ 5 │
│ 42 │ ᴺᵁᴸᴸ │
└───────┴───────────┘