Skip to main content
ClickHouse Connect에는 core driver를 기반으로 한 clickhousedb SQLAlchemy 방언이 포함되어 있습니다. 동기 방언은 SQLAlchemy 1.4.40 이상(2.x 포함)을 지원하며, Core 쿼리, ClickHouse DDL, 리플렉션, 간단한 ORM 삽입에 중점을 둡니다. 비동기 방언을 사용하려면 SQLAlchemy 2.0.44 이상이 필요합니다. package extra를 사용하여 SQLAlchemy 의존성을 설치하십시오:

SQLAlchemy로 연결

clickhousedb:// 또는 clickhousedb+connect:// URL 형식 중 하나를 사용해 엔진을 생성합니다:

ClickHouse 세션 ID

동기(synchronous) 방언과 비동기(async) 방언 모두에서 풀의 각 연결은 기본적으로 고유한 ClickHouse 세션 ID를 생성합니다. 해당 연결의 요청이 동일한 ClickHouse 서버 프로세스에 도달하면 SET으로 변경한 설정과 임시 테이블이 그 연결에서 계속 유지됩니다. 이름이 지정된 세션의 상태와 동일 세션 중복 검사는 프로세스 내에서만 적용됩니다. 하나의 서버 프로세스에서 동일한 사용자와 세션 ID로 요청이 겹쳐 들어오면, 해당 요청은 큐에서 대기하지 않고 서버 코드 373과 함께 즉시 거부됩니다. 고정된 session_id를 구성하는 경우 pool_size=1, max_overflow=0을 사용하거나, 요청이 ClickHouse에 도달하기 전에 액세스를 직렬화하십시오. ClickHouse Cloud를 비롯한 로드 밸런싱 배포 환경에서는 동일한 세션 ID를 가진 요청이 서로 다른 서버로 전달될 수 있으므로, 고정된 session_id를 분산 상태나 분산 뮤텍스로 사용하지 마십시오.

비동기 연결

