使用 sqlalchemy_utils.create_materialized_view() 时如何自动创建索引

Posted

技术标签:

【中文标题】使用 sqlalchemy_utils.create_materialized_view() 时如何自动创建索引【英文标题】:How can an index automatically be created when using sqlalchemy_utils.create_materialized_view() 【发布时间】:2021-03-30 07:07:42 【问题描述】:

我试图让sqlalchemy 使用sqlalchemy_utils.view.create_materialized_view() 自动创建一个物化视图,这可行,但我也希望它也自动为其创建索引。 sqlalchemy_utils 提供了一种机制,但需要一个 sa.Index ,它需要一个 Column 对象。 Column 对象是从一个可选对象中创建的,但在模型上不存在以便在创建索引时将其传递给该对象。



class MVCustomerSearch(Base):
    __table__ = create_materialized_view(
        "mv_customer_search",
        select(
            [
                TSubStation.ixSubStation.label("ixSubStation"),
                TLocation.ixLocation.label("ixLocation"),
                TCustomer.ixCustomer.label("ixCustomer"),
                (
                    func.to_tsvector("english", func.coalesce(TLocation.sLocation, ""))
                    + func.to_tsvector(
                        "english", func.coalesce(TCustomer.sCustomer, "")
                    )
                    + func.to_tsvector(
                        "english", func.coalesce(TSubStation.sSubStation, "")
                    )
                ).label("tsv"),
            ],
        ).select_from(
            join(
                TCustomer, TLocation, TCustomer.ixCustomer == TLocation.ixCustomer
            ).join(TSubStation, TSubStation.ixLocation == TLocation.ixLocation)
        ),
        metadata,
        indexes=(
            db.Index(
                "idx_fts_customer_search",
                # This needs to be a Column and won't work.
                "MVCustomerSearch.tsv",
                postgresql_using="gin",
            )
        ),
    )

    customer = db.relationship(
        "TCustomer",
        uselist=False,
        primaryjoin="TCustomer.ixCustomer==MVCustomerSearch.ixCustomer",
        foreign_keys="TCustomer.ixCustomer",
    )
    location = db.relationship(
        "TLocation",
        uselist=False,
        primaryjoin="TLocation.ixLocation==MVCustomerSearch.ixLocation",
        foreign_keys="TLocation.ixLocation",
    )
    substation = db.relationship(
        "TSubStation",
        uselist=False,
        primaryjoin="TSubStation.ixSubStation==MVCustomerSearch.ixSubStation",
        foreign_keys="TSubStation.ixSubStation",
    )

如果我在模型存在后模拟创建索引,它将起作用。

In [31]: from .models.dbviews import MVCustomerSearch

In [32]: idx = db.Index(
    ...:                 "idx_fts_customer_search",
    ...:                 MVCustomerSearch.tsv,
    ...:                 postgresql_using="gin",
    ...:             )

In [33]: print(CreateIndex(idx).compile(dialect=postgresql.dialect()))
CREATE INDEX idx_fts_customer_search ON mv_customer_search USING gin (tsv)

如何传递一个Column 对象,该对象可以在列甚至存在之前创建该索引? SqlAlchemy 有一种在建立关系时使用字符串的机制,但这不适用于Index

编辑: 尝试使用字符串时添加 Traceback

In [34]: idx = db.Index(
    ...:                 "idx_fts_customer_search",
    ...:                 "MVCustomerSearch.tsv",
    ...:                 postgresql_using="gin",
    ...:             )

In [35]: print(CreateIndex(idx).compile(dialect=postgresql.dialect()))
---------------------------------------------------------------------------
CompileError                              Traceback (most recent call last)
<ipython-input-35-00025f58e3f9> in <module>
----> 1 print(CreateIndex(idx).compile(dialect=postgresql.dialect()))

<string> in <lambda>(self, bind, dialect, **kw)

