Skip to content
v0.1.0latest release21 skills2 categories3 sub-categories22 tested scripts68 reference docsContact us

sql-dialect-translator

Translate SQL between dialects — PostgreSQL, MySQL, SQL Server (T-SQL), Oracle, SQLite — covering data types, functions, pagination, date handling, upserts and identity columns. Use when migrating queries between databases or the user asks to convert SQL to another dialect. Includes a sqlglot-based translation script.

Programming / SQLv1.0.0Updated OCT 20 20261 reference1 script

Convert SQL queries between different database dialects (PostgreSQL, MySQL, SQL Server, Oracle, SQLite).

Trigger Conditions

Activate this skill when the user:

  • Says "convert to PostgreSQL/MySQL/etc."
  • Asks to migrate queries between databases
  • Needs help with dialect-specific syntax
  • Asks about differences between SQL dialects
  • Has queries failing after database migration

Translation Workflow

text
1. IDENTIFY    -> Detect source dialect
2. ANALYZE     -> Find dialect-specific features
3. MAP         -> Identify equivalent features in target
4. TRANSLATE   -> Convert syntax
5. VALIDATE    -> Verify correctness
6. OPTIMIZE    -> Apply target dialect best practices

Dialect Feature Comparison

Data Types

FeaturePostgreSQLMySQLSQL ServerOracleSQLite
Auto-incrementSERIAL, BIGSERIALAUTO_INCREMENTIDENTITYSEQUENCEINTEGER PRIMARY KEY
BooleanBOOLEANTINYINT(1), BOOLEANBITNUMBER(1)INTEGER
Text (unlimited)TEXTLONGTEXTVARCHAR(MAX)CLOBTEXT
BinaryBYTEABLOBVARBINARY(MAX)BLOBBLOB
JSONJSON, JSONBJSONNVARCHAR(MAX)JSON (21c+)TEXT
UUIDUUIDCHAR(36)UNIQUEIDENTIFIERRAW(16)TEXT
DateDATEDATEDATEDATETEXT
TimestampTIMESTAMPDATETIMEDATETIME2TIMESTAMPTEXT
ArrayARRAY[]JSONN/AVARRAYN/A

String Functions

OperationPostgreSQLMySQLSQL ServerOracle
Concatenate|| or CONCATCONCAT+ or CONCAT||
SubstringSUBSTRING(s, start, len)SUBSTRING(s, start, len)SUBSTRING(s, start, len)SUBSTR(s, start, len)
LengthLENGTHLENGTH, CHAR_LENGTHLENLENGTH
LowercaseLOWERLOWERLOWERLOWER
UppercaseUPPERUPPERUPPERUPPER
TrimTRIMTRIMLTRIM, RTRIMTRIM
PositionPOSITION(x IN y)LOCATE(x, y)CHARINDEX(x, y)INSTR(y, x)
ReplaceREPLACEREPLACEREPLACEREPLACE
Pad leftLPADLPADRIGHT(REPLICATE...)LPAD
Regex match~REGEXPLIKE (limited)REGEXP_LIKE

Date/Time Functions

OperationPostgreSQLMySQLSQL ServerOracle
Current dateCURRENT_DATECURDATE()GETDATE()SYSDATE
Current timestampCURRENT_TIMESTAMPNOW()GETDATE()SYSTIMESTAMP
Extract partEXTRACT(part FROM date)EXTRACT(part FROM date)DATEPART(part, date)EXTRACT(part FROM date)
Date adddate + INTERVAL '1 day'DATE_ADD(date, INTERVAL 1 DAY)DATEADD(day, 1, date)date + 1
Date diffdate1 - date2DATEDIFF(date1, date2)DATEDIFF(day, date1, date2)date1 - date2
Format dateTO_CHAR(date, 'fmt')DATE_FORMAT(date, 'fmt')FORMAT(date, 'fmt')TO_CHAR(date, 'fmt')
Parse dateTO_DATE(str, 'fmt')STR_TO_DATE(str, 'fmt')CONVERT(date, str)TO_DATE(str, 'fmt')
TruncateDATE_TRUNC('month', date)DATE_FORMAT(date, '%Y-%m-01')DATETRUNC(month, date)TRUNC(date, 'MM')

Limiting Results

DialectSyntax
PostgreSQLLIMIT 10 OFFSET 20
MySQLLIMIT 10 OFFSET 20 or LIMIT 20, 10
SQL ServerOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY (2012+) or TOP 10
OracleOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY (12c+) or ROWNUM
SQLiteLIMIT 10 OFFSET 20

Identifier Quoting

DialectSyntax
PostgreSQLDouble quotes: "table"
MySQLBackticks: `table`
SQL ServerSquare brackets: [table] or double quotes
OracleDouble quotes: "TABLE" (case-sensitive)
SQLiteDouble quotes, backticks, or square brackets

NULL Handling

OperationPostgreSQLMySQLSQL ServerOracle
CoalesceCOALESCE, NULLIFCOALESCE, IFNULL, NULLIFCOALESCE, ISNULL, NULLIFCOALESCE, NVL, NULLIF
NULL-safe equalIS NOT DISTINCT FROM<=>N/AN/A
First non-nullCOALESCECOALESCECOALESCENVL, COALESCE

Conditional Logic

