All Products
Search
Document Center

Hologres:Migrate MySQL data to Hologres

Last Updated:Mar 26, 2026

Hologres is PostgreSQL-compatible, so migrating from MySQL requires mapping data types and adapting SQL queries. This topic covers the available sync methods, data type mappings, and the query and function differences you'll encounter after migration.

Choose a sync method

The following table lists the sync methods available for migrating MySQL data to Hologres. For Extract, Transform, Load (ETL) processing, use Flink to read from MySQL and write to Hologres.

Method When to use Documentation
Offline sync for a single table Sync data from a single MySQL table to Hologres as a one-time batch operation. Sync a single MySQL table to Hologres offline
Real-time sync for a single table Stream changes from a single MySQL table to Hologres using MySQL binary logging (Binlog). Sync a single MySQL table to Hologres in real time
Real-time sync for an entire database Stream changes from an entire MySQL database to Hologres in real time. Sync an entire MySQL database to Hologres in real time
Sync solution Use Data Integration to sync multiple tables in a single operation, with integrated full data sync followed by continuous incremental data sync. Sync solution to Hologres

Data type mapping

Use the following table to map MySQL types to Hologres types. For a complete Hologres type reference, see Data type reference.

Key rules before you start:

  • Integer sizing: Hologres has three integer types — SMALLINT (2 bytes), INTEGER (4 bytes), and BIGINT (8 bytes) — compared to MySQL's five. Map each MySQL integer to a Hologres type with an equal or greater byte size.

  • Unsigned integers: Hologres does not support unsigned integers. Account for potential overflow when mapping unsigned fields, and use a larger integer type or TEXT if values may exceed the target range.

  • Text types: Map all MySQL text variants (TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT) to TEXT in Hologres.

  • DATETIME and time zones: MySQL DATETIME has no time zone and uses the YYYY-MM-DD HH:MM:SS format. By default, it maps to TIMESTAMPTZ (TIMESTAMP WITH TIME ZONE) in Hologres. To use a time zone-free TIMESTAMP, specify it explicitly in your sync configuration or table creation statement.

MySQL type Hologres type Notes
BIGINT BIGINT
BIGINT(20) UNSIGNED TEXT Hologres does not support unsigned integers. TEXT avoids overflow risk.
BINARY(n) BYTEA
BIT BOOLEAN
CHAR(n), CHARACTER(n) CHAR(n), CHARACTER(n)
DATE DATE
DATETIME TIMESTAMPTZ (TIMESTAMP WITH TIME ZONE) MySQL DATETIME has no time zone. Maps to TIMESTAMPTZ by default. Specify TIMESTAMP explicitly to opt out.
DECIMAL(p,s), DEC(p,s) DECIMAL(p,s), DEC(p,s)
DOUBLE DOUBLE PRECISION
FLOAT REAL
INT, INTEGER INT, INTEGER
MEDIUMINT INTEGER MEDIUMINT is 3 bytes; INTEGER is 4 bytes — the next larger Hologres integer type.
NUMERIC(p,s) NUMERIC(p,s)
SMALLINT SMALLINT
TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB BYTEA
TINYINT SMALLINT TINYINT is 1 byte; SMALLINT is the smallest Hologres integer type (2 bytes).
TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT TEXT All MySQL text variants map to TEXT.
TIME TIME [WITHOUT TIME ZONE]
TIMESTAMP TIMESTAMP [WITH TIME ZONE]
VARBINARY(n), VARBINARY(max) BYTEA
VARCHAR(n) VARCHAR(n)
VARCHAR(max) TEXT

Query syntax differences

Hologres is PostgreSQL-compatible, so several MySQL query patterns require adaptation.

Quotation marks

Hologres is case-insensitive by default. To reference a case-sensitive identifier, wrap it in double quotation marks ("").

Replace MySQL backtick syntax with double quotes:

-- MySQL
SELECT `A` FROM b;

-- Hologres
SELECT "A" FROM b;

Filter conditions

Hologres requires exact type matching in filter conditions and does not perform implicit type conversion. If the types don't match, you'll get an error such as:

operator does not exist: text = integer

To resolve this, create custom type casts:

CREATE CAST (TEXT AS INTEGER) WITH INOUT AS IMPLICIT;
CREATE CAST (TEXT AS BIGINT) WITH INOUT AS IMPLICIT;
CREATE CAST (TEXT AS DECIMAL) WITH INOUT AS IMPLICIT;
CREATE CAST (TEXT AS TIMESTAMP) WITH INOUT AS IMPLICIT;
CREATE CAST (NUMERIC AS TEXT) WITH INOUT AS IMPLICIT;

Example: If the ds column is TEXT and you filter with an integer value, the following query fails in Hologres:

SELECT * FROM business_module WHERE ds = 20210329;

After creating the appropriate cast, the query runs without error.

Pagination

Replace the MySQL two-argument LIMIT syntax with the standard OFFSET/LIMIT form:

-- MySQL
LIMIT 0, 10

-- Hologres
OFFSET 0 LIMIT 10

Sorting

MySQL and Hologres differ in how they sort NULL values in ascending order:

Database ASC default DESC default
MySQL NULLS FIRST NULLS FIRST
Hologres NULLS LAST NULLS FIRST

To get consistent results, specify the NULL sort order explicitly:

ORDER BY column DESC NULLS LAST

Grouping

Hologres does not support GROUP BY on non-exact types (FLOAT, DOUBLE) by default. Two options:

  • Cast the column to DECIMAL before grouping.

  • Enable the hg_experimental_enable_double_equivalent parameter:

    -- Session level
    SET hg_experimental_enable_double_equivalent = on;
    
    -- Database level
    ALTER DATABASE <database_name> SET hg_experimental_enable_double_equivalent = on;
This parameter requires Hologres V0.10 or later. If your instance runs an earlier version, contact technical support through the Hologres DingTalk group to request an upgrade. For details, see How do I get more online support?.

UNION

The UNION operator requires matching column types across all queries. MySQL performs implicit type conversion; Hologres does not. A type mismatch produces:

UNION types bigint and text cannot be matched

Cast mismatched columns explicitly:

-- Fails if project_id is BIGINT in tableA and TEXT in tableB
SELECT project_id FROM tableA
UNION ALL
SELECT project_id FROM tableB;

-- Fix: cast to a matching type
SELECT project_id FROM tableA
UNION ALL
SELECT CAST(project_id AS BIGINT) FROM tableB;

Function differences

Hologres supports most PostgreSQL-compatible functions. For the full list, see PostgreSQL-compatible functions. The following MySQL functions need adaptation.

Division by zero

MySQL returns NULL for division by zero. Hologres returns an error:

ERROR: division by zero

Option 1: Use NULLIF to treat the divisor as NULL when it is zero:

SELECT a / NULLIF(b, 0) FROM table;

Option 2 (Hologres V1.3.21 and later): Enable the mysql_compatible extension to tolerate division by zero without changing individual queries:

-- Run once per database (requires Superuser)
CREATE EXTENSION IF NOT EXISTS mysql_compatible;

-- Enable division-by-zero tolerance for the current session
SET mysql_compatible.enable = on;

This covers multiple scenarios:

-- Scenario 1: Constant division of the same type
SET mysql_compatible.enable = on;
SELECT 1 / 0;

-- Scenario 2: Constant division with type conversion
SET mysql_compatible.enable = on;
SELECT 1.0 / 0;

-- Scenario 3: Dividend is a column expression
SET mysql_compatible.enable = on;
SELECT SUM(c) / 0 FROM (SELECT generate_series(1, 100) AS c) AS t;

-- Scenario 4: Divisor is a column expression
SET mysql_compatible.enable = on;
SELECT MAX(c) / SUM(d)
FROM (
  SELECT generate_series(1, 101) AS c,
         generate_series(-50, 50) AS d
) AS t;

-- Scenario 5: Tolerate division by zero during INSERT
CREATE TABLE IF NOT EXISTS test_insert_divide_by_zero (c1 INT);
SET mysql_compatible.strict_mode = off;
SET mysql_compatible.enable = on;
INSERT INTO test_insert_divide_by_zero SELECT 100 / 0.0;

If you encounter issues, see Common upgrade preparation failure errors or contact technical support through the Hologres DingTalk group. For details, see How do I obtain additional online support?.

Integer division

MySQL returns a decimal when dividing two integers with a remainder; Hologres truncates to an integer.

Expression MySQL result Hologres result
5 / 2 2.5 2

Cast one operand to a floating-point type to get the decimal result:

SELECT 1 / 2::FLOAT;

IF function

Hologres does not support the IF function. Replace it with a CASE WHEN expression:

-- MySQL
IF(condition, true_value, false_value)

-- Hologres
CASE WHEN condition THEN true_value ELSE false_value END

IFNULL function

Replace the MySQL IFNULL function with COALESCE:

-- MySQL
IFNULL(x, y)

-- Hologres
COALESCE(x, y)

LENGTH function

Replace the MySQL LENGTH function with CHAR_LENGTH:

-- MySQL
LENGTH(string)

-- Hologres
CHAR_LENGTH(string)

FAQ

COUNT DISTINCT returns different results for multiple columns

Symptom: A multi-column COUNT DISTINCT query returns different row counts in MySQL and Hologres.

Cause: MySQL's count(distinct column_1, column_2, ...) excludes rows where any of the specified columns is NULL. Hologres's count(distinct(column_1, column_2, ...)) includes rows even when some columns are NULL, which produces a higher count.

Fix: Concatenate the columns so that any NULL propagates through the expression, matching MySQL's behavior:

SELECT count(distinct column_1 || column_2 || ...) FROM table;

Example:

CREATE TABLE count_distinct_test (
  a TEXT,
  b TEXT
);

INSERT INTO count_distinct_test VALUES
  ('a', 'b'),
  ('a', NULL),
  (NULL, 'b'),
  ('a', 'b');

-- Hologres default: includes rows with NULL columns → returns 3
SELECT count(distinct(a, b)::text) FROM count_distinct_test;

-- MySQL-compatible: NULL propagates through concatenation → returns 1
SELECT count(distinct a || b) FROM count_distinct_test;