비동기 방언은 SQLAlchemy 2.0.44 이상이 필요하며, ClickHouse Connect의 네이티브 AsyncClient를 사용합니다. 필요한 의존성을 설치한 후 clickhousedb+async:// URL로 비동기 엔진을 생성하세요.
결과는 버퍼링됩니다. 서버 측 커서(cursor)가 비활성화되어 있으므로 AsyncConnection.stream()을 호출하면 InvalidRequestError가 발생합니다. SQLAlchemy는 AsyncSession.stream() 호출을 허용하지만, 방언(dialect)이 전체 결과를 버퍼링한 후에 반환합니다. 대용량 결과를 처리하려면 네이티브 AsyncClient 스트리밍 메서드를 사용하십시오. SQLAlchemy 연결이 체크아웃되어 있는 동안에는 원시(raw) 네이티브 클라이언트에 driver_connection으로 접근할 수 있습니다.
SQLAlchemy 연결과 해당 원시(raw) 클라이언트를 동시에 사용하지 마십시오. SQLAlchemy 연결 블록을 벗어나기 전에 원시 클라이언트 스트림을 완료하고, 연결이 풀로 반환된 후에는 원시 클라이언트를 계속 보관하지 마십시오. 빌려온 클라이언트의 수명 주기는 SQLAlchemy가 관리하므로 client.close()나 그 밖의 비공개 수명 주기 메서드를 절대 호출하지 마십시오. 연결 동시성은 SQLAlchemy의 풀이 관리합니다. 풀에 속한 각 연결은 하나의 네이티브 비동기 클라이언트를 소유하며, aiohttp 커넥터 제한은 기본적으로 전체 연결 1개, 호스트당 연결 1개로 설정됩니다. 이러한 전송 설정을 재정의하려면 URL 또는 connect_args에서 connector_limit, connector_limit_per_host, keepalive_timeout을 설정하십시오. pool_pre_ping=True를 사용하면 SQLAlchemy는 풀에서 연결을 체크아웃할 때 재사용되는 연결을 SELECT 1로 확인합니다. 현재 비동기 SQLAlchemy executemany 삽입은 드라이버의 Native 대량 삽입 프로토콜을 사용하지 않고 매개변수 세트마다 HTTP 요청을 하나씩 전송합니다. 따라서 이 방식은 소규모 배치에만 사용하십시오. 대량 데이터를 처리할 때는 앞서 설명한 풀 소유 driver_connection 접근 패턴을 사용하고, SQLAlchemy 연결을 풀에 반환하기 전에 client.insert()를 await하십시오. 비동기 executemany는 쿼리 매개변수 바인딩을 사용하므로, 시간대 정보가 없는(naive) datetime 값은 동기 Native executemany에서 사용하는 naive_datetime_insert 설정이 아니라 naive_datetime_binding을 따릅니다. 타입이 지정된 SQLAlchemy DateTime64 바인딩은 클라이언트 측 매개변수와 서버 측 매개변수 모두에서 소수 초를 보존합니다. 반면 exec_driver_sql()에 전달되는 타입이 지정되지 않은 %s 또는 %(name)s 매개변수는 naive datetime 값을 기본 방식대로 초 단위(소수점 이하 제외)로 포맷합니다. 시간대가 모호하지 않도록 하려면 시간대 정보가 포함된 값을 사용하십시오. Native 대량 삽입 방식이 필요하면 client.insert()를 사용하십시오. 비동기 엔진은 해당 엔진을 사용하는 이벤트 루프에서 생성하고 해제하십시오. 체크아웃한 모든 연결을 반환한 다음, 종료 시점 및 다른 이벤트 루프에서 엔진을 사용하기 전에 engine.dispose()를 await하십시오. 엔진을 소유한 루프가 이미 닫혔다면 재사용하기 전에 현재 루프에서 engine.dispose()를 await하십시오. 소유 루프가 닫힌 뒤에야 정리가 시작되면 aiohttp가 닫히지 않은 전송이 있다고 보고할 수 있으므로, 가능하면 엔진을 옮기기 전에 해제하십시오. 풀링된 비동기 엔진을 다른 이벤트 루프로 옮길 때 pool_pre_ping=True로 해제를 대신할 수는 없습니다. 루프에 바인딩된 연결을 남기지 않고 여러 이벤트 루프에서 하나의 엔진을 공유하려면 poolclass=NullPool을 설정하십시오. 연결이 아직 체크아웃된 상태에서 해제가 실행되면, 방언(dialect)은 해당 연결이 반환되거나 가비지 컬렉션될 때 연결을 닫습니다. 동기 코드에서는 engine.sync_engine.dispose()를 호출하지 마십시오. 이 경우 SQLAlchemy는 비동기 연결 정리를 await할 수 없으므로, 풀링된 전송을 닫지 못하고 오류만 로그로 남길 수 있습니다. URL 쿼리 매개변수에는 ClickHouse 설정, compression, query_limit, 타임아웃과 같은 ClickHouse Connect 클라이언트 옵션, 또는 ca_cert와 같은 HTTP/TLS 옵션을 포함할 수 있습니다. 필요한 경우 ClickHouse 설정 앞에 ch_를 붙이면 해당 설정을 강제로 서버 설정으로 처리할 수 있습니다(예: ch_http_max_field_name_size=99999). 사용 가능한 클라이언트 옵션은 연결 인수 및 설정을 참조하십시오. DDL 및 검사(inspection)와 같은 동기 SQLAlchemy 헬퍼는 AsyncConnection.run_sync()를 통해 실행하십시오:

쿼리별 설정

SQLAlchemy 실행 옵션을 통해 ClickHouse 설정을 전달할 수 있습니다. 설정은 engine, connection 또는 statement에 지정할 수 있습니다. 동일한 키가 있는 경우 statement 값이 connection 또는 engine 값보다 우선합니다.

쿼리별 읽기 포맷

SQLAlchemy 실행 옵션에서 query_formats를 사용해 엔진, 연결 또는 statement에 ClickHouse 읽기 포맷을 설정합니다. statement 포맷이 먼저 적용되므로 일치하는 연결 또는 엔진 키와 와일드카드보다 우선합니다.

오류 처리

