【问题标题】:Python-Sqlalchemy-Postgres : How to store subquery result in a variable and use it to a master queryPython-Sqlalchemy-Postgres:如何将子查询结果存储在变量中并将其用于主查询
【发布时间】:2021-10-07 12:56:42
【问题描述】:

我有一个子查询,它在我的主查询中的多个 where 条件中使用。因此,子查询多次执行以获得相同的结果。有没有办法存储和使用子查询结果,使其只执行一次。

示例代码:

from sqlalchemy.sql.schema import ForeignKey
from sqlalchemy import Column, Integer, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.expression import select, union

Base = declarative_base()
class Table1(Base):
    __tablename__ = 'table1'

    id = Column(Integer, primary_key=True)
    uuid = Column(Text, unique=True, nullable=False)
    
class Table2(Base):
    __tablename__ = 'table2'

    id = Column(Integer, primary_key=True)
    uuid = Column(Text, unique=True, nullable=False)
    
class Table3(Base):
    __tablename__ = 'table3'

    id = Column(Integer, primary_key=True)
    uuid = Column(Text, unique=True, nullable=False)
    

class Table4(Base):
    __tablename__ = 'table4'

    id = Column(Integer, primary_key=True)
    type = Column(Text, nullable=False)


class Table5(Base):
    __tablename__ = 'table5'

    id = Column(Integer, primary_key=True)
    res_id = Column(Integer, ForeignKey('table4.id'), nullable=False)
    value = Column(Text, nullable=False)

class Table1Map(Base):
    __tablename__ = 'table1_map'

    id = Column(Integer, ForeignKey('table4.id'), primary_key=True, nullable=False)
    map_id = Column(Integer, ForeignKey('table1.id'), primary_key=True, unique=True, nullable=False)

class Table2Map(Base):
    __tablename__ = 'table2_map'

    id = Column(Integer, ForeignKey('table4.id'), primary_key=True, nullable=False)
    map_id = Column(Integer, ForeignKey('table2.id'), primary_key=True, unique=True, nullable=False)
    
class Table3Map(Base):
    __tablename__ = 'table3_map'

    id = Column(Integer, ForeignKey('table4.id'), primary_key=True, nullable=False)
    map_id = Column(Integer, ForeignKey('table3.id'), primary_key=True, unique=True, nullable=False)


sub_query = select([Table5.__table__.c.id]).where(Table5.__table__.c.value=='somevalue')
subquery_1 = select([Table1.__table__.c.uuid.label("map_id"), Table1Map.__table__.c.id.label("id")]).select_from(Table1.__table__.join(Table1Map.__table__, Table1Map.__table__.c.map_id==Table1.__table__.c.id)).where(Table1Map.__table__.c.id.in_(sub_query))

subquery_2 = select([Table2.__table__.c.uuid.label("map_id"), Table2Map.__table__.c.id.label("id")]).select_from(Table2.__table__.join(Table2Map.__table__, Table2Map.__table__.c.map_id==Table2.__table__.c.id)).where(Table2Map.__table__.c.id.in_(sub_query))

subquery_3 = select([Table3.__table__.c.uuid.label("map_id"), Table3Map.__table__.c.id.label("id")]).select_from(Table3.__table__.join(Table3Map.__table__, Table3Map.__table__.c.map_id==Table3.__table__.c.id)).where(Table3Map.__table__.c.id.in_(sub_query))

main_query = union(subquery_1, subquery_2, subquery_3)

print(main_query)

这会产生以下查询。我需要避免这个子查询被重复执行多次。

SELECT TABLE1.UUID AS MAP_ID,
    TABLE1_MAP.ID AS ID
FROM TABLE1
JOIN TABLE1_MAP ON TABLE1_MAP.MAP_ID = TABLE1.ID
WHERE TABLE1_MAP.ID IN
        (SELECT TABLE5.ID
            FROM TABLE5
            WHERE TABLE5.VALUE = 'some_value')
UNION
SELECT TABLE2.UUID AS MAP_ID,
    TABLE2_MAP.ID AS ID
FROM TABLE2
JOIN TABLE2_MAP ON TABLE2_MAP.MAP_ID = TABLE2.ID
WHERE TABLE2_MAP.ID IN
        (SELECT TABLE5.ID
            FROM TABLE5
            WHERE TABLE5.VALUE = 'some_value')
UNION
SELECT TABLE3.UUID AS MAP_ID,
    TABLE3_MAP.ID AS ID
FROM TABLE3
JOIN TABLE3_MAP ON TABLE3_MAP.MAP_ID = TABLE3.ID
WHERE TABLE3_MAP.ID IN
        (SELECT TABLE5.ID
            FROM TABLE5
            WHERE TABLE5.VALUE = 'some_value')

【问题讨论】:

    标签: postgresql sqlalchemy query-optimization python-3.6


    【解决方案1】:

    为什么?您是否充分运行explain (analyze, buffers) 以表明它实际上导致了性能问题。重复执行很可能已经在内存中找到了必要的值,因此不需要额外的 IO。但是,在 Postgres 中完成此操作的方法是从 CTE 中的 table5 中选择值:(抱歉,我不知道您的混淆管理器 Python-Sqlalchemy)。

    with cte (id) as 
         (select id 
            from table5 t5
           where t5.value = 'some_value'
         )          
    select t1.uuid as map_id 
           t1m.id as id
      from table1 t1
      join table1_map on t1m.id = t1.id
      where t1m.id in
            (select  id
                from cte
            )
    select t2.uuid as map_id 
           t2m.id as id
      from table2 t2
      join table2_map on t2m.id = t2.id
      where t2m.id in
            (select  id
                from cte
            )  
    select t3.uuid as map_id 
           t3m.id as id
      from table3 t3
      join table3_map on t3m.id = t3.id
      where t3m.id in
            (select  id
                from cte
            );  
    

    请注意,您仍然需要重复子选择(仅引用 CTE)。如果您坚持删除任何重复项,您当然可以在子选择中执行并集,然后过滤 id。

    select uuid, id
      from (select t1.uuid 
                  , t1.id    
               from table1 t1 
            union 
            select t2.uuid 
                 , t2.id    
              from table2 t2
            union
            select t3.uuid 
                 , t3.id    
              from table3 t3 
           )  tall 
    where tall.id in 
          (select t5.id
             from table5 t5
            where t5.value = 'some_value'
          );
    

    无论哪种方式,请运行 explain 以查看在您的环境中实际表现最佳的内容。 (确保它有生产量。如果你的生产有 100K 行,IE 不会对有 100 行的表运行测试)。

    未测试。

    注意: 列名 uuid 是个坏主意。 Postgres 支持原生数据类型uuid。名称选择不当会导致混乱(开发人员不是 Postgres),而混乱会导致错误。通常直到成为关键的生产问题才被发现。

    【讨论】:

    • 谢谢@Belayer。 CTE 有帮助,是的,我已经运行了解释分析,并且计划每次都执行子查询。与已解决的 CTE。此外,我正在测试 table5 中的 1200 万行,所有其他表都有 100 万行。我有所有这些表的索引。
    • 让我们选择select t3.uuid as map_id t3m.id as id from table3 t3 join table3_map on t3m.id = t3.id where t3m.id in (select id from cte); 我有t3m.idt3.id 的索引。在计划中t3m.id = t3.id(索引扫描)和CTE扫描之间的in操作不是那么快。是的,它比每次执行子查询都快。但是CTE扫描和索引扫描之间的嵌套循环操作比索引扫描到索引扫描要慢。我们可以做些什么来进一步优化这个查询?
    猜你喜欢
    • 2014-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    相关资源
    最近更新 更多