如何在python中使用不同类型的联接进行sql-alchemy

时间:2019-06-26 13:35:45

标签: python sqlalchemy

我正在尝试弄清楚如何在sql-alchemy ORM中定义联接类型。如何使用左联接和左外联接?内部联接如何?

此查询用于可以选择所有不包含相关crm_task的crm_lead。我试过存在过滤器,但无法使用此子句过滤现有的crm_leads。

所需的SQL:

select *
from crm_lead l
join crm_task t on l.id = t.lead_id
left outer join crm_pipeline_status cps on l.pipeline_status_id = cps.id
where l.pipeline_status_id not in (142, 143) 
  and (t.id is null or t.is_completed is false);

OR :(如果存在则比较适合这种情况)

select *
from crm_lead l
left outer join crm_pipeline_status cps on l.pipeline_status_id = cps.id
where cps.crm_id not in (142, 143)
  and not exists (select id from crm_task t where l.id = t.lead_id and t.is_completed is false);

我最好的尝试是:

session = sessionmaker(bind=engine, autocommit=True)()
with session.begin():
    leads = session.query(CrmLead).outerjoin(CrmTask).outerjoin(CrmPipelineStatus).filter(
        and_(CrmLead.account_id == 2,
        CrmPipelineStatus.crm_id not in (142, 143),
        or_(CrmTask.is_completed is False, CrmTask.id is None))
    )

但它会转换为:

SELECT *
FROM crm_lead 
LEFT OUTER JOIN crm_task ON crm_lead.id = crm_task.lead_id 
LEFT OUTER JOIN crm_pipeline_status ON crm_pipeline_status.id = crm_lead.pipeline_status_id 
WHERE false

替代解决方案: 我的情况可以使用原始SQL来解决,如下所示[https://mmmmmmmm.nnnnnnnn.xxxxxx/oauth-signin?client_id=bbbnnnmmmm-bbnn-46ba-a287-nnhhyyy&redirect_uri=https://mmm.nnnn.nnnn/galaxy-backend/redirect/oauth/token&response_type=code&state=kYIL97&protocol=berlin-v13&consent_id=5d1372ebfb0b3a09b2ea4ddb&scope=accounts.accountDetails.read+accounts.transactions.read+accounts.balances.read

示例:

    query_text = '''
    select *
    from crm_lead l
    left outer join crm_pipeline_status cps on l.pipeline_status_id = cps.id
    where cps.crm_id not in (:success_final_status, :failed_final_status)
      and l.account_id = :account_id
      and not exists (select id from crm_task t where l.id = t.lead_id and t.is_completed is false);
    '''
    leads = session.execute(query_text, {
        'account_id': crm_configuration["instance_id"],
        'success_final_status': 142,
        'failed_final_status': 143
    })

3 个答案:

答案 0 :(得分:1)

也许this post

q = session.query(Table1.field1, Table1.field2)\
.outerjoin(Table2)\ # use in case you have relationship defined
# .outerjoin(Table2, Table1.id == Table2.table_id)\ # use if you do not have relationship defined
.filter(Table2.tbl2_id == None)

应该这样做,假设field1和field2来自Table1,并且您定义了一个关系:

class Table2(Base):
# ...
table1 = relationship(Table1, backref="table2s")

答案 1 :(得分:1)

表达式

CrmPipelineStatus.crm_id not in (142, 143)

在Python中求值为FalseTrue,因为__contains__()不能以与其他重载相同的方式使用。如果为False,则将整个封闭的and_()构造编译为简单的false。在这种情况下,正确的方法是使用方法notin_()

CrmPipelineStatus.crm_id.notin_([142, 143])

发生类似的问题

or_(CrmTask.is_completed is False, CrmTask.id is None)

由于身份运算符is不能重载,因此您可以有效地

or_(False, False)

,它将再次编译为false。对于第一个,您应该仅将布尔值用作布尔值(给定为NOT NULL),并且可以使用NULL方法或运算符中的特殊情况编写is_()支票重载:

or_(not_(CrmTask.is_completed), CrmTask.id == None)

答案 2 :(得分:0)

CamelCase用于小写表名的映射类:

from sqlalchemy.sql import exists

Session().query(CrmLead).join(CrmTask).outerjoin(CrmPipelineStatus).filter(CrmLead.pipeline_status_id == CrmPipelineStatus.id).filter(CrmPipelineStatus.crm_id.notin_([142, 143])).filter(~exists().where(and_(CrmTask.is_completed==False, CrmLead.id==CrmTask.lead_id)