SQLAlchemy 연결을 통해 드라이버에서 발생하는 오류는 clickhouse_connect.dbapi에서 내보내는 DB-API 클래스를 사용합니다. 이 클래스들은 clickhouse_connect.driver.exceptions의 대응 클래스와 동일한 클래스 객체이므로, SQLAlchemy는 이를 해당하는 sqlalchemy.exc.DBAPIError 하위 클래스로 래핑합니다. StreamFailureError는 OperationalError의 일종이므로 sqlalchemy.exc.OperationalError로 래핑됩니다. 호출자 측의 취소로 인해 명시적으로 호출한 AsyncConnection.invalidate()가 중단될 수 있다면, 무효화를 별도로 소유한 작업(task)에서 실행하고, 취소를 전파하기 전에 해당 작업이 완료될 때까지 기다리십시오. 이렇게 하면 SQLAlchemy가 연결 레코드 정리 작업을 끝까지 마칠 수 있습니다:
무효화 작업이 아직 실행 중일 때는 연결을 사용하지 마십시오. 직접 호출한 await connection.invalidate()가 취소되어 connection.invalidated가 여전히 false라면, 연결을 사용하거나 닫기 전에 connection.invalidate()를 다시 await하여 정리를 완료하십시오.

서버 측 매개변수

SQLAlchemy는 일반적으로 매개변수를 클라이언트 측에서 렌더링합니다. 엔진을 생성할 때 ClickHouse 서버 측 매개변수를 사용하도록 설정하십시오:
비동기 방언에서는 create_async_engine()에 동일한 server_side_params=True 인수를 지정하십시오. 이 모드에서는 바인딩된 모든 값이 ClickHouse와 호환되는 SQLAlchemy 타입이어야 합니다. 지원되는 IN 목록은 타입이 지정된 ClickHouse Array 매개변수로 변환됩니다. 컴파일러는 호환되는 타입을 추론할 수 없거나 바인딩을 안전하게 처리할 수 없는 경우 CompileError를 발생시킵니다. 바인딩 이름은 ClickHouse ASCII BareWord 이름이어야 합니다. core driver가 원시 바이너리 쿼리 매개변수용으로 예약하므로 $로 시작하고 끝나는 이름은 거부됩니다.

Core 쿼리

이 방언은 조인, 필터, 정렬, LIMIT 및 OFFSET, DISTINCT, 복합 SELECT를 포함한 SQLAlchemy Core SELECT 쿼리를 지원합니다. SQLAlchemy union(), intersect(), except_()는 ClickHouse UNION DISTINCT, INTERSECT DISTINCT, EXCEPT DISTINCT로 컴파일됩니다. 이에 대응하는 union_all(), intersect_all(), except_all()은 해당 ALL 연산자로 컴파일됩니다. 이 명시적 매핑은 ClickHouse 집합 연산의 기본값과 관계없이 SQLAlchemy의 중복 처리 의미를 유지합니다.
명시적인 WHERE 절이 필요한 경량 DELETE를 지원합니다:

리터럴 렌더링

SQLAlchemy가 literal_binds 또는 literal_execute를 통해 바인딩된 값을 인라인으로 삽입하면, 방언은 일반 String 타입과 ClickHouse 타입에 ClickHouse 인용 규칙을 사용합니다. 이는 TypeDecorator 래퍼와 with_variant() 선택에도 적용됩니다. 다른 바인딩된 매개변수가 남아 있어도 문자열 값의 퍼센트 기호와 백슬래시는 유지됩니다. ClickHouse DateTime64 SQLAlchemy 타입이 지정된 Python datetime 값은 클라이언트 측 매개변수와 인라인 리터럴에서 마이크로초가 유지되며, 널 허용 값과 배열 및 튜플 안에 중첩된 값도 마찬가지입니다. 정밀도는 ClickHouse가 선언된 값에 따라 적용합니다. Python datetime은 소수점 이하 최대 6자리까지 제공합니다. 일반 DateTime 값은 초 단위 포맷이 그대로 유지됩니다. text() SQL 문에서 소수 초를 보존하려면 bindparam("ts", type_=DateTime64(6))와 같이 타입을 명시적으로 지정하십시오. SQLAlchemy 컬럼 타입은 서버 스키마와 일치해야 합니다. 서버의 DateTime 컬럼에 DateTime64를 선언하면 소수 초가 렌더링되어, 삽입 시와 IN 비교에서 변환 오류가 발생할 수 있습니다. SQLAlchemy 2.x에서 ClickHouse Tuple 항목을 포함하는 일반 sqlalchemy.ARRAY 타입을 인라인 리터럴로 사용하려면 dimensions=1(중첩 배열이라면 그에 맞는 더 높은 차원 수)을 지정해야 SQLAlchemy가 각 튜플을 하나의 항목으로 처리합니다. SQLAlchemy 1.4는 일반 ARRAY 타입의 인라인 리터럴을 지원하지 않습니다. 이름이 지정된 datetime 매개변수를 재사용할 때 소수 부분을 보존하려면, 해당 매개변수가 사용되는 모든 위치에 호환되는 DateTime64 바인드 타입을 지정해야 합니다. 타입이 지정되지 않은 위치가 있거나 타입이 충돌하면 초 단위 포맷이 유지됩니다. 각 bindparam에 type_=DateTime64(6)를 설정하거나, 서로 다른 매개변수 이름을 사용하여 각각 적절한 타입을 지정하십시오.

