【问题标题】:Case insensitive indexing in Postgres with SQLAlchemy使用 SQLAlchemy 在 Postgres 中进行不区分大小写的索引
【发布时间】:2018-10-30 11:55:30
【问题描述】:

考虑一个带有索引String 字段的声明性 SQLAlchemy 模型:

class User(Base):
    name = Column(String(100), index=True, nullable=False)

name 字段区分大小写,这意味着应保留原始大小写,但应支持对索引的高效不区分大小写查询。

在 SQLAlchemy 中实现和实施的最佳方法是什么?

如果需要查询可以使用lower()

session.query(User).filter_by(name=lower('SOME_name'))

但这并不重要,只要解决方案优雅且高效即可。

由于性能要求,使用 ILIKE 和 Postgres 级别的 lower() 的查询是不可接受的,它们已经过测试,对于我的用例而言,它们在大型表上的执行速度不够快。

【问题讨论】:

    标签: python postgresql sqlalchemy


    【解决方案1】:

    创建一个索引表达式LOWER(name)functional index

    Index('idx_user_name_lower', func.lower(User.name))
    

    使用索引到位查询,例如

    session.query(User).filter(func.lower(User.name) == 'SOME_name'.lower())
    

    如果LOWER(name) 具有高基数,可能会表现更好。

    然后您可以将小写处理封装在 custom comparator 中:

    # Verbatim from the documentation
    class CaseInsensitiveComparator(Comparator):
        def __eq__(self, other):
            return func.lower(self.__clause_element__()) == func.lower(other)
    
    class User(Base):
        ...
        @hybrid_property
        def name_insensitive(self):
            return self.name.lower()
    
        @name_insensitive.comparator
        def name_insensitive(cls):
            return CaseInsensitiveComparator(cls.name)
    

    比较器会在后台对双方应用func.lower()

    session.query(User).filter_by(name_insensitive='SOME_name')
    

    等价于

    session.query(User).filter(func.lower(User.name) == func.lower('SOME_name'))
    

    【讨论】:

    • 感谢您的详尽回复!当您说“可能表现更好”时,我是否可以期望性能与常规索引查询相当?
    • 是的,它应该也可以执行,因为统计信息和查询都使用了索引。
    猜你喜欢
    • 2021-06-14
    • 2011-10-17
    • 2014-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    • 2013-04-17
    相关资源
    最近更新 更多