OperationPostgreSQLMySQLSQL ServerOracle
If-thenCASE WHENCASE WHEN, IF()CASE WHEN, IIF()CASE WHEN, DECODE
Null checkNULLIF, COALESCENULLIF, IFNULLNULLIF, ISNULLNVL, NVL2

Upsert/Merge

PostgreSQL:

sql
INSERT INTO table (id, name) VALUES (1, 'test')
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;

MySQL:

sql
INSERT INTO table (id, name) VALUES (1, 'test')
ON DUPLICATE KEY UPDATE name = VALUES(name);

SQL Server:

sql
MERGE INTO table AS target
USING (VALUES (1, 'test')) AS source (id, name)
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET name = source.name
WHEN NOT MATCHED THEN INSERT (id, name) VALUES (source.id, source.name);

Oracle:

sql
MERGE INTO table target
USING (SELECT 1 AS id, 'test' AS name FROM dual) source
ON (target.id = source.id)
WHEN MATCHED THEN UPDATE SET target.name = source.name
WHEN NOT MATCHED THEN INSERT (id, name) VALUES (source.id, source.name);

Common Table Expressions

All modern versions support standard CTE syntax:

sql
WITH cte AS (
    SELECT ...
)
SELECT * FROM cte;

Recursive CTEs:

  • PostgreSQL: WITH RECURSIVE
  • MySQL 8.0+: WITH RECURSIVE
  • SQL Server: WITH (recursive by default when self-referencing)
  • Oracle: WITH (use CONNECT BY for older versions)

Window Functions

All modern databases support standard window functions. Key differences:

FeaturePostgreSQLMySQLSQL ServerOracle
FILTER clauseYesNoNoNo
Frame: GROUPSYesYes (8.0+)NoYes
PERCENTILE_CONTYesNo (use variable)YesYes
STRING_AGGSTRING_AGGGROUP_CONCATSTRING_AGGLISTAGG

JSON Support

PostgreSQL:

sql
SELECT data->>'name' FROM table;
SELECT data->'nested'->>'field' FROM table;
SELECT data @> '{"key": "value"}' FROM table;

MySQL:

sql
SELECT JSON_EXTRACT(data, '$.name') FROM table;
SELECT data->'$.name' FROM table;
SELECT JSON_CONTAINS(data, '"value"', '$.key') FROM table;

SQL Server:

sql
SELECT JSON_VALUE(data, '$.name') FROM table;
SELECT JSON_QUERY(data, '$.nested') FROM table;

Translation Examples

LIMIT/OFFSET Translation

PostgreSQL:

sql
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20;

To SQL Server:

sql
SELECT * FROM users ORDER BY id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

To Oracle (12c+):

sql
SELECT * FROM users ORDER BY id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

To Oracle (11g):

sql
SELECT * FROM (
    SELECT t.*, ROWNUM rn FROM (
        SELECT * FROM users ORDER BY id
    ) t WHERE ROWNUM <= 30
) WHERE rn > 20;

Auto-Increment Translation

PostgreSQL:

sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100)
);

To MySQL:

sql
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100)
);

To SQL Server:

sql
CREATE TABLE users (
    id INT IDENTITY(1,1) PRIMARY KEY,
    name VARCHAR(100)
);

To Oracle:

sql
CREATE TABLE users (
    id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    name VARCHAR2(100)
);

String Concatenation Translation

PostgreSQL:

sql
SELECT first_name || ' ' || last_name AS full_name FROM users;

To MySQL:

sql
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;

To SQL Server:

sql
SELECT first_name + ' ' + last_name AS full_name FROM users;
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;

Date Arithmetic Translation

PostgreSQL:

sql
SELECT created_at + INTERVAL '30 days' FROM users;
SELECT DATE_TRUNC('month', created_at) FROM users;

To MySQL:

sql
SELECT DATE_ADD(created_at, INTERVAL 30 DAY) FROM users;
SELECT DATE_FORMAT(created_at, '%Y-%m-01') FROM users;

To SQL Server:

sql
SELECT DATEADD(day, 30, created_at) FROM users;
SELECT DATETRUNC(month, created_at) FROM users;  -- SQL Server 2022+
SELECT DATEFROMPARTS(YEAR(created_at), MONTH(created_at), 1) FROM users;  -- Earlier

To Oracle:

sql
SELECT created_at + 30 FROM users;
SELECT TRUNC(created_at, 'MM') FROM users;

Boolean Translation

PostgreSQL:

sql
SELECT * FROM users WHERE is_active = TRUE;

To MySQL:

sql
SELECT * FROM users WHERE is_active = 1;
SELECT * FROM users WHERE is_active = TRUE;  -- Also works

To SQL Server:

sql
SELECT * FROM users WHERE is_active = 1;

To Oracle:

sql
SELECT * FROM users WHERE is_active = 1;

Output Format

When translating queries:

  1. Source: Original query with identified dialect
  2. Target: Translated query for target dialect
  3. Changes Made: List of modifications
  4. Caveats: Any functionality differences or limitations
  5. Alternatives: Other approaches if applicable

Scripts

Use the translator for a first draft, then review it against the feature comparison above:

bash
pip install sqlglot
python scripts/translate_sql.py query.sql --from mysql --to postgres
echo "SELECT TOP 10 * FROM t" | python scripts/translate_sql.py - --from tsql --to oracle

Constructs with no equivalent in the target dialect are printed as warnings on stderr. Fix those by hand and list them under Caveats.

Additional Resources

FileContent
examples.mdComplete translation examples
More in SQL