我有一个数据库,除其他外,记录成分之间的反应结果。它目前由以下三个表格构成:
| Material |
|----------------|
| id : Integer |
| name : Varchar |
| Reaction |
|-----------------|
| id : Integer |
| <other details> |
| Ingredient |
|-----------------------|
| material_id : Integer |
| reaction_id : Integer |
| quantity : Real |
这映射了材料和反应之间的多对多关系。
我想运行一个返回每对不会产生反应的材料的查询。 (即每对( x , y ),使得没有任何反应完全使用 x 和 y 没有其他材料。)在其他情况下,我会通过LEFT JOIN进入中间表,然后查找NULL reaction_id
s。在这种情况下,我通过在材质表和它自身上进行CROSS JOIN来获得对,但是我不确定如何(或者是否)在两个材质上做两个LEFT JOIN可以起作用。
如何做到这一点?
我最感兴趣的是一种通用的SQL方法,但我目前正在使用SQLite3和SQLAlchemy。我可以选择将数据库移动到PostgreSQL,但SQLite是首选。
答案 0 :(得分:2)
使用cross join
生成列表,然后删除同一反应中的对。
select m.id, m2.id as id2
from materials m cross join
materials m2
where not exists (select 1
from ingredient i join
ingredient i2
on i.reaction_id = i2.reaction_id and
i.material_id = m.id and
i2.material_id = m2.id
);
虽然此查询看起来很复杂,但它本质上是您问题的直接翻译。 where
条款是说每种材料的相同反应没有两种成分。
为了提高性能,您需要ingredient(reaction_id, material_id)
上的索引。
编辑:
如果您愿意,可以使用exists
和left join
1}}在没有where
的情况下执行此操作:
select m.id, m2.id
from materials m cross join
materials m2 left join
ingredients i
on i.material_id = m.id left join
ingredients i2
on i2.material_id = m2.id and
i2.reaction_id = m2.reaction_id
where i2.reaction_id is null;