JOIN 절은 각 테이블에 공통으로 존재하는 값을 사용해 하나 이상의 테이블에서 컬럼을 결합하고, 그 결과로 새 테이블을 만듭니다. 이는 SQL을 지원하는 데이터베이스에서 흔히 사용되는 연산이며, 관계대수의 조인에 해당합니다. 하나의 테이블을 자기 자신과 조인하는 특수한 경우는 보통 “self-join”이라고 합니다.
구문
ON 절의 표현식과 USING 절의 컬럼을 “조인 키”라고 합니다. 별도로 명시하지 않는 한, JOIN은 “조인 키”가 일치하는 행들로 카테시안 곱을 생성하므로, 원본 테이블보다 훨씬 많은 행이 결과에 포함될 수 있습니다.
지원되는 JOIN 유형
| 유형 | 설명 |
|---|---|
INNER JOIN | 일치하는 행만 반환됩니다. |
LEFT OUTER JOIN | 일치하는 행과 함께 왼쪽 테이블의 비일치 행도 반환됩니다. |
RIGHT OUTER JOIN | 일치하는 행과 함께 오른쪽 테이블의 비일치 행도 반환됩니다. |
FULL OUTER JOIN | 일치하는 행과 함께 양쪽 테이블의 비일치 행도 반환됩니다. |
CROSS JOIN | 전체 테이블의 카테시안 곱을 생성하며, “조인 키”는 지정하지 않습니다. |
NATURAL JOIN | 두 테이블에서 이름이 같은 모든 컬럼을 기준으로 자동으로 조인하며, 각 공통 컬럼은 결과에 한 번만 나타납니다. INNER(기본값), LEFT, RIGHT, FULL 변형을 지원합니다. 컬럼 목록이 자동으로 도출된다는 점만 제외하면 JOIN ... USING (col1, col2, ...)와 동일합니다. |
- 유형을 지정하지 않은
JOIN은INNER를 의미합니다. OUTER키워드는 생략해도 됩니다.CROSS JOIN의 대체 구문으로,FROM절에서 여러 테이블을 쉼표로 구분해 지정할 수 있습니다.NATURAL JOIN에 일치하는 컬럼이 없으면CROSS JOIN처럼 동작합니다.
| 유형 | 설명 |
|---|---|
LEFT SEMI JOIN, RIGHT SEMI JOIN | 카테시안 곱을 생성하지 않고 “조인 키”에 대해 허용 목록을 적용합니다. |
LEFT ANTI JOIN, RIGHT ANTI JOIN | 카테시안 곱을 생성하지 않고 “조인 키”에 대해 거부 목록을 적용합니다. |
LEFT ANY JOIN, RIGHT ANY JOIN, INNER ANY JOIN | 표준 JOIN 유형의 카테시안 곱을 부분적으로(LEFT 및 RIGHT의 반대편) 또는 완전히(INNER 및 FULL) 비활성화합니다. |
ASOF JOIN, LEFT ASOF JOIN | 정확히 일치하지 않는 조건으로 시퀀스를 조인합니다. ASOF JOIN 사용법은 아래에 설명되어 있습니다. |
PASTE JOIN | 두 테이블을 가로로 이어 붙입니다. |
join_algorithm이
partial_merge로 설정된 경우, RIGHT JOIN 및 FULL JOIN은 ALL 엄격성에서만 지원됩니다(SEMI, ANTI, ANY, ASOF는 지원되지 않음).설정
join_default_strictness 설정으로 재정의할 수 있습니다.
ANY JOIN 작업에서 ClickHouse 서버의 동작은 any_join_distinct_right_table_keys 설정에 따라 달라집니다.
관련 항목
join_algorithmjoin_any_take_last_rowjoin_use_nullspartial_merge_join_rows_in_right_blocksjoin_on_disk_max_files_to_mergeany_join_distinct_right_table_keys
CROSS JOIN을 INNER JOIN으로 재작성하지 못할 때의 동작은 cross_to_inner_join_rewrite 설정으로 지정할 수 있습니다. 기본값은 1이며, 이 경우 조인은 계속 수행되지만 더 느려집니다. 오류를 발생시키려면 cross_to_inner_join_rewrite를 0으로 설정하고, 크로스 조인을 실행하지 않고 대신 모든 쉼표 조인/크로스 조인을 재작성하도록 강제하려면 2로 설정하십시오. 값이 2일 때 재작성에 실패하면 “Please, try to simplify WHERE section”라는 오류 메시지가 표시됩니다.
ON 절의 조건
ON 절에는 AND 및 OR 연산자로 결합된 여러 조건을 포함할 수 있습니다. 조인 키를 지정하는 조건은 다음 요구 사항을 충족해야 합니다.
- 왼쪽 테이블과 오른쪽 테이블을 모두 참조해야 합니다
- 동등 연산자를 사용해야 합니다
JOIN 유형에 따라 행이 결과에 포함될 수 있습니다. 같은 조건을 WHERE 절에 넣었는데 조건이 충족되지 않으면, 행은 항상 결과에서 필터링된다는 점에 유의하십시오.
ON 절 내부의 OR 연산자는 해시 조인 알고리즘을 사용해 동작합니다. 즉, JOIN의 조인 키가 있는 각 OR 인수마다 별도의 해시 테이블이 생성되므로, ON 절의 OR 표현식 수가 늘어날수록 메모리 활용량과 쿼리 실행 시간은 선형적으로 증가합니다.
조건이 서로 다른 테이블의 컬럼을 참조하는 경우 현재는 동등 연산자(
=)만 지원됩니다.table_1 및 table_2를 가정해 보겠습니다.
table_2에 대한 추가 조건이 포함된 쿼리:
Query
C이고 text 컬럼이 비어 있는 행도 포함된다는 점에 유의하십시오. 이는 OUTER join을 사용했기 때문에 결과에 포함된 것입니다.
Response
INNER 유형의 조인에서 여러 조건을 사용하는 쿼리:
Query
Response
INNER 유형과 OR 조건을 사용하는 쿼리:
Query
Response
INNER이며 조건에 OR와 AND를 사용하는 쿼리:
기본적으로 같지 않은 조건은 같은 테이블의 컬럼을 사용하는 경우에만 지원됩니다.
예를 들어
t1.a = t2.key AND t1.b > 0 AND t2.b > t2.c는 t1.b > 0이 t1의 컬럼만 사용하고 t2.b > t2.c는 t2의 컬럼만 사용하므로 지원됩니다.
하지만 t1.a = t2.key AND t1.b > t2.key와 같은 조건에 대해서는 실험적 지원을 사용해 볼 수 있습니다. 자세한 내용은 아래 섹션을 확인하십시오.Query
Response
서로 다른 테이블의 컬럼에 대한 부등 조건 JOIN
ALL/ANY/SEMI/ANTI INNER/LEFT/RIGHT/FULL JOIN을 지원합니다. 부등 조건은 hash 및 grace_hash 조인 알고리즘에서만 지원됩니다. 부등 조건은 join_use_nulls와 함께는 지원되지 않습니다.
예시
테이블 t1:
t2
JOIN 키의 NULL 값
NULL은 자기 자신을 포함해 어떤 값과도 같지 않습니다. 즉, 한 테이블에서 JOIN 키의 값이 NULL이면 다른 테이블의 NULL 값과 일치하지 않습니다.
예시
테이블 A:
B:
A 테이블의 Charlie가 있는 행과 B 테이블의 점수 88인 행은 JOIN 키에 NULL 값이 있기 때문에 결과에 포함되지 않습니다.
NULL 값을 서로 일치시키려면 JOIN 키를 비교할 때 isNotDistinctFrom 함수를 사용하십시오.
ASOF JOIN 사용
ASOF JOIN이 유용합니다.
이 JOIN 알고리즘을 사용하려면 테이블에 특수한 컬럼이 필요합니다. 이 컬럼은 다음 조건을 충족해야 합니다.
- 정렬된 시퀀스를 포함해야 합니다.
- 다음 타입 중 하나여야 합니다: Int, UInt, Float, Date, DateTime, Decimal.
hash조인 알고리즘에서는JOIN절의 유일한 컬럼일 수 없습니다.
ASOF JOIN ... ON:
SELECT count() FROM table_1 ASOF LEFT JOIN table_2 ON table_1.a == table_2.b AND table_2.t <= table_1.t.
가장 가까운 일치에 지원되는 조건: >, >=, <, <=.
구문 ASOF JOIN ... USING:
ASOF JOIN은 동등 조건으로 조인할 때 equi_columnX를 사용하고, table_1.asof_column >= table_2.asof_column 조건에서 가장 가까운 값을 기준으로 조인할 때는 asof_column을 사용합니다. asof_column 컬럼은 항상 USING 절의 마지막에 위치합니다.
예시로, 다음 테이블을 살펴보겠습니다.
ASOF JOIN은 table_1의 사용자 이벤트 타임스탬프를 기준으로, 가장 가까운 일치 조건에 맞으면서 table_1의 이벤트 타임스탬프와 가장 가까운 타임스탬프를 가진 table_2의 이벤트를 찾을 수 있습니다. 같은 타임스탬프 값이 있으면 그것이 가장 가까운 값으로 선택됩니다. 여기서 user_id 컬럼은 동등 조건 조인에 사용할 수 있고, ev_time 컬럼은 최근접 일치 조인에 사용할 수 있습니다. 이 예시에서는 event_1_1은 event_2_1과 조인할 수 있고 event_1_2는 event_2_3과 조인할 수 있지만, event_2_2는 조인할 수 없습니다.
ASOF JOIN은 hash 및 full_sorting_merge 조인 알고리즘에서만 지원됩니다.
Join 테이블 엔진에서는 지원되지 않습니다.PASTE JOIN 사용법
PASTE JOIN의 결과는 왼쪽 서브쿼리의 모든 컬럼 뒤에 오른쪽 서브쿼리의 모든 컬럼이 이어지는 테이블입니다.
행은 원본 테이블에서의 위치를 기준으로 대응됩니다(행 순서가 정의되어 있어야 합니다).
서브쿼리가 반환하는 행 수가 서로 다르면 초과된 행은 잘립니다.
예시:
분산 JOIN
- 일반
JOIN을 사용하면 쿼리가 원격 서버로 전송됩니다. 오른쪽 테이블을 만들기 위해 각 서버에서 서브쿼리가 실행되며, 이렇게 만들어진 테이블로 조인이 수행됩니다. 즉, 오른쪽 테이블은 각 서버에서 각각 구성됩니다. GLOBAL ... JOIN을 사용하면 먼저 요청 서버가 서브쿼리를 실행해 조인의 한쪽을 계산하고, 그 결과를 임시 테이블에 수집합니다. 그런 다음 이 임시 테이블이 각 원격 서버로 전달되고, 전송된 임시 데이터를 사용해 각 서버에서 쿼리가 실행됩니다.LEFT및INNER조인의 경우 오른쪽 테이블이 서브쿼리로 계산됩니다.RIGHT조인의 경우 유지되는 쪽은 오른쪽 테이블이며 세그먼트에서 읽어야 하므로, 대신 왼쪽 테이블이 계산됩니다.
GLOBAL을 사용할 때는 주의하십시오. 자세한 내용은 분산 서브쿼리 섹션을 참조하십시오.
암시적 타입 변환
INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN 쿼리는 “조인 키”에 대한 암시적 타입 변환을 지원합니다. 하지만 왼쪽 테이블과 오른쪽 테이블의 join 키를 하나의 타입으로 변환할 수 없으면 쿼리를 실행할 수 없습니다(예를 들어 UInt64와 Int64의 모든 값을 모두 표현할 수 있는 데이터 타입이 없거나, String과 Int32를 함께 표현할 수 있는 데이터 타입이 없는 경우).
예시
테이블 t_1을 살펴보겠습니다:
t_2 테이블:
사용 권장 사항
비어 있거나 NULL인 셀 처리
JOIN 키가 널 허용 필드인 경우, 키 중 하나 이상이 NULL 값인 행은 조인되지 않습니다.
구문
USING에 지정된 컬럼은 두 서브쿼리에서 이름이 같아야 하며, 나머지 컬럼은 서로 다른 이름이어야 합니다. 별칭을 사용해 서브쿼리의 컬럼 이름을 변경할 수 있습니다.
USING 절은 조인할 하나 이상의 컬럼을 지정하며, 이를 통해 해당 컬럼들이 서로 같은 값으로 매칭됩니다. 컬럼 목록은 괄호 없이 지정합니다. 더 복잡한 조인 조건은 지원되지 않습니다.
구문 제한 사항
SELECT 쿼리에서 여러 JOIN 절을 사용하는 경우:
*로 모든 컬럼을 가져오는 것은 테이블을 조인한 경우에만 가능하며, 서브쿼리에는 사용할 수 없습니다.PREWHERE절은 사용할 수 없습니다.USING절은 사용할 수 없습니다.
ON, WHERE, GROUP BY 절:
ON,WHERE,GROUP BY절에서는 임의의 표현식을 사용할 수 없지만,SELECT절에서 표현식을 정의한 뒤 별칭을 통해 해당 절에서 사용할 수 있습니다.
성능
JOIN을 실행할 때는 쿼리의 다른 단계와의 관계에서 실행 순서가 최적화되지 않습니다. join(오른쪽 테이블을 검색하는 작업)은 WHERE 필터링보다 먼저, 그리고 집계보다 먼저 실행됩니다.
동일한 JOIN으로 쿼리를 실행할 때마다 결과가 캐시되지 않으므로 서브쿼리가 매번 다시 실행됩니다. 이를 방지하려면 항상 RAM에 상주하는, 조인용으로 미리 준비된 배열인 특수한 Join 테이블 엔진을 사용하십시오.
경우에 따라 JOIN 대신 IN을 사용하는 편이 더 효율적입니다.
차원 테이블(예: 광고 캠페인 이름과 같은 차원 속성을 담고 있는 비교적 작은 테이블)과 조인하기 위해 JOIN이 필요한 경우, 모든 쿼리마다 오른쪽 테이블에 다시 접근해야 하므로 JOIN이 그리 편리하지 않을 수 있습니다. 이런 경우에는 JOIN 대신 “딕셔너리” 기능을 사용하는 것이 좋습니다. 자세한 내용은 Dictionaries 섹션을 참조하십시오.
메모리 제한
join_algorithm = 'auto'가 활성화되어 있으면, 메모리 사용량이 일정 임계값에 도달한 후 ClickHouse는 머지 조인 알고리즘으로 전환합니다. JOIN 알고리즘에 대한 설명은 join_algorithm 설정을 참조하십시오.
JOIN 연산의 메모리 사용량을 제한해야 하는 경우 다음 설정을 사용하십시오.
- max_rows_in_join — 해시 테이블의 행 수를 제한합니다.
- max_bytes_in_join — 해시 테이블의 크기를 제한합니다.