JSON 타입 힌트

typed_paths 매핑으로 타입이 지정된 JSON 경로를 선언합니다. 경로 타입에는 ClickHouse SQLAlchemy 타입 클래스, 구성된 인스턴스, 또는 ClickHouse 타입 이름 문자열을 사용할 수 있습니다. 타입 이름 문자열은 Dynamic처럼 SQLAlchemy 생성자가 없는 타입도 지원하며, 복잡하게 구성된 타입 표현식에도 그대로 사용할 수 있습니다. 또한 이름이 지정된 Tuple에서 이름을 보존합니다. 타입 이름 문자열에는 Array(JSON(`child` UInt32))처럼 구성된 중첩 JSON 타입을 포함할 수 있습니다. 이러한 문자열에서 인식되는 ClickHouse 타입 이름은 대소문자를 구분하지 않으며, 정규 대소문자 표기로 출력됩니다. 문자열에는 완전한 타입 표현식이 하나만 포함되어야 합니다. 뒤에 남은 텍스트나 형식이 잘못된 중첩 JSON 인수는 거부됩니다. 빈 Tuple()은 ClickHouse가 JSON 컬럼의 Native 형식으로 이를 serialize할 수 없으므로 JSON의 타입이 지정된 경로로는 지원되지 않습니다. core driver는 쿼리 및 삽입 컬럼의 모든 위치에서 Tuple()을 지원하며, positional 또는 named tuple 내부에 중첩된 경우, Array 내부, 그리고 서버에서 활성화된 경우의 Nullable(Tuple()) 형태가 여기에 포함됩니다.
단순한 Python 식별자 경로의 경우, 키워드 인수는 typed_paths의 축약 표기입니다. 예를 들어 JSON(user_id=UInt32)와 같습니다. 점이 포함된 경로, 공백, 백틱, %2E로 인코딩된 점, 또는 생성자 옵션과 이름이 겹치는 경우에는 typed_paths를 사용하십시오. SKIP이라는 이름의 타입이 지정된 경로도 이 매핑을 통해 지원됩니다. typed_paths의 키와 skip_paths의 값은 디코딩된 이름입니다. 앞뒤에 오는 백틱과 큰따옴표는 미리 적용된 SQL 인용 부호가 아니라 리터럴 경로 문자로 처리됩니다. 원시 타입 문자열 내부에서는 백틱과 큰따옴표가 ClickHouse 식별자 구문으로 해석됩니다. 타입이 지정된 경로는 최대 1000개까지 구성할 수 있습니다. max_dynamic_paths는 0에서 10000까지, max_dynamic_types는 0에서 254까지 허용합니다. 이 범위는 중첩된 원시 JSON 타입 문자열 내부에도 동일하게 적용됩니다. 명시적인 서버 기본값인 1024와 32는 생성된 DDL에서 생략됩니다. 일반 스킵 경로는 중복이 제거됩니다. 정규식 문자열은 ClickHouse가 RE2 구문을 사용하므로 Python에서 검사하지 않습니다. 중복된 정규식은 그대로 유지됩니다. ClickHouse가 SKIP REGEXP를 위해 해당 토큰을 예약해 두었으므로, 일반 스킵 경로의 이름을 정확히 REGEXP로 지정할 수 없습니다. 반면 REGEXP_foo와 같은 이름은 유효합니다. 원시 JSON 타입 문자열에서 일반 SKIP 피연산자는 하나의 ClickHouse 식별자이거나 점으로 구분된 복합 식별자여야 합니다. 인용되지 않은 복합 식별자는 REGEXP로 시작할 수 없으며, 첫 번째 구성 요소가 경로 데이터라면 해당 부분을 인용 부호로 묶으십시오. SKIP REGEXP에는 작은따옴표로 묶인 문자열 리터럴이 하나 있어야 합니다. 식별자 부분에 공백이나 구두점이 포함된 경우 백틱이나 큰따옴표로 묶으십시오. 원시 JSON 타입 힌트는 Variant(...)를 지원하며, 단독 Variant에는 공개 SQLAlchemy 생성자가 없습니다. Variant 멤버는 ClickHouse가 사용하는 것과 동일한 정규 이름을 기준으로 정렬되고 중복이 제거됩니다. 생성자는 ClickHouse가 반환하는 것과 동일한 정규 형식으로 인수를 정렬합니다. 리플렉션된 타입, SQLAlchemy 타입 복사본, Alembic 자동 생성은 이 구성을 그대로 보존합니다.