~\AppData\Local\pypoetry\Cache\virtualenvs\pce-testsheets-g8TS-oaq-py3.8\Lib\site-packages\sqlalchemy\sql\elements.py in compile(self, default, bind, dialect, **kw)
    479             else:
    480                 dialect = default.StrCompileDialect()
--> 481         return self._compiler(dialect, bind=bind, **kw)
    482
    483     def _compiler(self, dialect, **kw):

~\AppData\Local\pypoetry\Cache\virtualenvs\pce-testsheets-g8TS-oaq-py3.8\Lib\site-packages\sqlalchemy\sql\ddl.py in _compiler(self, dialect, **kw)
     27         Dialect."""
     28
---> 29         return dialect.ddl_compiler(dialect, self, **kw)
     30
     31

~\AppData\Local\pypoetry\Cache\virtualenvs\pce-testsheets-g8TS-oaq-py3.8\Lib\site-packages\sqlalchemy\sql\compiler.py in __init__(self, dialect, statement, bind, schema_translate_map, compile_kwargs)
    320             if self.can_execute:
    321                 self.execution_options = statement._execution_options
--> 322             self.string = self.process(self.statement, **compile_kwargs)
    323
    324     @util.deprecated(

~\AppData\Local\pypoetry\Cache\virtualenvs\pce-testsheets-g8TS-oaq-py3.8\Lib\site-packages\sqlalchemy\sql\compiler.py in process(self, obj, **kwargs)
    350
    351     def process(self, obj, **kwargs):
--> 352         return obj._compiler_dispatch(self, **kwargs)
    353
    354     def __str__(self):

~\AppData\Local\pypoetry\Cache\virtualenvs\pce-testsheets-g8TS-oaq-py3.8\Lib\site-packages\sqlalchemy\sql\visitors.py in _compiler_dispatch(self, visitor, **kw)
     94                     )
     95                 else:
---> 96                     return meth(self, **kw)
     97
     98         else:

~\AppData\Local\pypoetry\Cache\virtualenvs\pce-testsheets-g8TS-oaq-py3.8\Lib\site-packages\sqlalchemy\dialects\postgresql\base.py in visit_create_index(self, create)
   2079         preparer = self.preparer
   2080         index = create.element
-> 2081         self._verify_index_table(index)
   2082         text = "CREATE "
   2083         if index.unique:

~\AppData\Local\pypoetry\Cache\virtualenvs\pce-testsheets-g8TS-oaq-py3.8\Lib\site-packages\sqlalchemy\sql\compiler.py in _verify_index_table(self, index)
   2993     def _verify_index_table(self, index):
   2994         if index.table is None:
-> 2995             raise exc.CompileError(
   2996                 "Index '%s' is not associated " "with any table." % index.name
   2997             )

CompileError: Index 'idx_fts_customer_search' is not associated with any table.

【问题讨论】:

能否提供错误回溯(如果有)?使用字符串代替尚不存在的列对象对我有用。 添加了回溯 【参考方案1】:

可以在表之外定义索引,当然也可以在create_materialized_view() 之外定义索引,因为它只是创建Table 的包装器。如果Index() 是用表定义之外的表绑定列调用的,则它与该表相关联。所以,正如你所尝试的那样:

class MVCustomerSearch(Base):
    __table__ = create_materialized_view(
        "idx_fts_customer_search",
        select(...),
        metadata
    )

Index(
    "idx_fts_customer_search",
    MVCustomerSearch.tsv,
    postgresql_using="gin",
)

或者(更明显的是imo):

MVCustomerSearchTable = create_materialized_view(
    "idx_fts_customer_search",
    select(...),
    metadata
)

Index(
    "idx_fts_customer_search",
    MVCustomerSearchTable.c.tsv,
    postgresql_using="gin",
)

class MVCustomerSearch(Base):
    __table__ = MVCustomerSearchTable

【讨论】:

以上是关于使用 sqlalchemy_utils.create_materialized_view() 时如何自动创建索引的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)