我正在尝试弄清楚如何在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
示例:
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
})
答案 0 :(得分:1)
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中求值为False
或True
,因为__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)