JSON 서브컬럼

ClickHouse JSON으로 선언되었거나 반영된 컬럼에서 스토리지 기반 서브컬럼 경로의 세그먼트를 한 번에 하나씩 선택하려면 대괄호를 사용합니다:
payload["severity"]는 ClickHouse의 점 표기 식별자 구문으로 컴파일됩니다. 각 부분은 개별적으로 인용되며, 예를 들어 `events`.`payload`.`severity`와 같습니다. ClickHouse에 저장된 JSON 하위 컬럼을 읽으며 getSubcolumn은 호출하지 않습니다. 각 경로 세그먼트에 대해 [] 또는 .subcolumn()을 한 번씩 연결하십시오. 각 세그먼트는 비어 있지 않은 문자열이어야 합니다. .subcolumn()에 type_을 전달하면 점 표기 경로가 SQL CAST로 감싸지고 해당 타입이 SQLAlchemy 표현식에 할당됩니다. type_이 없으면 .subcolumn("segment")는 ["segment"]와 동일하게 동작합니다. 타입이 지정되지 않은 경로는 ClickHouse의 Dynamic 타입을 갖습니다. ClickHouse에서는 Dynamic 값을 ORDER BY 또는 GROUP BY에 직접 사용할 수 없습니다. 이러한 위치에서 하위 컬럼을 사용하려면 type_을 전달하십시오. 정적으로 타입이 지정된 코드에서는 clickhouse_connect.cc_sqlalchemy에서 json_subcolumn을 가져오십시오. 이 도우미 함수도 한 번에 하나의 세그먼트를 받으며 type_의 Python 결과 타입을 유지합니다:
이 예시에서 타입 검사기는 request_id를 ColumnElement[int]로 봅니다. 공백이나 백틱이 포함된 이름을 비롯해 각 세그먼트는 각각 따옴표로 묶습니다. 백틱을 사용해도 ClickHouse JSON 경로 처리에서 점이 리터럴로 해석되지는 않습니다. json_type_escape_dots_in_keys가 활성화된 경우 키의 리터럴 점에는 ClickHouse의 %2E 인코딩을 사용하십시오. a.b라는 키에는 payload["a%2Eb"]로 접근하고, payload["a.b"]는 사용하지 마십시오.

ClickHouse 쿼리 확장 기능

정적 타입 검사기가 타입이 지정된 ClickHouse 메서드를 인식할 수 있도록 clickhouse_connect.cc_sqlalchemy에서 select를 가져오십시오. 표준 sqlalchemy.select도 런타임에 이러한 메서드를 제공합니다.
ClickHouse Select 메서드는 다음과 같습니다: SQLAlchemy의 Select.with_hint()는 테이블 힌트 API입니다. ClickHouse 방언은 테이블 힌트를 렌더링하지 않습니다. 적용되는 와일드카드 또는 clickhousedb 힌트는 SAWarning을 발생시키며 생성된 SQL은 변경되지 않습니다. 해당 ClickHouse 절에는 final(), sample(), prewhere() 또는 limit_by()를 사용하십시오. Select.with_statement_hint()는 원시 후행 지시문 API입니다. ClickHouse 전용 유효성 검사 없이 제공된 텍스트를 SELECT 끝에 추가합니다. SETTINGS max_threads=1과 같은 신뢰할 수 있는 정적 SQL에 계속 사용할 수 있습니다:
ClickHouse 설정에는 드라이버가 SQL 텍스트와 별도로 설정을 처리할 수 있도록 실행 옵션을 사용하는 것이 좋습니다:
예를 들어, ClickHouse GLOBAL ANY LEFT JOIN은 사용자 정의 FromClause를 중첩하지 않고 체이닝할 수 있습니다:
ClickHouse 고차 함수에서는 명시적 Lambda 구문을 사용하십시오:
표준 SQLAlchemy values() 구문은 공통 테이블 표현식(CTE)에서 사용할 때를 포함해 ClickHouse의 VALUES 테이블 함수 구문으로 컴파일됩니다. CTE 형식에는 Values.cte()가 추가된 SQLAlchemy 2.0.42 이상이 필요합니다.

