SELECT
SELECT (조회)
하나 이상의 테이블에서 행을 검색하는 Trino의 핵심 쿼리 문이에요. 거의 모든 데이터 분석은 이 SELECT 문에서 시작해요.
출처: 문서
본문
하나 이상의 테이블에서 행을 검색해요.
시그니처
[ WITH SESSION [ name = expression [, ...] ]
[ WITH [ FUNCTION udf ] [, ...] ]
[ WITH [ RECURSIVE ] with_query [, ...] ]
SELECT [ ALL | DISTINCT ] select_expression [, ...]
[ FROM from_item [, ...] ]
[ WHERE condition ]
[ GROUP BY [ ALL | DISTINCT ] grouping_element [, ...] ]
[ HAVING condition]
[ WINDOW window_definition_list]
[ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
[ ORDER BY expression [ ASC | DESC ] [, ...] ]
[ OFFSET count [ ROW | ROWS ] ]
[ LIMIT { count | ALL } ]
[ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ]
from_item은 다음 중 하나예요:
table_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
from_item join_type from_item
[ ON join_condition | USING ( join_column [, ...] ) ]
table_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
MATCH_RECOGNIZE pattern_recognition_specification
[ [ AS ] alias [ ( column_alias [, ...] ) ] ]
MATCH_RECOGNIZE 절의 자세한 설명은 FROM 절의 패턴 인식 문서를 참고해요.
from_item PIVOT pivot_specification
[ [ AS ] alias [ ( column_alias [, ...] ) ] ]
PIVOT 절의 자세한 설명은 pivot 문서를 참고해요.
TABLE (table_function_invocation) [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
테이블 함수 사용 설명은 table functions 문서를 참고해요.
join_type은 다음 중 하나예요:
[ INNER ] JOIN
LEFT [ OUTER ] JOIN
RIGHT [ OUTER ] JOIN
FULL [ OUTER ] JOIN
CROSS JOIN
grouping_element은 다음 중 하나예요:
()
expression
AUTO
GROUPING SETS ( ( column [, ...] ) [, ...] )
CUBE ( column [, ...] )
ROLLUP ( column [, ...] )
WITH SESSION 절
WITH SESSION 절은 현재 SELECT 문의 처리에만 적용되는 세션·카탈로그 세션 속성 값을 설정하게 해줘요. 정의된 값은 다른 모든 구성과 세션 속성 설정을 덮어써요. 여러 속성은 쉼표로 구분돼요.
다음 예시는 전역 구성 속성 query.max-execution-time을 세션 속성 query_max_execution_time으로 덮어써 실행 시간을 2h로 줄여요. 또한 Iceberg 커넥터를 사용하는 example 카탈로그의 카탈로그 속성 iceberg.query-partition-filter-required를 카탈로그 세션 속성 query_partition_filter_required를 true로 설정해 덮어써요:
WITH
SESSION
query_max_execution_time='2h',
example.query_partition_filter_required=true
SELECT *
FROM example.default.thetable
LIMIT 100;
WITH FUNCTION 절
WITH FUNCTION 절은 쿼리의 나머지 부분에서 사용할 수 있는 인라인 사용자 정의 함수 목록을 정의하게 해줘요.
다음 예시는 두 인라인 UDF를 선언하고 사용해요:
WITH
FUNCTION hello(name varchar)
RETURNS varchar
RETURN format('Hello %s!', name),
FUNCTION bye(name varchar)
RETURNS varchar
RETURN format('Bye %s!', name)
SELECT hello('Finn') || ' and ' || bye('Joe');
-- Hello Finn! and Bye Joe!
UDF 전반, 인라인 UDF, 모든 지원 문, 예시에 대한 추가 정보는 사용자 정의 함수 문서에서 찾을 수 있어요.
WITH 절
WITH 절은 쿼리 안에서 사용할 이름 붙은 관계를 정의해요. 중첩된 쿼리를 펼치거나 서브쿼리를 단순화하게 해줘요. 예를 들어 다음 쿼리들은 동등해요:
SELECT a, b
FROM (
SELECT a, MAX(b) AS b FROM t GROUP BY a
) AS x;
WITH x AS (SELECT a, MAX(b) AS b FROM t GROUP BY a)
SELECT a, b FROM x;
이것은 여러 서브쿼리에서도 동작해요:
WITH
t1 AS (SELECT a, MAX(b) AS b FROM x GROUP BY a),
t2 AS (SELECT a, AVG(d) AS d FROM y GROUP BY a)
SELECT t1.*, t2.*
FROM t1
JOIN t2 ON t1.a = t2.a;
또한 WITH 절 안의 관계는 체인으로 연결될 수 있어요:
WITH
x AS (SELECT a FROM t),
y AS (SELECT a AS b FROM x),
z AS (SELECT b AS c FROM y)
SELECT c FROM z;
Warning
현재
WITH절의 SQL은 이름 붙은 관계가 사용되는 어디든 인라인됩니다. 이는 관계가 두 번 이상 사용되고 쿼리가 비결정적이면 매번 결과가 다를 수 있다는 뜻이에요.
WITH RECURSIVE 절
WITH RECURSIVE 절은 WITH 절의 변형이에요. 처리할 쿼리 목록을 정의하며, 적합한 쿼리의 재귀 처리를 포함해요.
Warning
이 기능은 실험적입니다. 잠재적인 쿼리 실패와 재귀 처리가 워크로드에 미치는 영향을 이해하고 있을 때만 사용하세요.
재귀 WITH-쿼리는 두 관계의 UNION 형태여야 해요. 첫 번째 관계는 recursion base(재귀 기반)라 하고, 두 번째 관계는 recursion step(재귀 단계)이라 해요. Trino는 쿼리 안에서 WITH-쿼리에 대한 단일 재귀 참조가 있는 재귀 WITH-쿼리를 지원해요. 쿼리 T의 이름 T는 재귀 단계 관계의 FROM 절에서 한 번 언급될 수 있어요.
다음 목록은 목록의 단일 쿼리로 자주 쓰이는 형태를 보여주는 간단한 예시예요:
WITH RECURSIVE t(n) AS (
VALUES (1)
UNION ALL
SELECT n + 1 FROM t WHERE n < 4
)
SELECT sum(n) FROM t;
위 쿼리에서 단순 할당 VALUES (1)이 recursion base 관계를 정의해요. SELECT n + 1 FROM t WHERE n < 4는 recursion step 관계를 정의해요. 재귀 처리는 다음 단계를 수행해요:
- recursive base는
1을 산출 - 첫 번째 재귀는
1 + 1 = 2를 산출 - 두 번째 재귀는 첫 번째 결과를 사용해 1을 더한다:
2 + 1 = 3 - 세 번째 재귀는 두 번째 결과를 사용해 다시 1을 더한다:
3 + 1 = 4 - 네 번째 재귀는
n = 4이므로 중단 - 그 결과
t는1,2,3,4값을 가짐 - 최종 문은 이 요소들의 합 연산을 수행하며 최종 결과 값은
10
반환된 컬럼의 타입은 base 관계의 타입이에요. 따라서 step 관계의 타입이 base 관계 타입으로 강제될 수 있어야 해요.
RECURSIVE 절은 WITH 목록의 모든 쿼리에 적용되지만, 전부 재귀일 필요는 없어요. WITH-쿼리가 위 규칙에 맞지 않거나 재귀 참조를 포함하지 않으면 일반 WITH-쿼리처럼 처리돼요. 재귀 WITH 목록의 모든 쿼리에 컬럼 별칭은 필수예요.
WITH 절 제한에 더해, SQL 표준을 따르고 구현 선택으로 인해 다음 제한이 적용돼요:
- 단일 요소 재귀 주기만 지원돼요. 일반
WITH-쿼리처럼WITH목록의 이전 쿼리 참조는 허용돼요. 이후 쿼리 참조는 금지돼요. - 외부 결합, 집합 연산, limit 절 등의 사용은 step 관계에서 항상 허용되지는 않아요.
- 재귀 깊이는 고정되며 기본값은
10이고, 실제 쿼리 결과에 의존하지 않아요.
재귀 깊이는 세션 속성 max_recursion_depth로 조정할 수 있어요. 값을 바꿀 때 쿼리 계획 크기의 증가가 재귀 깊이와 함께 이차적으로 커진다는 점을 고려해요.
SELECT 절
SELECT 절은 쿼리의 출력을 지정해요. 각 select_expression은 결과에 포함될 컬럼(들)을 정의해요.
SELECT [ ALL | DISTINCT ] select_expression [, ...]
ALL과 DISTINCT 수량자는 중복 행이 결과 집합에 포함되는지 결정해요. 인자 ALL을 지정하면 모든 행이 포함돼요. 인자 DISTINCT를 지정하면 고유 행만 결과 집합에 포함돼요. 이 경우 각 출력 컬럼은 비교가 가능한 타입이어야 해요. 어느 인자도 지정하지 않으면 동작은 기본적으로 ALL이에요.
Select 표현식
각 select_expression은 다음 형태 중 하나여야 해요:
expression [ [ AS ] column_alias ]
row_expression.* [ AS ( column_alias [, ...] ) ]
relation.*
*
expression [ [ AS ] column_alias ]의 경우 단일 출력 컬럼이 정의돼요.
row_expression.* [ AS ( column_alias [, ...] ) ]의 경우 row_expression은 ROW 타입의 임의 표현식이에요. 행의 모든 필드가 결과 집합에 포함될 출력 컬럼을 정의해요. 수신자가 JSON 타입이면 같은 표면 문법이 JSON 단순 접근자를 호출해요 — 자세한 내용은 JSON 함수 레퍼런스를 참고해요.
relation.*의 경우 relation의 모든 컬럼이 결과 집합에 포함돼요. 이 경우 컬럼 별칭은 허용되지 않아요.
*의 경우 쿼리가 정의한 관계의 모든 컬럼이 결과 집합에 포함돼요.
결과 집합에서 컬럼의 순서는 select 표현식이 지정된 순서와 같아요. select 표현식이 여러 컬럼을 반환하면, 소스 관계나 행 타입 표현식에서 정렬된 방식과 동일하게 정렬돼요.
컬럼 별칭이 지정되면 기존 컬럼이나 행 필드 이름을 덮어써요:
SELECT (CAST(ROW(1, true) AS ROW(field1 bigint, field2 boolean))).* AS (alias1, alias2);
alias1 | alias2
--------+--------
1 | true
(1 row)
그렇지 않으면 기존 이름이 사용돼요:
SELECT (CAST(ROW(1, true) AS ROW(field1 bigint, field2 boolean))).*;
field1 | field2
--------+--------
1 | true
(1 row)
그리고 이름이 없으면 익명 컬럼이 생성돼요:
SELECT (ROW(1, true)).*;
_col0 | _col1
-------+-------
1 | true
(1 row)
GROUP BY 절
GROUP BY 절은 SELECT 문의 출력을 일치 값을 가진 행 그룹으로 나눠요. 단순 GROUP BY 절은 입력 컬럼으로 구성된 어떤 표현식도 포함할 수 있고, 출력 컬럼을 위치로 선택하는 서수(1부터 시작)일 수도 있어요.
다음 쿼리들은 동등해요. 둘 다 nationkey 입력 컬럼으로 출력을 그룹화하되, 첫 번째 쿼리는 출력 컬럼의 서수 위치를, 두 번째 쿼리는 입력 컬럼 이름을 사용해요:
SELECT count(*), nationkey FROM customer GROUP BY 2;
SELECT count(*), nationkey FROM customer GROUP BY nationkey;
GROUP BY 절은 select 문의 출력에 나타나지 않는 입력 컬럼 이름으로 출력을 그룹화할 수 있어요. 예를 들어 다음 쿼리는 입력 컬럼 mktsegment를 사용해 customer 테이블의 행 수를 생성해요:
SELECT count(*) FROM customer GROUP BY mktsegment;
_col0
-------
29968
30142
30189
29949
29752
(5 rows)
SELECT 문에서 GROUP BY 절을 사용하면 모든 출력 표현식은 집계 함수이거나 GROUP BY 절에 있는 컬럼이어야 해요.
복잡한 그룹핑 연산
Trino는 GROUPING SETS, CUBE, ROLLUP 문법으로 복잡한 집계도 지원해요. 이 문법은 단일 쿼리에서 여러 컬럼 집합에 대한 집계가 필요한 분석을 수행하게 해줘요. 복잡한 그룹핑 연산은 입력 컬럼으로 구성된 표현식에 대한 그룹핑을 지원하지 않아요. 컬럼 이름만 허용돼요.
복잡한 그룹핑 연산은 종종 단순 GROUP BY 표현식의 UNION ALL과 동등해요. 아래 예시에서 그렇죠. 다만 집계 데이터 소스가 비결정적이면 이 동등성은 적용되지 않아요.
AUTO
AUTO를 지정하면 Trino 엔진이 그룹핑 컬럼을 명시적으로 나열하도록 요구하는 대신 자동으로 결정해요. 이 모드에서 SELECT 목록의 집계 함수 일부가 아닌 모든 컬럼은 암시적으로 그룹핑 컬럼으로 취급돼요.
이 예시 쿼리는 시장 세그먼트별 총 계정 잔액을 계산해요. AUTO 절은 mktsegment가 어떤 집계 함수(즉 sum)에도 사용되지 않으므로 그룹핑 키로 파생해요.
SELECT mktsegment, sum(acctbal) FROM shipping GROUP BY AUTO;
mktsegment | _col1
------------+--------------------
BUILDING | 1444587.8
MACHINERY | 1296958.61
HOUSEHOLD | 1279340.66
FURNITURE | 1265282.8
AUTOMOBILE | 1395695.7200000004
(5 rows)
GROUPING SETS
그룹핑 셋은 그룹핑할 컬럼 목록을 여러 개 지정하게 해줘요. 주어진 그룹핑 컬럼 부분 목록에 없는 컬럼은 NULL로 설정돼요.
SELECT * FROM shipping;
origin_state | origin_zip | destination_state | destination_zip | package_weight
--------------+------------+-------------------+-----------------+----------------
California | 94131 | New Jersey | 8648 | 13
California | 94131 | New Jersey | 8540 | 42
New Jersey | 7081 | Connecticut | 6708 | 225
California | 90210 | Connecticut | 6927 | 1337
California | 94131 | Colorado | 80302 | 5
New York | 10002 | New Jersey | 8540 | 3
(6 rows)
GROUPING SETS 의미는 이 예시 쿼리로 설명돼요:
SELECT origin_state, origin_zip, destination_state, sum(package_weight)
FROM shipping
GROUP BY GROUPING SETS (
(origin_state),
(origin_state, origin_zip),
(destination_state));
origin_state | origin_zip | destination_state | _col0
--------------+------------+-------------------+-------
New Jersey | NULL | NULL | 225
California | NULL | NULL | 1397
New York | NULL | NULL | 3
California | 90210 | NULL | 1337
California | 94131 | NULL | 60
New Jersey | 7081 | NULL | 225
New York | 10002 | NULL | 3
NULL | NULL | Colorado | 5
NULL | NULL | New Jersey | 58
NULL | NULL | Connecticut | 1562
(10 rows)
위 쿼리는 논리적으로 여러 GROUP BY 쿼리의 UNION ALL과 동등하다고 볼 수 있어요:
SELECT origin_state, NULL, NULL, sum(package_weight)
FROM shipping GROUP BY origin_state
UNION ALL
SELECT origin_state, origin_zip, NULL, sum(package_weight)
FROM shipping GROUP BY origin_state, origin_zip
UNION ALL
SELECT NULL, NULL, destination_state, sum(package_weight)
FROM shipping GROUP BY destination_state;
다만 복잡한 그룹핑 문법(GROUPING SETS, CUBE, ROLLUP)이 있는 쿼리는 기본 데이터 소스를 한 번만 읽는 반면, UNION ALL 쿼리는 기본 데이터를 세 번 읽어요. 그래서 데이터 소스가 결정적이지 않으면 UNION ALL 쿼리가 일관되지 않은 결과를 만들 수 있어요.
CUBE
CUBE 연산자는 주어진 컬럼 집합에 대한 모든 가능한 그룹핑 셋(즉 power set)을 생성해요. 예를 들어 쿼리:
SELECT origin_state, destination_state, sum(package_weight)
FROM shipping
GROUP BY CUBE (origin_state, destination_state);
는 다음과 동등해요:
SELECT origin_state, destination_state, sum(package_weight)
FROM shipping
GROUP BY GROUPING SETS (
(origin_state, destination_state),
(origin_state),
(destination_state),
()
);
origin_state | destination_state | _col0
--------------+-------------------+-------
California | New Jersey | 55
California | Colorado | 5
New York | New Jersey | 3
New Jersey | Connecticut | 225
California | Connecticut | 1337
California | NULL | 1397
New York | NULL | 3
New Jersey | NULL | 225
NULL | New Jersey | 58
NULL | Connecticut | 1562
NULL | Colorado | 5
NULL | NULL | 1625
(12 rows)
ROLLUP
ROLLUP 연산자는 주어진 컬럼 집합에 대한 모든 가능한 부분 합계(subtotal)를 생성해요. 예를 들어 쿼리:
SELECT origin_state, origin_zip, sum(package_weight)
FROM shipping
GROUP BY ROLLUP (origin_state, origin_zip);
origin_state | origin_zip | _col2
--------------+------------+-------
California | 94131 | 60
California | 90210 | 1337
New Jersey | 7081 | 225
New York | 10002 | 3
California | NULL | 1397
New York | NULL | 3
New Jersey | NULL | 225
NULL | NULL | 1625
(8 rows)
는 다음과 동등해요:
SELECT origin_state, origin_zip, sum(package_weight)
FROM shipping
GROUP BY GROUPING SETS ((origin_state, origin_zip), (origin_state), ());
여러 그룹핑 표현식 결합
같은 쿼리에서 여러 그룹핑 표현식은 크로스 곱(cross-product) 의미를 갖는 것으로 해석돼요. 예를 들어 다음 쿼리:
SELECT origin_state, destination_state, origin_zip, sum(package_weight)
FROM shipping
GROUP BY
GROUPING SETS ((origin_state, destination_state)),
ROLLUP (origin_zip);
는 다음과 다시 쓸 수 있어요:
SELECT origin_state, destination_state, origin_zip, sum(package_weight)
FROM shipping
GROUP BY
GROUPING SETS ((origin_state, destination_state)),
GROUPING SETS ((origin_zip), ());
논리적으로 다음와 동등해요:
SELECT origin_state, destination_state, origin_zip, sum(package_weight)
FROM shipping
GROUP BY GROUPING SETS (
(origin_state, destination_state, origin_zip),
(origin_state, destination_state)
);
origin_state | destination_state | origin_zip | _col3
--------------+-------------------+------------+-------
New York | New Jersey | 10002 | 3
California | New Jersey | 94131 | 55
New Jersey | Connecticut | 7081 | 225
California | Connecticut | 90210 | 1337
California | Colorado | 94131 | 5
New York | New Jersey | NULL | 3
New Jersey | Connecticut | NULL | 225
California | Colorado | NULL | 5
California | Connecticut | NULL | 1337
California | New Jersey | NULL | 55
(10 rows)
ALL과 DISTINCT 수량자는 중복 그룹핑 셋이 각각 별개의 출력 행을 만드는지 결정해요. 같은 쿼리에서 여러 복잡한 그룹핑 셋을 결합할 때 특히 유용해요. 예를 들어 다음 쿼리:
SELECT origin_state, destination_state, origin_zip, sum(package_weight)
FROM shipping
GROUP BY ALL
CUBE (origin_state, destination_state),
ROLLUP (origin_state, origin_zip);
는 다음과 동등해요:
SELECT origin_state, destination_state, origin_zip, sum(package_weight)
FROM shipping
GROUP BY GROUPING SETS (
(origin_state, destination_state, origin_zip),
(origin_state, origin_zip),
(origin_state, destination_state, origin_zip),
(origin_state, origin_zip),
(origin_state, destination_state),
(origin_state),
(origin_state, destination_state),
(origin_state),
(origin_state, destination_state),
(origin_state),
(destination_state),
()
);
다만 쿼리가 GROUP BY에 DISTINCT 수량자를 사용하면:
SELECT origin_state, destination_state, origin_zip, sum(package_weight)
FROM shipping
GROUP BY DISTINCT
CUBE (origin_state, destination_state),
ROLLUP (origin_state, origin_zip);
고유 그룹핑 셋만 생성돼요:
SELECT origin_state, destination_state, origin_zip, sum(package_weight)
FROM shipping
GROUP BY GROUPING SETS (
(origin_state, destination_state, origin_zip),
(origin_state, origin_zip),
(origin_state, destination_state),
(origin_state),
(destination_state),
()
);
기본 집합 수량자는 ALL이에요.
GROUPING 연산
grouping(col1, ..., colN) -> bigint
그룹핑 연산은 십진수로 변환된 비트 집합을 반환하며, 어떤 컬럼이 그룹핑에 있는지 나타내요. GROUPING SETS, ROLLUP, CUBE, GROUP BY와 함께 사용해야 하며, 그 인자는 해당 GROUPING SETS, ROLLUP, CUBE, GROUP BY 절에서 참조된 컬럼과 정확히 일치해야 해요.
특정 행의 결과 비트 집합을 계산하려면, 인자 컬럼에 가장 오른쪽 컬럼이 최하위 비트가 되도록 비트를 할당해요. 주어진 그룹핑에 대해 해당 컬럼이 그룹핑에 포함되면 비트는 0으로, 그렇지 않으면 1로 설정돼요. 예를 들어 아래 쿼리를 고려해요:
SELECT origin_state, origin_zip, destination_state, sum(package_weight),
grouping(origin_state, origin_zip, destination_state)
FROM shipping
GROUP BY GROUPING SETS (
(origin_state),
(origin_state, origin_zip),
(destination_state)
);
origin_state | origin_zip | destination_state | _col3 | _col4
--------------+------------+-------------------+-------+-------
California | NULL | NULL | 1397 | 3
New Jersey | NULL | NULL | 225 | 3
New York | NULL | NULL | 3 | 3
California | 94131 | NULL | 60 | 1
New Jersey | 7081 | NULL | 225 | 1
California | 90210 | NULL | 1337 | 1
New York | 10002 | NULL | 3 | 1
NULL | NULL | New Jersey | 58 | 6
NULL | NULL | Connecticut | 1562 | 6
NULL | NULL | Colorado | 5 | 6
(10 rows)
위 결과의 첫 번째 그룹핑은 origin_state 컬럼만 포함하고 origin_zip과 destination_state 컬럼은 제외해요. 그 그룹핑에 대해 구성된 비트 집합은 011이며, 여기서 최상위 비트가 origin_state를 나타내요.
HAVING 절
HAVING 절은 집계 함수와 GROUP BY 절과 함께 사용되어 어떤 그룹을 선택할지 제어해요. HAVING 절은 주어진 조건을 충족하지 않는 그룹을 제거해요. HAVING은 그룹과 집계가 계산된 후 그룹을 필터링해요.
다음 예시는 customer 테이블을 쿼리하고 계정 잔액이 지정된 값보다 큰 그룹을 선택해요:
SELECT count(*), mktsegment, nationkey,
CAST(sum(acctbal) AS bigint) AS totalbal
FROM customer
GROUP BY mktsegment, nationkey
HAVING sum(acctbal) > 5700000
ORDER BY totalbal DESC;
_col0 | mktsegment | nationkey | totalbal
-------+------------+-----------+----------
1272 | AUTOMOBILE | 19 | 5856939
1253 | FURNITURE | 14 | 5794887
1248 | FURNITURE | 9 | 5784628
1243 | FURNITURE | 12 | 5757371
1231 | HOUSEHOLD | 3 | 5753216
1251 | MACHINERY | 2 | 5719140
1247 | FURNITURE | 8 | 5701952
(7 rows)
WINDOW 절
WINDOW 절은 이름 붙은 윈도우 사양을 정의하는 데 사용돼요. 정의된 이름 붙은 윈도우 사양은 둘러싸는 쿼리의 SELECT와 ORDER BY 절에서 참조할 수 있어요:
SELECT orderkey, clerk, totalprice,
rank() OVER w AS rnk
FROM orders
WINDOW w AS (PARTITION BY clerk ORDER BY totalprice DESC)
ORDER BY count() OVER w, clerk, rnk
WINDOW 절의 윈도우 정의 목록은 다음 형태의 이름 붙은 윈도우 사양을 하나 이상 포함할 수 있어요:
window_name AS (window_specification)
윈도우 사양은 다음 구성 요소를 가져요:
- 기존 윈도우 이름.
WINDOW절의 이름 붙은 윈도우 사양을 참조해요. 참조된 이름과 연관된 윈도우 사양이 현재 사양의 기초예요. - 분할(partition) 사양. 입력 행을 다른 파티션으로 나눠요. 이는
GROUP BY절이 집계 함수를 위해 행을 다른 그룹으로 나누는 방식과 유사해요. - 정렬(order) 사양. 윈도우 함수가 입력 행을 처리할 순서를 결정해요.
- 윈도우 프레임. 주어진 행에 대해 함수가 처리할 행의 슬라이딩 윈도우를 지정해요. 프레임을 지정하지 않으면 기본값은
RANGE UNBOUNDED PRECEDING이며, 이는RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW와 같아요. 이 프레임은 파티션 시작부터 현재 행의 마지막 peer까지 모든 행을 포함해요.ORDER BY가 없으면 모든 행이 peer로 간주되므로RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW는BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING과 동등해요. 윈도우 프레임 문법은 행 패턴 인식용 추가 절을 지원해요. 행 패턴 인식 절을 지정하면, 특정 행의 윈도우 프레임은 그 행에서 시작하는 패턴과 일치한 행들로 구성돼요. 또한 프레임이 행 패턴 measure를 지정하면, 윈도우 함수처럼 윈도우에 대해 호출할 수 있어요. 자세한 내용은 윈도우 구조의 행 패턴 인식 문서를 참고해요.
각 윈도우 구성 요소는 선택적이에요. 윈도우 사양이 윈도우 분할·정렬·프레임을 지정하지 않으면, 그 구성 요소는 기존 윈도우 이름이 참조하는 윈도우 사양 또는 참조 체인의 다른 윈도우 사양에서 얻어요. 기존 윈도우 이름이 지정되지 않았거나 참조된 윈도우 사양 중 어느 것도 그 구성 요소를 포함하지 않으면 기본값이 사용돼요.
집합 연산
UNION, INTERSECT, EXCEPT는 모두 집합 연산이에요. 이 절들은 둘 이상의 select 문의 결과를 단일 결과 집합으로 결합하는 데 사용돼요:
query UNION [ALL | DISTINCT] [CORRESPONDING] query
query INTERSECT [ALL | DISTINCT] [CORRESPONDING] query
query EXCEPT [ALL | DISTINCT] [CORRESPONDING] query
인자 ALL 또는 DISTINCT는 어떤 행이 최종 결과 집합에 포함되는지 제어해요. ALL을 지정하면 행이 동일해도 모든 행이 포함돼요. DISTINCT를 지정하면 고유 행만 결합 결과 집합에 포함돼요. 어느 것도 지정하지 않으면 동작은 기본적으로 DISTINCT예요.
여러 집합 연산은 괄호로 순서를 명시하지 않으면 왼쪽에서 오른쪽으로 처리돼요. 또한 INTERSECT는 EXCEPT와 UNION보다 더 강하게 결합돼요. 즉 A UNION B INTERSECT C EXCEPT D는 A UNION (B INTERSECT C) EXCEPT D와 같아요.
UNION 절
UNION은 첫 번째 쿼리의 결과 집합에 있는 모든 행을 두 번째 쿼리의 결과 집합에 있는 행들과 결합해요. 다음은 가장 단순한 UNION 절 중 하나의 예시예요. 값 13을 선택하고 이 결과 집합을 값 42를 선택하는 두 번째 쿼리와 결합해요:
SELECT 13
UNION
SELECT 42;
_col0
-------
13
42
(2 rows)
다음 쿼리는 UNION과 UNION ALL의 차이를 보여줘요. 값 13을 선택하고 이 결과 집합을 42와 13 값을 선택하는 두 번째 쿼리와 결합해요:
SELECT 13
UNION
SELECT * FROM (VALUES 42, 13);
_col0
-------
13
42
(2 rows)
SELECT 13
UNION ALL
SELECT * FROM (VALUES 42, 13);
_col0
-------
13
42
13
(2 rows)
CORRESPONDING은 위치가 아니라 이름으로 컬럼을 일치시켜요:
SELECT * FROM (VALUES (1, 'alice')) AS t(id, name)
UNION ALL CORRESPONDING
SELECT * FROM (VALUES ('bob', 2)) AS t(name, id);
id | name
----+-------
1 | alice
2 | bob
(2 rows)
SELECT * FROM (VALUES (DATE '2025-04-23', 'alice')) AS t(order_date, name)
UNION ALL CORRESPONDING
SELECT * FROM (VALUES ('bob', 123.45)) AS t(name, price);
name
-------
alice
bob
(2 rows)
INTERSECT 절
INTERSECT는 첫 번째와 두 번째 쿼리의 결과 집합 모두에 있는 행만 반환해요. 다음은 가장 단순한 INTERSECT 절 중 하나의 예시예요. 값 13과 42를 선택하고 이 결과 집합을 값 13을 선택하는 두 번째 쿼리와 결합해요. 42는 첫 번째 쿼리의 결과 집합에만 있으므로 최종 결과에 포함되지 않아요.
SELECT * FROM (VALUES 13, 42)
INTERSECT
SELECT 13;
_col0
-------
13
(2 rows)
CORRESPONDING은 위치가 아니라 이름으로 컬럼을 일치시켜요:
SELECT * FROM (VALUES (1, 'alice')) AS t(id, name)
INTERSECT CORRESPONDING
SELECT * FROM (VALUES ('alice', 1)) AS t(name, id);
id | name
----+-------
1 | alice
(1 row)
EXCEPT 절
EXCEPT는 첫 번째 쿼리의 결과 집합에는 있지만 두 번째에는 없는 행을 반환해요. 다음은 가장 단순한 EXCEPT 절 중 하나의 예시예요. 값 13과 42를 선택하고 이 결과 집합을 값 13을 선택하는 두 번째 쿼리와 결합해요. 13은 두 번째 쿼리의 결과 집합에도 있으므로 최종 결과에 포함되지 않아요.
SELECT * FROM (VALUES 13, 42)
EXCEPT
SELECT 13;
_col0
-------
42
(2 rows)
CORRESPONDING은 위치가 아니라 이름으로 컬럼을 일치시켜요:
SELECT * FROM (VALUES (1, 'alice'), (2, 'bob')) AS t(id, name)
EXCEPT CORRESPONDING
SELECT * FROM (VALUES ('alice', 1)) AS t(name, id);
id | name
----+------
2 | bob
(1 row)
ORDER BY 절
ORDER BY 절은 하나 이상의 출력 표현식으로 결과 집합을 정렬하는 데 사용돼요:
ORDER BY expression [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...]
각 표현식은 출력 컬럼으로 구성될 수도 있고, 1부터 시작해 위치로 출력 컬럼을 선택하는 서수일 수도 있어요. ORDER BY 절은 어떤 GROUP BY나 HAVING 절 이후, 그리고 어떤 OFFSET, LIMIT, FETCH FIRST 절 이전에 평가돼요. 기본 null 정렬은 정렬 방향과 무관하게 NULLS LAST예요.
SQL 사양에 따라 ORDER BY 절은 그 절을 곧바로 포함하는 쿼리에 대해서만 행 순서에 영향을 준다는 점을 유의해요. Trino는 그 사양을 따르며, 부정적 성능 영향을 피하기 위해 중복 사용 절을 버려요.
다음 예시에서 절은 select 문에만 적용돼요.
INSERT INTO some_table
SELECT * FROM another_table
ORDER BY field;
SQL에서 테이블은 본질적으로 순서가 없고, 이 경우 ORDER BY 절이 어떤 차이도 만들지 않으면서 전체 insert 문 실행 성능에 부정적 영향을 주므로, Trino는 정렬 연산을 건너뛰어요.
ORDER BY 절이 중복이고 전체 문의 결과에 영향을 주지 않는 또 다른 예시는 중첩 쿼리예요:
SELECT *
FROM some_table
JOIN (SELECT * FROM another_table ORDER BY field) u
ON some_table.key = u.key;
더 많은 배경 정보와 세부 사항은 이 최적화에 관한 블로그 포스트에서 찾을 수 있어요.
OFFSET 절
OFFSET 절은 결과 집합에서 앞의 여러 행을 버리는 데 사용돼요:
OFFSET count [ ROW | ROWS ]
ORDER BY 절이 있으면 OFFSET 절은 정렬된 결과 집합에 대해 평가되고, 앞의 행이 버려진 후에도 집합은 정렬된 상태를 유지해요:
SELECT name FROM nation ORDER BY name OFFSET 22;
name
----------------
UNITED KINGDOM
UNITED STATES
VIETNAM
(3 rows)
그렇지 않으면 어떤 행이 버려지는지는 임의적이에요. OFFSET 절에 지정된 개수가 결과 집합 크기와 같거나 크면 최종 결과는 비어요.
LIMIT 또는 FETCH FIRST 절
LIMIT 또는 FETCH FIRST 절은 결과 집합의 행 수를 제한해요.
LIMIT { count | ALL }
FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES }
다음 예시는 큰 테이블을 쿼리하지만 LIMIT 절이 출력을 5행으로 제한해요(쿼리에 ORDER BY가 없으므로 정확히 어떤 행이 반환되는지는 임의적):
SELECT orderdate FROM orders LIMIT 5;
orderdate
------------
1994-07-25
1993-11-12
1992-10-06
1994-01-04
1997-12-28
(5 rows)
LIMIT ALL은 LIMIT 절을 생략하는 것과 같아요.
FETCH FIRST 절은 FIRST 또는 NEXT 키워드와 ROW 또는 ROWS 키워드를 지원해요. 이 키워드들은 동등하며 키워드 선택은 쿼리 실행에 영향을 주지 않아요.
FETCH FIRST 절에 개수를 지정하지 않으면 기본값은 1이에요:
SELECT orderdate FROM orders FETCH FIRST ROW ONLY;
orderdate
------------
1994-02-12
(1 row)
OFFSET 절이 있으면 LIMIT 또는 FETCH FIRST 절은 OFFSET 절 이후에 평가돼요:
SELECT * FROM (VALUES 5, 2, 4, 1, 3) t(x) ORDER BY x OFFSET 2 LIMIT 2;
x
---
3
4
(2 rows)
FETCH FIRST 절의 경우 인자 ONLY 또는 WITH TIES가 어떤 행이 결과 집합에 포함되는지 제어해요.
ONLY를 지정하면 결과 집합은 count가 정하는 정확한 앞의 행 수로 제한돼요.
WITH TIES를 지정하면 ORDER BY 절이 있어야 해요. 결과 집합은 같은 앞의 행 집합과, ORDER BY 절의 정렬이 정하는 마지막 행과 같은 peer 그룹('ties')의 모든 행으로 구성돼요. 결과 집합은 정렬돼요:
SELECT name, regionkey
FROM nation
ORDER BY regionkey FETCH FIRST ROW WITH TIES;
name | regionkey
------------+-----------
ETHIOPIA | 0
MOROCCO | 0
KENYA | 0
ALGERIA | 0
MOZAMBIQUE | 0
(5 rows)
TABLESAMPLE
여러 샘플링 방법이 있어요:
BERNOULLI
각 행이 표본 비율의 확률로 테이블 샘플에 선택돼요. 베르누이 방법으로 테이블을 샘플링하면 테이블의 모든 물리적 블록이 스캔되고, 특정 행이 건너뛰어져요(표본 비율과 런타임에 계산된 임의 값의 비교에 기반).
행이 결과에 포함될 확률은 다른 어떤 행과도 독립적이에요. 이것은 샘플링된 테이블을 디스크에서 읽는 데 걸리는 시간을 줄이지 않아요. 샘플링된 출력을 더 처리하면 전체 쿼리 시간에 영향을 줄 수 있어요.
SYSTEM
이 샘플링 방법은 테이블을 논리적 데이터 세그먼트로 나누고 이 세분성으로 테이블을 샘플링해요. 이 샘플링 방법은 특정 데이터 세그먼트의 모든 행을 선택하거나 건너뛰어요(표본 비율과 런타임에 계산된 임의 값의 비교에 기반).
시스템 샘플링에서 선택된 행은 어떤 커넥터를 사용하는지에 따라 달라져요. 예를 들어 Hive와 함께 사용하면 데이터가 HDFS에 어떻게 배치되는지에 따라 달라져요. 이 방법은 독립적인 샘플링 확률을 보장하지 않아요.
Note
두 방법 모두 반환되는 행 수에 대한 결정적 경계를 허용하지 않아요.
예시:
SELECT *
FROM users TABLESAMPLE BERNOULLI (50);
SELECT *
FROM users TABLESAMPLE SYSTEM (75);
결합과 함께 샘플링 사용:
SELECT o.*, i.*
FROM orders o TABLESAMPLE SYSTEM (10)
JOIN lineitem i TABLESAMPLE BERNOULLI (40)
ON o.orderkey = i.orderkey;
UNNEST
UNNEST는 ARRAY나 MAP을 관계로 확장하는 데 사용할 수 있어요. 배열은 단일 컬럼으로 확장돼요:
SELECT * FROM UNNEST(ARRAY[1,2]) AS t(number);
number
--------
1
2
(2 rows)
맵은 두 컬럼(key, value)으로 확장돼요:
SELECT * FROM UNNEST(
map_from_entries(
ARRAY[
('SQL',1974),
('Java', 1995)
]
)
) AS t(language, first_appeared_year);
language | first_appeared_year
----------+---------------------
SQL | 1974
Java | 1995
(2 rows)
UNNEST는 ROW 구조의 ARRAY와 결합해 ROW의 각 필드를 해당 컬럼으로 확장하는 데 사용될 수 있어요:
SELECT *
FROM UNNEST(
ARRAY[
ROW('Java', 1995),
ROW('SQL' , 1974)],
ARRAY[
ROW(false),
ROW(true)]
) as t(language,first_appeared_year,declarative);
language | first_appeared_year | declarative
----------+---------------------+-------------
Java | 1995 | false
SQL | 1974 | true
(2 rows)
UNNEST는 선택적으로 WITH ORDINALITY 절을 가질 수 있으며, 이 경우 추가 서수 컬럼이 끝에 추가돼요:
SELECT a, b, rownumber
FROM UNNEST (
ARRAY[2, 5],
ARRAY[7, 8, 9]
) WITH ORDINALITY AS t(a, b, rownumber);
a | b | rownumber
------+---+-----------
2 | 7 | 1
5 | 8 | 2
NULL | 9 | 3
(3 rows)
UNNEST는 배열/맵이 비어 있으면 0개 항목을 반환해요:
SELECT * FROM UNNEST (ARRAY[]) AS t(value);
value
-------
(0 rows)
UNNEST는 배열/맵이 null이면 0개 항목을 반환해요:
SELECT * FROM UNNEST (CAST(null AS ARRAY(integer))) AS t(number);
number
--------
(0 rows)
UNNEST는 보통 JOIN과 함께 사용되며, 결합 왼쪽 관계의 컬럼을 참조할 수 있어요:
SELECT student, score
FROM (
VALUES
('John', ARRAY[7, 10, 9]),
('Mary', ARRAY[4, 8, 9])
) AS tests (student, scores)
CROSS JOIN UNNEST(scores) AS t(score);
student | score
---------+-------
John | 7
John | 10
John | 9
Mary | 4
Mary | 8
Mary | 9
(6 rows)
UNNEST는 여러 인자와 함께 사용할 수도 있으며, 이 경우 여러 컬럼으로 확장되어 가장 높은 카디널리티 인자만큼의 행을 만들어요(다른 컬럼은 null로 채워짐):
SELECT numbers, animals, n, a
FROM (
VALUES
(ARRAY[2, 5], ARRAY['dog', 'cat', 'bird']),
(ARRAY[7, 8, 9], ARRAY['cow', 'pig'])
) AS x (numbers, animals)
CROSS JOIN UNNEST(numbers, animals) AS t (n, a);
numbers | animals | n | a
-----------+------------------+------+------
[2, 5] | [dog, cat, bird] | 2 | dog
[2, 5] | [dog, cat, bird] | 5 | cat
[2, 5] | [dog, cat, bird] | NULL | bird
[7, 8, 9] | [cow, pig] | 7 | cow
[7, 8, 9] | [cow, pig] | 8 | pig
[7, 8, 9] | [cow, pig] | 9 | NULL
(6 rows)
결합 왼쪽 관계의 참조된 컬럼이 비어 있거나 NULL 값을 가질 수 있을 때 해당 array/map 필드를 포함하는 행을 잃지 않으려면 LEFT JOIN이 선호돼요:
SELECT runner, checkpoint
FROM (
VALUES
('Joe', ARRAY[10, 20, 30, 42]),
('Roger', ARRAY[10]),
('Dave', ARRAY[]),
('Levi', NULL)
) AS marathon (runner, checkpoints)
LEFT JOIN UNNEST(checkpoints) AS t(checkpoint) ON TRUE;
runner | checkpoint
--------+------------
Joe | 10
Joe | 20
Joe | 30
Joe | 42
Roger | 10
Dave | NULL
Levi | NULL
(7 rows)
LEFT JOIN을 사용하는 경우 현재 구현이 지원하는 유일한 조건은 ON TRUE라는 점을 유의해요.
JSON_TABLE
JSON_TABLE은 JSON 데이터를 관계형 테이블 형식으로 변환해요. UNNEST와 LATERAL처럼 SELECT 문의 FROM 절에서 JSON_TABLE을 사용해요. 자세한 내용은 JSON_TABLE 문서를 참고해요.
결합 (Joins)
결합은 여러 관계의 데이터를 결합하게 해줘요.
CROSS JOIN
크로스 결합은 두 관계의 카테시안 곱(모든 조합)을 반환해요. 크로스 결합은 명시적 CROSS JOIN 문법이나 FROM 절에서 여러 관계를 지정해 나타낼 수 있어요.
다음 두 쿼리 모두 동등해요:
SELECT *
FROM nation
CROSS JOIN region;
SELECT *
FROM nation, region;
nation 테이블은 25행, region 테이블은 5행이므로 두 테이블의 크로스 결합은 125행을 만들어요:
SELECT n.name AS nation, r.name AS region
FROM nation AS n
CROSS JOIN region AS r
ORDER BY 1, 2;
nation | region
----------------+-------------
ALGERIA | AFRICA
ALGERIA | AMERICA
ALGERIA | ASIA
ALGERIA | EUROPE
ALGERIA | MIDDLE EAST
ARGENTINA | AFRICA
ARGENTINA | AMERICA
...
(125 rows)
LATERAL
FROM 절에 나타나는 서브쿼리는 키워드 LATERAL이 앞에 올 수 있어요. 이렇게 하면 앞선 FROM 항목이 제공하는 컬럼을 참조할 수 있어요.
LATERAL 결합은 FROM 목록의 최상위에 나타날 수도 있고, 괄호로 묶인 결합 트리 안 어디든 나타날 수 있어요. 후자의 경우 자신이 오른쪽에 있는 JOIN의 왼쪽에 있는 항목도 참조할 수 있어요.
FROM 항목이 LATERAL 교차 참조를 포함하면 평가는 다음과 같이 진행돼요: 크로스 참조 컬럼을 제공하는 FROM 항목의 각 행에 대해, LATERAL 항목이 그 행 집합의 컬럼 값으로 평가돼요. 결과 행은 그들이 계산된 행들과 평소처럼 결합돼요. 이것은 컬럼 소스 테이블의 행 집합마다 반복돼요.
LATERAL은 결합할 행을 계산하는 데 크로스 참조 컬럼이 필요할 때 주로 유용해요:
SELECT name, x, y
FROM nation
CROSS JOIN LATERAL (SELECT name || ' :-' AS x)
CROSS JOIN LATERAL (SELECT x || ')' AS y);
LATERAL이 FULL JOIN의 오른쪽에 나타나면 현재 구현이 지원하는 유일한 조건은 ON TRUE예요.
NEAREST
NEAREST는 결합 왼쪽의 각 행에 대해 FROM 관계에서 최대 한 행을 선택하는 관계예요.
명시적 CROSS JOIN, ON TRUE가 있는 INNER JOIN, ON TRUE가 있는 LEFT JOIN, 또는 암시적 콤마 결합의 오른쪽에서 NEAREST를 사용해요:
CROSS JOIN NEAREST (
FROM relation
[ WHERE condition ]
MATCH comparison
)
INNER JOIN NEAREST (
FROM relation
[ WHERE condition ]
MATCH comparison
) ON TRUE
relation,
NEAREST (
FROM relation
[ WHERE condition ]
MATCH comparison
)
LEFT JOIN NEAREST (
FROM relation
[ WHERE condition ]
MATCH comparison
) ON TRUE
MATCH 절은 필수예요. FROM 관계의 표현식 하나와 FROM이 아닌 표현식 하나를 <, <=, >, >= 연산자 중 하나로 사용하는 단일 비교여야 해요.
비교는 일치 방향과 후보 행의 정렬 순서를 모두 결정해요:
<와<=는FROM관계에서 일치 키가 다른 표현식보다 작거나 작거나 같은 가장 가까운 행을 선택해요.>와>=는FROM관계에서 일치 키가 다른 표현식보다 크거나 크거나 같은 가장 가까운 행을 선택해요.
선택적인 WHERE 절은 가장 가까운 행이 선택되기 전에 후보 행을 필터링해요.
NEAREST는 일치하는 후보 행으로 필터링하고, FROM 관계 일치 키로 정렬하고, 첫 행만 유지하는 lateral 서브쿼리의 축약으로 이해할 수 있어요. 예를 들어:
NEAREST ( FROM ... WHERE ... MATCH right_key <= left_key )는 다음과 동등해요:
LATERAL (
SELECT *
FROM ...
WHERE ...
AND right_key <= left_key
ORDER BY right_key DESC
FETCH FIRST 1 ROW ONLY
)
NEAREST ( FROM ... WHERE ... MATCH right_key >= left_key )는 다음과 동등해요:
LATERAL (
SELECT *
FROM ...
WHERE ...
AND right_key >= left_key
ORDER BY right_key ASC
FETCH FIRST 1 ROW ONLY
)
일반적으로 <와 <=는 FROM 관계 일치 키를 내림차순으로 정렬하고, >와 >=는 오름차순으로 정렬해요.
예를 들어 다음 쿼리는 각 거래를 같은 심볼의 가장 최근 quote와 일치시켜요:
SELECT trades.symbol, trades.ts, quotes.price
FROM trades
CROSS JOIN NEAREST (
FROM quotes
WHERE quotes.symbol = trades.symbol
MATCH quotes.ts <= trades.ts
);
가장 가까운 행이 없을 때 왼쪽 행을 보존하려면 LEFT JOIN NEAREST를 사용해요:
SELECT trades.symbol, quotes.price
FROM trades
LEFT JOIN NEAREST (
FROM quotes
WHERE quotes.symbol = trades.symbol
MATCH quotes.ts >= trades.ts
) ON TRUE;
현재 구현은 CROSS JOIN NEAREST (...), INNER JOIN NEAREST (...) ON TRUE, NEAREST (...)가 있는 암시적 콤마 결합, LEFT JOIN NEAREST (...) ON TRUE를 지원해요. JOIN USING, NATURAL JOIN, ON TRUE 외의 결합 조건은 NEAREST에서 지원되지 않아요.
컬럼 이름 한정
결합에서 두 관계가 같은 이름의 컬럼을 가지면, 컬럼 참조는 관계 별칭(관계에 별칭이 있으면) 또는 관계 이름으로 한정되어야 해요:
SELECT nation.name, region.name
FROM nation
CROSS JOIN region;
SELECT n.name, r.name
FROM nation AS n
CROSS JOIN region AS r;
SELECT n.name, r.name
FROM nation n
CROSS JOIN region r;
다음 쿼리는 Column 'name' is ambiguous 오류로 실패해요:
SELECT name
FROM nation
CROSS JOIN region;
서브쿼리
서브쿼리는 쿼리로 구성된 표현식이에요. 서브쿼리가 서브쿼리 밖의 컬럼을 참조하면 상관(correlated) 있어요. 논리적으로 서브쿼리는 둘러싸는 쿼리의 각 행에 대해 평가돼요. 따라서 참조된 컬럼은 서브쿼리의 단일 평가 동안 일정할 거예요.
Note
상관 서브쿼리 지원은 제한적이에요. 모든 표준 형식이 지원되지는 않아요.
EXISTS
EXISTS 술어는 서브쿼리가 어떤 행을 반환하는지 결정해요:
SELECT name
FROM nation
WHERE EXISTS (
SELECT *
FROM region
WHERE region.regionkey = nation.regionkey
);
IN
IN 술어는 서브쿼리가 만든 어떤 값이 제공된 표현식과 같은지 결정해요. IN의 결과는 null에 대한 표준 규칙을 따라요. 서브쿼리는 정확히 하나의 컬럼을 생성해야 해요:
SELECT name
FROM nation
WHERE regionkey IN (
SELECT regionkey
FROM region
WHERE name = 'AMERICA' OR name = 'AFRICA'
);
스칼라 서브쿼리
스칼라 서브쿼리는 0 또는 1행을 반환하는 비상관 서브쿼리예요. 서브쿼리가 1행 이상을 만들면 오류예요. 서브쿼리가 행을 만들지 않으면 반환되는 값은 NULL이에요:
SELECT name
FROM nation
WHERE regionkey = (SELECT max(regionkey) FROM region);
Note
현재 스칼라 서브쿼리에서 반환할 수 있는 컬럼은 단일 컬럼뿐이에요.
더 알아보기 (Learn more)
SELECT 안에서 쓰이는 MATCH_RECOGNIZE, PIVOT 같은 강력한 하위 절과, 데이터를 넣고 바꾸는 INSERT, UPDATE, DELETE를 함께 보면 Trino 쿼리 작성의 전체 그림이 잡혀요.