Python SQLAlchemy在查询中获取子字符串

Python SQLAlchemy在查询中获取子字符串,python,sqlalchemy,Python,Sqlalchemy,我有一个带有SQLite的dev数据库(也可以运行单元测试),还有一个带有MySQL的prod数据库 我需要用SQLAlchemy编写一个查询,它在WHERE语句中使用一个子字符串。我试图使用func,但它不需要为特定的数据库引擎进行修改就可以翻译它 我的问题是: MyTable.field == func.substring_index(OtherTable.other_field, ":", 1) 所以我基本上想把一个值除以“:”并取第一部分 问题是它是用SQLite的substring\

我有一个带有SQLite的dev数据库(也可以运行单元测试),还有一个带有MySQL的prod数据库

我需要用SQLAlchemy编写一个查询,它在WHERE语句中使用一个子字符串。我试图使用
func
,但它不需要为特定的数据库引擎进行修改就可以翻译它

我的问题是:

MyTable.field == func.substring_index(OtherTable.other_field, ":", 1)
所以我基本上想把一个值除以“:”并取第一部分


问题是它是用SQLite的
substring\u index
翻译的,这是不正确的。有没有办法在WHERE子句中使用子字符串?

您可以检查数据库方言名称,并根据该名称生成子字符串。例如:

def substring(column, delimeter):
    if session.bind.dialect.name == 'sqlite':
        return func.substr(column, 1, func.instr(column, delimeter) - 1)
    elif session.bind.dialect.name == 'mysql':
        return func.substring_index(column, delimeter, 1)
然后将您的过滤条件替换为:

MyTable.field == substring(OtherTable.other_field, ":")
SQLAlchemy支持和。使用这些函数,您可以注册
substring\u index()
作为一个函数,并对SQLite进行特殊处理:

from sqlalchemy.sql.functions import GenericFunction
from sqlalchemy.types import String
from sqlalchemy.ext.compiler import compiles

class substring_index(GenericFunction):
    type = String

@compiles(substring_index, 'sqlite')
def compile_substring_index_sqlite(element, compiler, **kw):
    s, delim, count = element.clauses

    # This assumes that count is a `bindparam`, produced from passing
    # literal integer to `func.substring_index()`.
    assert count.value == 1, "INSTR(X, Y) only supports first occurrence"

    s = compiler.process(s, **kw)
    delim = compiler.process(delim, **kw)

    return f"substr({s}, 1, instr({s}, {delim}) - 1)"
另一个选项是在SQLite中作为
子字符串\u index()

有了这个函数,您可以像在MySQL中一样调用它



至于为什么这不是现成的SQLAlchemy库的一部分,这将是一场无休止的战斗,因为不同的DBMS支持完全不同的功能。例如,某些三角函数的名称各不相同,而且是现成的。在一个代码库中支持不同的SQL DBMS并非易事,而且往往不值得这样做。

谢谢!我试试看!你知道为什么它不是图书馆的一部分吗?我不确定。我猜是因为每个DBMS对子字符串问题都有自己的想法和实现,而且它们并不完全兼容。
from sqlalchemy import event

def sqlite_substring_index(s, delim, count):
    parts = s.split(delim)

    if count > 0:
        parts = parts[:count]

    else:
        parts = parts[count:]

    return delim.join(parts)

# In your SQLite branch, before anything else DB related is performed:
@event.listens_for(engine, 'connect')
def create_functions(dbapi_connection, connection_record):
    dbapi_connection.create_function('substring_index', 3, sqlite_substring_index)