在同一层次结构中使用Django ORM语法的UNION操作

1 投票
3 回答
1320 浏览
提问于 2025-04-16 18:38

我需要实现类似下面的功能:

 (SELECT table1.*, val=2 FROM table1 INNER JOIN table2 ON table1.id = table2.id WHERE some_condition)
 UNION
 (SELECT table1.*, val=3 FROM table1 INNER JOIN table3 ON table1.id = table3.id WHERE some_condition)

或者

 (SELECT val1, val2, val3, val=2 FROM table2 WHERE some_condition)
 UNION
 (SELECT val1, val2, val3, val=3 FROM table3 WHERE some_condition)

也就是说,我有三个类,分别叫'table1'、'table2'和'table3',其中'table2'和'table3'是从'table1'派生出来的。我想要选择这三个类的所有内容,并且还需要额外的字段。问题是,我希望避免使用原始的SQL查询,因为某些条件应该是可以重复使用的。如果我尝试使用额外的字段,它会抱怨我使用了.extra

3 个回答

0

你可以把它改写成一个子查询:

select val1, val2, val3, val4
from (
     SELECT val1, val2, val3, val=2 as val4 FROM table2
     UNION
     SELECT val1, val2, val3, val=3 as val4 FROM table3
     ) t
where some_condition

不过要小心,因为这不一定是最好的做法。

这样做可能让你觉得满意,因为你避免了重复写条件,但对于查询计划器来说,这两个查询可能是完全不同的情况。Postgres 有时候聪明到可以把条件放进子查询里,但我从没见过它在子查询中处理任何聚合函数时这样做。

具体来说,如果条件放在外面(就像上面那样),你会先把两个完整的表合并在一起。然后你会对它们进行聚合,以消除重复项(顺便说一下,这一步你可以通过使用 union all 而不是 union 来避免),最后你会在结果集中筛选出符合条件的行。

相反,如果条件放在每个小部分里面,你会合并并排序两个较小的行集。这样会快得多,而且占用的内存也会少很多。

总之,尽量在查询中尽早筛选出行。

1

如果你试图在不使用 .execute 的情况下写这个 JOINUNION,你会花很多时间在和 Django 的 ORM 接口斗争。如果这种情况很常见,建议你使用一个 VIEW,然后只需从你新创建的 VIEW 中简单地 SELECT 数据。

0

我决定换到sqlalchemy,因为我的问题不太适合用django来解决,而我更喜欢用tg2。并不是说django不好,只是它不太适合我现在要做的事情。

撰写回答