Python实现SQLite到Oracle SELECT语句的定制化语法转换
SQLite SELECT 转 Oracle 语法的 Python 实现
直接手动字符串替换或者正则很容易踩坑(比如嵌套子查询、CTE、复杂表达式的情况),用SQL解析库处理AST(抽象语法树)是更可靠的方案。下面用sqlglot这个Python库来实现你需要的三个转换规则:
依赖安装
先安装sqlglot:
pip install sqlglot
实现代码
import sqlglot def sqlite_to_oracle(sql: str) -> str: # 解析SQLite语句为AST parsed = sqlglot.parse_one(sql, read="sqlite") # 规则1:处理LIMIT转ROWNUM select_stmt = parsed.find(sqlglot.exp.Select) if select_stmt: limit_clause = select_stmt.args.get("limit") if limit_clause: # 提取LIMIT的数值 limit_val = limit_clause.args["expression"].sql() # 处理WHERE子句 where_clause = select_stmt.args.get("where") rownum_expr = sqlglot.exp.EQ( this=sqlglot.exp.Identifier(this="ROWNUM"), expression=sqlglot.exp.Literal(this=limit_val) ) if where_clause: # 已有WHERE,追加AND ROWNUM条件 select_stmt.set("where", sqlglot.exp.And(this=where_clause, expression=rownum_expr)) else: # 无WHERE,插入新的WHERE子句 select_stmt.set("where", rownum_expr) # 移除原LIMIT子句 select_stmt.args.pop("limit") # 规则2:移除SELECT/JOIN中表别名前的AS for table in parsed.find_all(sqlglot.exp.Table): alias = table.args.get("alias") if alias: # 移除AS关键字 alias.args.pop("as", None) # 规则3:给非CTE源表的选中列加双引号 # 先收集所有CTE表名 cte_table_names = set() for cte in parsed.find_all(sqlglot.exp.CTE): cte_name = cte.args["alias"].args["this"].sql() cte_table_names.add(cte_name) # 遍历SELECT中的列,判断是否来自源表并添加引号 for col in select_stmt.find_all(sqlglot.exp.Column): col_table = col.args.get("table") if col_table: # 列指定了表,判断是否为非CTE表 table_name = col_table.sql() if table_name not in cte_table_names: col.args["this"] = sqlglot.exp.Identifier(this=col.args["this"].sql(), quoted=True) else: # 无表前缀的列默认视为源表列(可根据实际场景调整逻辑) col.args["this"] = sqlglot.exp.Identifier(this=col.args["this"].sql(), quoted=True) # 生成符合Oracle语法的SQL return parsed.sql(dialect="oracle")
测试示例
示例1:无WHERE的LIMIT语句
输入SQLite语句:
SELECT id, name FROM users AS u LIMIT 10
输出Oracle语句:
SELECT "id", "name" FROM users u WHERE ROWNUM = 10
示例2:带WHERE的LIMIT语句
输入SQLite语句:
SELECT u.id, u.name FROM users AS u WHERE u.age > 18 LIMIT 5
输出Oracle语句:
SELECT "id", "name" FROM users u WHERE u.age > 18 AND ROWNUM = 5
示例3:带CTE的语句
输入SQLite语句:
WITH cte AS (SELECT id FROM users) SELECT id FROM cte LIMIT 3
输出Oracle语句:
WITH cte AS (SELECT "id" FROM users) SELECT id FROM cte WHERE ROWNUM = 3
注意事项
- 对于嵌套子查询、复杂聚合表达式的场景,可能需要调整AST遍历逻辑,确保只处理外层SELECT的LIMIT。
- 规则3中无表前缀的列默认按源表列处理,如果你的场景存在多表关联且无前缀的列来自不同表,需要补充表归属判断逻辑。
- Oracle中ROWNUM是在排序前生效的,如果原SQLite语句包含
ORDER BY,需要额外处理成嵌套子查询先排序再过滤ROWNUM(比如SELECT * FROM (SELECT ... ORDER BY ...) WHERE ROWNUM = N),可根据需求扩展代码。
内容的提问来源于stack exchange,提问作者Алексей Бородин
相关产品推荐
相关产品推荐