구체화된 CTE

기본적으로 ClickHouse는 공통 테이블 표현식(CTE)을 인라인하므로, CTE를 두 번 이상 참조하면 참조할 때마다 본문이 한 번씩 실행됩니다. .cte()에 materialized=True를 전달하면 WITH <name> AS MATERIALIZED (...)가 생성되어 본문을 한 번만 계산합니다:
서버는 키워드가 있고 enable_materialized_cte=1로 설정되어 있으며 분석기가 활성화된 경우에만 CTE를 구체화합니다. 쿼리별 설정에 나온 대로 statement, connection 또는 engine에 enable_materialized_cte를 설정하십시오. 이 기능을 지원하는 모든 서버에서는 분석기가 기본적으로 활성화되므로 enable_analyzer=1을 명시적으로 설정하는 것은 방어적 조치입니다. enable_materialized_cte는 Experimental ClickHouse 설정입니다. enable_materialized_cte=0 또는 enable_analyzer=0이면 쿼리는 성공하고 동일한 행을 반환합니다. ClickHouse는 아무런 알림 없이 MATERIALIZED를 무시하고 CTE를 다시 인라인 처리하므로, 설정을 빠뜨리면 오류 없이 성능이 저하됩니다. Materialized CTE를 사용하려면 ClickHouse 26.3 이상이 필요합니다. 이전 서버에서는 해당 키워드를 구문 오류로 처리합니다. 표준 sqlalchemy.select로 작성한 statement에는 모듈 수준의 cte()를 대신 사용하십시오. 이 함수는 첫 번째 인수로 statement를 받고, 그 외에는 Select.cte()와 동일하게 동작합니다:
이 키워드는 ClickHouse 방언에서만 렌더링되므로, 다른 backend와 공유되는 statement는 해당 backend에서 변경 없이 컴파일됩니다. ClickHouse는 재귀적 구체화된 CTE를 지원하지 않습니다. recursive=True와 materialized=True가 모두 설정되면 SQLAlchemy 헬퍼에서 ValueError를 발생시킵니다.

DDL 및 리플렉션

ClickHouse Connect는 ClickHouse 데이터 타입, 테이블 엔진, 딕셔너리 구문, 데이터베이스 DDL 및 테이블 리플렉션을 제공합니다. 단독 Variant 컬럼은 SQLAlchemy 내부 타입을 통해 리플렉션되며, Alembic 자동 생성은 반복적인 타입 변경 없이 해당 컬럼의 정규 원시 타입 이름을 유지합니다. Geometry 및 MultiPoint 컬럼은 공개 SQLAlchemy 타입으로 리플렉션됩니다.
리플렉션된 컬럼에는 DEFAULT 표현식용 server_default와, 있는 경우 clickhouse_codec, clickhouse_ttl, clickhouse_materialized, clickhouse_alias 같은 방언별 속성이 포함됩니다. DEFAULT, MATERIALIZED, ALIAS, TTL 절의 문자열 값에는 ClickHouse 문자열 이스케이프가 사용됩니다. Alembic에서 생성된 주석을 포함하여 테이블, 딕셔너리, 컬럼 주석에도 동일한 이스케이프가 적용됩니다. order_by, partition_by, primary_key, sample_by, ttl 등의 MergeTree 키 인수는 일반 문자열은 물론 SQLAlchemy 컬럼과 SQL 표현식도 받을 수 있습니다. Memory(), Log(), StripeLog(), TinyLog(), Null(), Set()은 인수 없이 사용할 수 있으며, Alembic 자동 생성을 거쳐도 변경 없이 그대로 유지됩니다. 기존 딕셔너리 인수도 계속 지원됩니다. 엔진 설정을 지정하려면 settings={...}를 사용하십시오. SummingMergeTree 및 ReplicatedSummingMergeTree는 키워드 전용 선택 인수인 columns를 지원합니다. 기존 위치 인수의 의미는 그대로 유지되므로 SummingMergeTree("id")는 여전히 ORDER BY id를 설정합니다.
문자열, SQLAlchemy 컬럼, 매핑된 컬럼 속성 또는 이러한 값으로 구성된 비어 있지 않은 리스트나 튜플을 전달하세요. 리스트 및 튜플의 문자열 항목은 식별자로 인용됩니다. 스칼라 문자열은 "delta" 또는 "(delta, n_tx)"와 같은 원시 SQL로 전달됩니다. 서버는 이러한 컬럼을 식별자로 지정해야 합니다. columns를 생략하면 ClickHouse가 합산할 컬럼을 선택합니다. 리플렉션과 Alembic 자동 생성은 명시적으로 지정된 컬럼 목록을 유지합니다.

