Hologres is a fully managed, PostgreSQL-compatible real-time data warehouse. This guide walks you through migrating schemas, data, and queries from a self-managed ClickHouse instance to Hologres.
Prerequisites
Before you begin, ensure that you have:
A running Hologres instance. See Purchase a Hologres instance
A ClickHouse instance with the ClickHouse client installed. Download: ClickHouse-Client. For installation details, see Getting started
A psql client connected to your Hologres instance. See PSQL client
Background
ClickHouse is a column-oriented database management system (DBMS) designed for online analytical processing (OLAP). Hologres extends that capability with PostgreSQL compatibility, high-queries-per-second (QPS) point queries, full multi-table JOIN support, and a cost-based optimizer (CBO). The table below summarizes the key differences.
| Category | Criteria | ClickHouse | Hologres |
|---|---|---|---|
| Product | Purpose | Network traffic analysis | General-purpose real-time data warehouse: data analytics and online services |
| Write | Storage | Column store | Column store and row store |
| Write visibility | Second-level (requires client-side batching; distributed table writes depend on shard replication completion) | Millisecond-level (adaptive write batching; immediately queryable after write) | |
| Write performance | High | Very high | |
| Detail storage | Supported | Supported | |
| Primary key | Not a true database primary key (no uniqueness constraint; used only for indexing and aggregation) | Standard database primary key with uniqueness constraint support | |
| Updatable | Incomplete (does not support high-QPS updates based on primary keys) | Fully supported (supports high-QPS updates based on primary keys) | |
| Real-time write | Append | Append / insert or ignore / insert or replace / update | |
| Index | primary key, minmax, ngram, token, bloom filter | bitmap, dictionary, segment, primary, clustering (automatically creates minmax, bloom filter, and ngram indexes) | |
| Query | Optimizer | Rule-based optimizer (RBO) | Cost-based optimizer (CBO) |
| Federated query | Supported (HDFS and Kafka) | Supported (FDW reads MaxCompute and Hive directly) | |
| Pre-aggregation | Supported (via MergeTree) | Supported (via stored procedures and scheduled jobs) | |
| High-QPS point query | Not supported | Supported; QPS exceeds tens of millions | |
| Complex single-table query | Good | Good | |
| Multi-table JOIN | Poor | Good | |
| SQL syntax | Custom syntax | PostgreSQL-compatible | |
| Window functions | Not supported | Supported | |
| Transaction | ACID | No (eventual consistency; no immediate queryability guarantee) | Limited (DDL transactions, single-row transactions, snapshot-based visibility) |
| Disaster recovery | Backup | Replication via ZooKeeper + ClickHouse | Logical replication via Binlog; physical replication via underlying mechanisms |
| Advanced features | Binlog | None | Supported |
| Vector search | Supported (ClickHouse 22.8+) | Supported | |
| Spatial data | Not supported | Supported | |
| Security | Custom permissions | PostgreSQL-compatible permission model with IP allowlists and data masking | |
| Storage-compute separation | Not separated; limited by single-node capacity | Separated; nearly unlimited storage capacity | |
| Availability | Manual failover | Automatic failover recovery | |
| O&M | Complex (manual shard distribution) | Fully managed | |
| Ecosystem | Data ingestion | Kafka, Flink, Spark | Flink, Spark, JDBC, DataX |
| BI tools | Limited (Tableau, Superset) | PostgreSQL ecosystem; supports 100+ mainstream BI tools |
Data type mapping
ClickHouse and Hologres use different type systems. The tables below map each ClickHouse type to its Hologres equivalent and highlight pitfalls to watch for during conversion.
Numeric types
ClickHouse offers signed and unsigned integer types at multiple precisions. Hologres uses the standard SQL integer hierarchy. Key difference: ClickHouse has no native Boolean type; Hologres supports BOOLEAN natively.
| ClickHouse type | Hologres type | Notes |
|---|---|---|
| Int8 | SMALLINT | Single-byte INT is not supported; use SMALLINT |
| Int16 | SMALLINT | |
| Int32 | INT | |
| Int64 | BIGINT | |
| UInt8 | INT | |
| UInt16 | INT | |
| UInt32 | BIGINT | |
| UInt64 | BIGINT | UInt64 values near the max (18,446,744,073,709,551,615) exceed BIGINT range; validate data before migrating |
| Float32 | FLOAT | |
| Float64 | DOUBLE PRECISION | |
| Decimal(P, S) | DECIMAL | |
| Decimal32(S) | DECIMAL | |
| Decimal64(S) | DECIMAL | |
| Decimal128(S) | DECIMAL | |
| Boolean | BOOLEAN | ClickHouse has no Boolean type; use UInt8 in ClickHouse to represent boolean values |
String and character types
ClickHouse stores all string data as raw bytes with a String type. Hologres maps this to TEXT. LowCardinality is a ClickHouse storage optimization with no direct Hologres equivalent — after migration, enable dictionary encoding manually on the corresponding columns.
| ClickHouse type | Hologres type | Notes |
|---|---|---|
| String | TEXT | |
| FixString(N) | TEXT | Fixed-length strings become variable-length; no Hologres fixed-length string equivalent |
| LowCardinality(T) | TEXT (or underlying type) | Not supported as a native type. After migration, enable dictionary encoding manually: CALL set_table_property('table', 'dictionary_encoding_columns', 'col'); |
Date and time types
ClickHouse uses Date and various DateTime variants. Hologres maps all timezone-aware types to TIMESTAMPTZ.
| ClickHouse type | Hologres type | Notes |
|---|---|---|
| Date | Date | |
| DateTime | TIMESTAMPTZ | |
| DateTime(timezone) | TIMESTAMPTZ | |
| DateTime64 | TIMESTAMPTZ |
Complex and other types
Nested structures in ClickHouse (such as Nested and Tuple) are flattened to arrays in Hologres. For Binary data, choose the Hologres type based on your use case.
| ClickHouse type | Hologres type | Notes |
|---|---|---|
| UUID | UUID | |
| Enum | TEXT | Not supported as a native type; use TEXT instead |
| Nested, Tuple, Array | Array | Nested types are flattened to arrays |
| Binary | BIT(n), VARBIT(n), BYTEA, CHAR(n) | Choose based on use case |
Migrate schemas
Schema migration converts ClickHouse data definition language (DDL) statements to Hologres (PostgreSQL-compatible) DDL. Complete the following steps.
Step 1: List all databases
Run the following command in the ClickHouse client to list databases in the source instance.
clickhouse-client --host="<host>" --port="<port>" --user="<username>" --password="<password>" \
--query="SHOW databases" > database.listThe system database appears in the results but does not need to be migrated. Filter it out.| Parameter | Description |
|---|---|
<host> | Address of the source ClickHouse instance |
<port> | Port of the source ClickHouse instance |
<username> | Account with DML read/write, settings, and DDL permissions |
<password> | Password for the account |
Step 2: List all tables
Run the following command in the ClickHouse client to list tables in a specific database.
clickhouse-client --host="<host>" --port="<port>" --user="<username>" --password="<password>" \
--query="SHOW tables from <database_name>" > table.listTables starting with .inner. are internal tables for materialized views and do not need to be migrated. Filter them out.To list all databases and tables at once, query the system.tables metadata table:
SELECT DISTINCT database, name FROM system.tables WHERE database != 'system';Step 3: Export table DDL
Export the DDL for each table you want to migrate.
clickhouse-client --host="<host>" --port="<port>" --user="<username>" --password="<password>" \
--query="SHOW CREATE TABLE <database_name>.<table_name>" > table.sqlAlternatively, query system.tables directly to inspect DDL and metadata for all non-distributed tables:
SELECT * FROM system.tables
WHERE database = '<database_name>' AND engine != 'Distributed';The following table explains how each system.tables field maps to Hologres concepts.
| Field | Mapping rule |
|---|---|
database | ClickHouse databases map to schemas in Hologres. CREATE DATABASE "<db>"; becomes CREATE SCHEMA "<schema>"; |
name | Table name; no changes needed |
engine | Hologres has no Local/Distributed table distinction. Filter out rows where engine = 'Distributed' |
is_temporary | Temporary tables do not need to be migrated. Hologres does not support temporary tables |
data_paths, metadata_path, metadata_modification_time | Ignore |
dependencies_database, dependencies_table | Views with dependencies must be created before their base tables. Hologres does not support materialized views |
create_table_query | Source DDL; convert to Hologres DDL (PostgreSQL syntax) |
engine_full | Ignore |
partition_key | Maps to the partition key in Hologres. If partition_key is col1, add PARTITION BY LIST (col1) to the Hologres DDL |
sorting_key | Maps to segment key and clustering key indexes in Hologres |
primary_key | Maps to the PRIMARY KEY clause in Hologres DDL |
sampling_key | Hologres does not support sampling; ignore |
storage_policy | Ignore |
Step 4: Convert ClickHouse DDL to Hologres DDL
Apply the type mappings and field conversion rules to convert each table's DDL. The following examples use TPC-H benchmark tables.
Example: lineitem table
ClickHouse DDL:
-- lineitem on ClickHouse
CREATE TABLE lineitem_local ON CLUSTER default(
l_orderkey UInt64,
l_partkey UInt32,
l_suppkey UInt32,
l_linenumber UInt32,
l_quantity decimal(15,2),
l_extendedprice decimal(15,2),
l_discount decimal(15,2),
l_tax decimal(15,2),
l_returnflag LowCardinality(String),
l_linestatus LowCardinality(String),
l_shipdate Date,
l_commitdate Date,
l_receiptdate Date,
l_shipinstruct LowCardinality(String),
l_shipmode LowCardinality(String),
l_comment LowCardinality(String)
) ENGINE = MergeTree
PARTITION BY toYear(l_shipdate)
ORDER BY (l_orderkey, l_linenumber);
CREATE TABLE lineitem ON CLUSTER default AS lineitem_local
ENGINE = Distributed(default, default, lineitem_local, l_orderkey);Converted Hologres DDL:
-- lineitem on Hologres
-- Create a table group with 32 shards
CALL hg_create_table_group ('lineitem_tg', 32);
BEGIN;
CREATE TABLE LINEITEM (
L_ORDERKEY BIGINT NOT NULL,
L_PARTKEY INT NOT NULL,
L_SUPPKEY INT NOT NULL,
L_LINENUMBER INT NOT NULL,
L_QUANTITY DECIMAL(15,2) NOT NULL,
L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL,
L_DISCOUNT DECIMAL(15,2) NOT NULL,
L_TAX DECIMAL(15,2) NOT NULL,
L_RETURNFLAG TEXT NOT NULL,
L_LINESTATUS TEXT NOT NULL,
L_SHIPDATE TIMESTAMPTZ NOT NULL,
L_COMMITDATE TIMESTAMPTZ NOT NULL,
L_RECEIPTDATE TIMESTAMPTZ NOT NULL,
L_SHIPINSTRUCT TEXT NOT NULL,
L_SHIPMODE TEXT NOT NULL,
L_COMMENT TEXT NOT NULL,
PRIMARY KEY (L_ORDERKEY, L_LINENUMBER)
);
-- sorting_key (l_orderkey, l_linenumber) maps to clustering key and segment key
CALL set_table_property('LINEITEM', 'clustering_key', 'L_SHIPDATE,L_ORDERKEY');
CALL set_table_property('LINEITEM', 'segment_key', 'L_SHIPDATE');
CALL set_table_property('LINEITEM', 'table_group', 'lineitem_tg');
CALL set_table_property('LINEITEM', 'distribution_key', 'L_ORDERKEY');
-- LowCardinality columns: enable bitmap and dictionary encoding
CALL set_table_property('LINEITEM', 'bitmap_columns', 'L_RETURNFLAG,L_LINESTATUS,L_SHIPINSTRUCT,L_SHIPMODE,L_COMMENT');
CALL set_table_property('LINEITEM', 'dictionary_encoding_columns', 'L_RETURNFLAG,L_LINESTATUS,L_SHIPINSTRUCT,L_SHIPMODE,L_COMMENT');
CALL set_table_property('LINEITEM', 'time_to_live_in_seconds', '31536000');
COMMIT;Example: customer table
ClickHouse DDL:
-- customer on ClickHouse
CREATE TABLE customer_local ON CLUSTER default(
c_custkey UInt32,
c_name String,
c_address String,
c_nationkey UInt32,
c_phone LowCardinality(String),
c_acctbal decimal(15,2),
c_mktsegment LowCardinality(String),
c_comment LowCardinality(String)
) ENGINE = MergeTree
ORDER BY (c_custkey);
CREATE TABLE customer ON CLUSTER default AS customer_local
ENGINE = Distributed(default, default, customer_local, c_custkey);Converted Hologres DDL:
-- customer on Hologres
BEGIN;
CREATE TABLE CUSTOMER (
C_CUSTKEY INT NOT NULL PRIMARY KEY,
C_NAME TEXT NOT NULL,
C_ADDRESS TEXT NOT NULL,
C_NATIONKEY INT NOT NULL,
C_PHONE TEXT NOT NULL,
C_ACCTBAL DECIMAL(15,2) NOT NULL,
C_MKTSEGMENT TEXT NOT NULL,
C_COMMENT TEXT NOT NULL
);
CALL set_table_property('CUSTOMER', 'distribution_key', 'C_CUSTKEY');
CALL set_table_property('CUSTOMER', 'table_group', 'lineitem_tg');
CALL set_table_property('CUSTOMER', 'bitmap_columns', 'C_CUSTKEY,C_NATIONKEY,C_NAME,C_ADDRESS,C_PHONE,C_MKTSEGMENT,C_COMMENT');
CALL set_table_property('CUSTOMER', 'dictionary_encoding_columns', 'C_NAME,C_ADDRESS,C_PHONE,C_MKTSEGMENT,C_COMMENT');
CALL set_table_property('CUSTOMER', 'time_to_live_in_seconds', '31536000');
COMMIT;Step 5: Import DDL into Hologres
Run the following command in the psql client to execute the converted DDL against the target Hologres instance.
PGUSER="<username>" PGPASSWORD="<password>" psql \
-h "<host>" -p "<port>" -d "<database_name>" -f table.sqlMigrate data
Three methods are available. Choose based on your data volume, latency requirements, and existing tooling.
| Method | Best for | Tools |
|---|---|---|
| Export to CSV, then COPY (recommended) | One-time batch migration; straightforward setup; no additional infrastructure required | ClickHouse client + psql |
| Flink or Spark job | Large-scale migration; real-time or incremental sync; teams with existing Flink/Spark infrastructure | Flink, Spark |
| DataWorks Data Integration or DataX | Managed migration without custom code; full-database offline synchronization | DataWorks, DataX |
For Flink and Spark, see Import data using Spark. For DataWorks Data Integration, see Data Integration and Synchronize an entire ClickHouse database to Hologres offline.
The following steps describe the CSV export and COPY approach.
Step 1: Export data from ClickHouse to CSV
CSV is recommended for its simplicity and broad tool support. For tables with complex nested types or high-precision decimals, validate the exported values before loading, as CSV does not preserve type metadata.
Run the following command in the ClickHouse client to export table data to a local CSV file.
clickhouse-client --host="<host>" --port="<port>" --user="<username>" --password="<password>" \
--query="SELECT * FROM <database_name>.<table_name> FORMAT CSV" > table.csv| Parameter | Description |
|---|---|
<host> | Address of the source ClickHouse instance |
<port> | Port of the source ClickHouse instance |
<username> | Account with DML read/write, settings, and DDL permissions |
<password> | Password for the account |
<database_name> | Name of the database containing the table to migrate |
<table_name> | Name of the table to migrate |
Step 2: Import the CSV file into Hologres
Run the following command in the psql client to load the CSV file into the target Hologres table.
PGUSER="<username>" PGPASSWORD="<password>" psql \
-h "<host>" -p "<port>" -d "<database_name>" \
-c "COPY <schema_name>.<table_name> FROM STDIN (FORMAT 'csv')" < table.csv| Parameter | Description |
|---|---|
<username> | AccessKey ID of your Alibaba Cloud account. Get it from AccessKey Management |
<password> | AccessKey secret of your Alibaba Cloud account. Get it from AccessKey Management |
<host> | Endpoint of the Hologres instance. Find it on the instance details page under Network Information in the Management Console |
<port> | Port of the Hologres instance. Find it under Network Information in the Management Console |
<database_name> | Target database name in the Hologres instance |
<schema_name> | Target schema name. Defaults to public if not specified |
<table_name> | Target table name in Hologres |
Step 3: Verify the import
Query the imported table in Hologres to confirm the data loaded correctly.
SELECT COUNT(*) FROM <schema_name>.<table_name>;Migrate queries
Hologres uses PostgreSQL syntax, while ClickHouse uses its own SQL dialect. Most statements are compatible, but scalar functions, aggregate functions, and identifier quoting differ.
Syntax differences
| Area | ClickHouse | Hologres |
|---|---|---|
| String identifiers | 'column_name' (single quotes) | "column_name" (double quotes) |
| Table reference | SELECT x FROM <database>.<table> | SELECT x FROM <schema>.<table> |
Function equivalents
Functions not listed below have identical syntax in both systems.
| ClickHouse | Hologres |
|---|---|
toYear(expr) | to_char(expr, 'YYYY') |
toInt32(expr) | CAST(expr AS INTEGER) |
uniq(), uniqCombined(), uniqCombined64(), uniqHLL12() | approx_count_distinct() |
uniqExact() | count(DISTINCT x) |
quantile(level)(expr) | approx_percentile(level) WITHIN GROUP (ORDER BY expr) |
quantileExact(level)(expr) | percentile_cont(level) WITHIN GROUP (ORDER BY expr) |
toDecimal32(val, scale) | Use a numeric literal directly (e.g., 0.0000010000) |
Migration strategies
Regular expression replacement — use regex to replace fixed patterns such as function names, identifier quoting, and <database>.<table> references with their Hologres equivalents.
ClickHouse Extension — Hologres provides a ClickHouse Extension that natively supports some ClickHouse functions, such as toUInt32(), without conversion. See ClickHouse-compatible functions.
Query migration examples
The following examples use TPC-H benchmark queries. Each example highlights a specific migration pattern.
Example 1: Simple aggregation — no changes required
For straightforward aggregations using standard SQL functions, ClickHouse and Hologres queries are identical.
ClickHouse (Q1):
SELECT
l_returnflag,
l_linestatus,
sum(l_quantity) AS sum_qty,
sum(l_extendedprice) AS sum_base_price,
sum(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
avg(l_quantity) AS avg_qty,
avg(l_extendedprice) AS avg_price,
avg(l_discount) AS avg_disc,
count(*) AS count_order
FROM lineitem
WHERE l_shipdate <= date '1998-12-01' - interval '90' day
GROUP BY l_returnflag, l_linestatus
ORDER BY l_returnflag, l_linestatus;Hologres (Q1) — no changes:
SELECT
l_returnflag,
l_linestatus,
sum(l_quantity) AS sum_qty,
sum(l_extendedprice) AS sum_base_price,
sum(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
avg(l_quantity) AS avg_qty,
avg(l_extendedprice) AS avg_price,
avg(l_discount) AS avg_disc,
count(*) AS count_order
FROM lineitem
WHERE l_shipdate <= date '1998-12-01' - interval '90' day
GROUP BY l_returnflag, l_linestatus
ORDER BY l_returnflag, l_linestatus;Example 2: Subquery rewrite — IN to EXISTS
ClickHouse supports IN (subquery) with cross-table references in a way that Hologres does not. Rewrite using EXISTS.
ClickHouse (Q4):
SELECT
o_orderpriority,
count(*) AS order_count
FROM orders
WHERE o_orderdate >= date '1993-07-01'
AND o_orderdate < date '1993-07-01' + interval '3' month
AND o_orderdate IN (
SELECT o_orderdate
FROM lineitem, orders
WHERE l_orderkey = o_orderkey
AND l_commitdate < l_receiptdate
)
GROUP BY o_orderpriority
ORDER BY o_orderpriority;Hologres (Q4) — IN (subquery) replaced with EXISTS:
SELECT
o_orderpriority,
count(*) AS order_count
FROM orders
WHERE o_orderdate >= date '1993-07-01'
AND o_orderdate < date '1993-07-01' + interval '3' month
AND EXISTS (
SELECT *
FROM lineitem
WHERE l_orderkey = o_orderkey
AND l_commitdate < l_receiptdate
)
GROUP BY o_orderpriority
ORDER BY o_orderpriority;Example 3: Function replacement — `toDecimal32()` to a literal
ClickHouse's toDecimal32(value, scale) function has no Hologres equivalent. Replace it with a plain numeric literal.
ClickHouse (Q11):
SELECT
ps_partkey,
sum(ps_supplycost * ps_availqty) AS value
FROM partsupp, supplier, nation
WHERE ps_suppkey = s_suppkey
AND s_nationkey = n_nationkey
AND n_name = 'GERMANY'
GROUP BY ps_partkey
HAVING sum(ps_supplycost * ps_availqty) > (
SELECT sum(ps_supplycost * ps_availqty) * toDecimal32(0.0000010000, 9)
FROM partsupp, supplier, nation
WHERE ps_suppkey = s_suppkey
AND s_nationkey = n_nationkey
AND n_name = 'GERMANY'
)
ORDER BY value DESC
LIMIT 100;Hologres (Q11) — toDecimal32(0.0000010000, 9) replaced with 0.0000010000:
SELECT
ps_partkey,
sum(ps_supplycost * ps_availqty) AS value
FROM partsupp, supplier, nation
WHERE ps_suppkey = s_suppkey
AND s_nationkey = n_nationkey
AND n_name = 'GERMANY'
GROUP BY ps_partkey
HAVING sum(ps_supplycost * ps_availqty) > (
SELECT sum(ps_supplycost * ps_availqty) * 0.0000010000
FROM partsupp, supplier, nation
WHERE ps_suppkey = s_suppkey
AND s_nationkey = n_nationkey
AND n_name = 'GERMANY'
)
ORDER BY value DESC
LIMIT 100;Function compatibility
Hologres and ClickHouse share many basic functions with identical syntax. For functions that differ, Hologres provides either a native equivalent or a semantically identical alternative. For the full compatibility list, see ClickHouse-compatible functions.