首页 > 解决方案 > 使用 SQLAlchemy 优化过滤多个 SQL 计数子查询

问题描述

我正在尝试将原始 postgres-SQL 转换为使用计数和过滤器的 sqlalchemy

SELECT

  (
   SELECT
     COUNT(user_model.uuid)
     FILTER ( WHERE  email_vector @@ parse_websearch('search term'))
   ) AS email_vector,

   (
    SELECT
    COUNT(user_model.uuid)
    FILTER ( WHERE  first_name_vector @@ parse_websearch('search term'))
    ) AS first_name_vector,

FROM 
user_model

WHERE  
(user_model.uuid = '20d7c90d-ebfa-4b04-9ee7-4fdedabc6c0b' AND all_vectors @@  parse_websearch('search term')  )

这很好用而且非常快(低于 100 毫秒 - 即使在过滤 20 个字段时)

我将 sqlalchemy 写为(简化)

search_query = []
search_vectors = [ email_vector, first_name_vector]
search_vector_names = [ 'email_vector', 'first_name_vector' ] 

for search_vector, search_vector_name in zip(search_vectors, search_vector_names):
     search_query.append(sa.sql.select(
                sa.func.count(user_model.uuid)) \
                    .filter(search_vector.op('@@') \
                        (sa.func.parse_websearch(search_term))) \
                    .label(search_vector_name)
            )

base_query = db.session.query(*search_query)

base_query = base_query.filter(user_model.uuid==user_uuid)
base_query = query.filter(search_vector.op('@@')(sa.func.parse_websearch(search_term))

这将产生以下 SQL

SELECT (
  SELECT count(user_model.uuid) AS count_1
  FROM user_model
  WHERE user_model.email_vector @@ parse_websearch(%(parse_websearch_1)s)) AS email_vector, 

  (SELECT count(user_model.uuid) AS count_2
  FROM user_model
  WHERE user_model.first_name_vector @@ parse_websearch(%(parse_websearch_2)s)) AS first_name_vector,
  
WHERE user_model.uuid = %(uuid_1)s AND (user_model.all_vectors @@ parse_websearch(%(parse_websearch_30)s)) 

这可行,但速度很慢(+1 ​​秒)。我不确定为什么它这么慢或如何对子查询进行过滤查询。

我怀疑在 SQL 使用最终 WHERE 条件的过滤结果的每个子查询上执行 COUNT,但我不完全确定,我找不到任何对过滤器的引用(我认为它是 CASE 的简写? ) 在 sqlalchemy 文档中。我已经尝试添加.filter(user_model.uuid==uuid) 到 COUNT SQL,但它仍然很慢。

谁能提供我所缺少的洞察力?

标签: sqlpostgresqlsqlalchemy

解决方案


我最终使用 CASE 做到了这一点

            case_stmt = sa.case(
                [
                    (
                        full_text_search_vector.op('@@') \
                        (sa.func.parse_websearch(last_search_term)),1
                    )
                ]
            ).label(col_name)

            query.append(case_stmt)


        base_query = db.session.query(*query)


推荐阅读