삽입 및 기본 ORM 사용

Core 삽입과 간단한 ORM 모델이 지원됩니다. 동기 방언에서는 호환되는 대량 데이터 처리에 Core executemany 삽입을 우선적으로 사용하십시오. 비동기 대량 삽입에는 비동기 연결에 설명된 네이티브 AsyncClient.insert() 경로를 사용하십시오.
동기 방언에서는 SQLAlchemy 컴파일러가 생성한 일반 Core executemany 삽입이 한 번의 Native 대량 삽입(bulk insert)으로 처리됩니다. 비동기 executemany는 비동기 연결에 설명된 대로 매개변수 집합마다 요청을 하나씩 전송합니다. Raw SQL, 그리고 표현식이나 안전하게 라우팅할 수 없는 기타 의미 체계를 포함하는 삽입은 원래 SQL을 그대로 유지한 채 매개변수 집합마다 한 번씩 실행됩니다. 이때 뒤쪽 매개변수 집합이 실패하더라도 앞선 매개변수 집합으로 기록된 행은 커밋된 상태로 남습니다. 명시적인 다중 행 insert(events).values([...]) SQL 문에는 딕셔너리 형태의 행, 테이블 컬럼 순서를 따르는 튜플, 행별 SQL 표현식을 사용할 수 있습니다. Pandas to_sql(method="multi")도 이 형식을 사용합니다. 이 방식은 행을 정상적으로 삽입하지만, 텍스트 형식의 INSERT 문은 DB-API 커서를 통해 행 수를 0으로 보고하므로 반환값은 0입니다. SQLAlchemy는 첫 번째 행을 기준으로 컬럼 목록을 결정합니다. 따라서 이후 행에 추가로 포함된 딕셔너리 키나 선택된 컬럼 목록을 벗어나는 튜플 값은 무시됩니다. 반대로 이후 행에 선택된 컬럼의 값이 없으면 컴파일이 실패합니다. 모든 행에 동일한 컬럼을 지정하십시오. ClickHouse 26.4 이상의 기본 HTTP 폼 제한에서는 server_side_params=True가 소규모 명시적 배치에만 적합하며, 다른 필드를 위한 여유분을 고려하면 바인드 값은 약 1000개 미만이어야 합니다. 이 상한은 서버 구성으로 높일 수 있습니다. 동기 방언으로 대규모 일반 배치를 처리하려면 드라이버가 Native 대량 삽입 경로를 사용할 수 있도록 행을 execute()의 두 번째 인수로 전달하십시오. 비동기 방식으로 대량 데이터를 처리하려면 네이티브 AsyncClient.insert() 메서드를 await하십시오.

Alembic 마이그레이션

