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:SSformat. By default, it maps toTIMESTAMPTZ(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_equivalentparameter:-- 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;