我想弄清楚为什么django ORM有这么奇怪(我认为)的行为。我有2个基本模型(简化以获得主要想法):
class A(models.Model):
pass
class B(models.Model):
name = models.CharField(max_length=15)
a = models.ForeignKey(A)
现在我想从表a
中选择从列b
引用但在列名中没有值的行。
以下是我希望Django ORM生成的示例SQL:
SELECT * FROM inefficient_foreign_key_exclude_a a
INNER JOIN inefficient_foreign_key_exclude_b b ON a.id = b.a_id
WHERE NOT (b.name = '123');
如果filter()
django.db.models.query.QuerySet
方法符合预期:
>>> from inefficient_foreign_key_exclude.models import A
>>> print A.objects.filter(b__name='123').query
SELECT `inefficient_foreign_key_exclude_a`.`id`
FROM `inefficient_foreign_key_exclude_a`
INNER JOIN `inefficient_foreign_key_exclude_b` ON (`inefficient_foreign_key_exclude_a`.`id` = `inefficient_foreign_key_exclude_b`.`a_id`)
WHERE `inefficient_foreign_key_exclude_b`.`name` = 123
但是如果我使用exclude()
方法(在底层逻辑中使用Q形式的负面形式),它会创建一个非常奇怪的SQL查询:
>>> print A.objects.exclude(b__name='123').query
SELECT `inefficient_foreign_key_exclude_a`.`id`
FROM `inefficient_foreign_key_exclude_a`
WHERE NOT ((`inefficient_foreign_key_exclude_a`.`id` IN (
SELECT U1.`a_id` FROM `inefficient_foreign_key_exclude_b` U1 WHERE (U1.`name` = 123 AND U1.`a_id` IS NOT NULL)
) AND `inefficient_foreign_key_exclude_a`.`id` IS NOT NULL))
为什么ORM会创建子查询而不仅仅是JOIN?
更新:
我做了一个测试来证明使用子查询根本没有效率。
我在a
和b
表中创建了500401行。在这里我得到了:
加入:
mysql> SELECT count(*)
-> FROM inefficient_foreign_key_exclude_a a
-> INNER JOIN inefficient_foreign_key_exclude_b b ON a.id = b.a_id
-> WHERE NOT (b.name = 'abc');
+----------+
| count(*) |
+----------+
| 500401 |
+----------+
1 row in set (0.97 sec)
对于子查询:
mysql> SELECT count(*)
-> FROM inefficient_foreign_key_exclude_a a
-> WHERE NOT ((a.id IN (
-> SELECT U1.`a_id` FROM `inefficient_foreign_key_exclude_b` U1 WHERE (U1.`name` = 'abc' AND U1.`a_id` IS NOT NULL)
-> ) AND a.id IS NOT NULL));
+----------+
| count(*) |
+----------+
| 500401 |
+----------+
1 row in set (3.76 sec)
加入快了近4倍。
答案 0 :(得分:0)
看起来这是一种优化。
虽然filter()
可以是'任意'条件,但它会进行连接并应用限制。
exclude()
限制性更强,因此您不必强制加入表,它可以使用子查询构建查询,我认为这会使查询更快(由于索引使用)。
如果您使用的是MySQL,可以在查询中使用explain
命令,看看我的建议是否正确。