ClickHouse Connect에는 ClickHouse 스키마 마이그레이션을 위한 Alembic 통합 기능이 포함되어 있습니다. 다음과 같이 설치하십시오:
비동기 방언으로 마이그레이션을 수행하려면 두 extras를 모두 설치하십시오:
async Alembic 프로젝트를 생성한 다음, 자동 생성된 환경 파일을 ClickHouse를 지원하는 예시로 교체하십시오:
생성된 alembic.ini는 script_location = %(here)s/alembic을 사용합니다. 마이그레이션 디렉터리 이름이 alembic이면 이 설정을 그대로 유지하고, 그렇지 않으면 alembic init에 전달한 디렉터리로 변경하십시오. alembic/env.py를 저장소에 포함된 비동기 Alembic env.py 예시로 교체한 다음, alembic.ini에서 sqlalchemy.url을 설정하십시오. 방언 통합을 등록하려면 Alembic의 env.py에서 clickhouse_connect.cc_sqlalchemy.alembic을 import하십시오. 자동 생성은 테이블 생성 및 제거, 컬럼 추가/수정/삭제, 기본값, 주석을 포함한 일반적인 테이블 스키마 변경을 지원합니다. 테이블 및 컬럼 이름 변경은 수동 작업을 사용하십시오. 생성된 모든 migration은 적용하기 전에 반드시 검토하십시오. Alembic의 마이그레이션 함수는 여전히 동기 방식으로 동작합니다. 비동기 환경에서는 AsyncEngine을 생성하고 AsyncConnection을 연 다음, 동기 마이그레이션 함수를 await connection.run_sync(...)에 전달합니다. 오프라인 마이그레이션은 context.configure(url=..., literal_binds=True, dialect_opts={"paramstyle": "named"})를 직접 호출하며 엔진을 생성하지 않습니다. 저장소에 포함된 비동기 Alembic env.py 예시에는 두 경로가 모두 포함되어 있으며, Alembic의 표준 sqlalchemy.url 구성을 통해 연결 URL을 읽습니다. 또한 include_object, make_include_name(...), clickhouse_writer, version_table 등 단계별 예시에서 사용한 ClickHouse Alembic 훅과 옵션을 그대로 유지합니다. 비동기 마이그레이션을 실행하거나 정리할 때 engine.sync_engine을 사용하지 마십시오. ClickHouse 전용 op.* 헬퍼는 다음을 지원합니다.
  • 추가, 구체화, 삭제 작업을 포함한 데이터 스키핑 인덱스
  • 추가, 구체화, 삭제 작업을 포함한 프로젝션
  • MergeTree 테이블 설정 수정 및 재설정
  • materialized view 생성 및 제거
  • 딕셔너리 생성, 제거 및 다시 로드
ClickHouse 데이터 스키핑 인덱스는 SQLAlchemy 인덱스가 아닙니다. 부분적이거나 잘못된 DDL을 방지하기 위해 Index, Column(index=True), op.create_index, op.drop_index는 허용되지 않습니다. op.add_clickhouse_index와 op.drop_clickhouse_index를 사용하십시오. 전체 Alembic 예시는 여기에서 확인할 수 있습니다. clickhouse-sqlalchemy에서 마이그레이션하는 사용자는 마이그레이션 가이드도 읽어보시기 바랍니다.

범위 및 제한 사항

  • ClickHouse는 이 HTTP 방언을 통해 전통적인 트랜잭션을 제공하지 않습니다. engine.begin() 및 Session.commit()은 Python 측 작업을 정리하지만, commit과 rollback은 서버에서는 실제로 아무 작업도 수행하지 않습니다.
  • UPDATE, 2단계 트랜잭션, 시퀀스, RETURNING, 고급 격리 수준은 이 방언에서 구현되지 않았습니다. 필요할 경우 서버 측 뮤테이션에는 명시적으로 ClickHouse SQL을 사용하십시오.
  • Column(..., primary_key=True)는 SQLAlchemy 객체 아이덴티티를 제공합니다. 하지만 서버 측 고유 제약을 생성하지는 않습니다. 정렬 및 선택적 프라이머리 키 표현식은 테이블 엔진을 통해 정의하십시오.
  • ClickHouse는 이러한 제약을 강제하지 않으므로 전통적인 외래 키, 고유 제약, 표준 인덱스 메타데이터는 사용할 수 없습니다.
  • ORM 릴레이션 관리, unit-of-work 방식의 업데이트, 캐스케이딩, eager 또는 lazy 릴레이션 loading은 지원되는 ORM 범위에 포함되지 않습니다.
마지막 수정일 2026년 9월 26일