You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

注意事项

  1. 对于嵌套子查询、复杂聚合表达式的场景,可能需要调整AST遍历逻辑,确保只处理外层SELECT的LIMIT。
  2. 规则3中无表前缀的列默认按源表列处理,如果你的场景存在多表关联且无前缀的列来自不同表,需要补充表归属判断逻辑。
  3. Oracle中ROWNUM是在排序前生效的,如果原SQLite语句包含ORDER BY,需要额外处理成嵌套子查询先排序再过滤ROWNUM(比如SELECT * FROM (SELECT ... ORDER BY ...) WHERE ROWNUM = N),可根据需求扩展代码。

内容的提问来源于stack exchange,提问作者Алексей Бородин

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 06:33:13