Django 如何使用 Exists

时间:2022-12-08 20:02:39

Django ORM 模型

class Base(models.Model):
    set_null = {
        'null': True,
        'blank': True
    }

    set_fk = {
        'db_constraint': False,
        'on_delete': models.SET_NULL
    }
    set_mtm = {
        'db_constraint': False,
    }

    class Meta:
        abstract = True


class Student(Base):
    name = models.CharField(max_length=10, **Base.set_null)
    age = models.IntegerField(**Base.set_null)
    classes = models.ManyToManyField('Class', related_name='student_class', **Base.set_mtm)

    class Meta:
        db_name = 'student'


class Class(Base):
    name = models.CharField(max_length=10, **Base.set_null)

    class Meta:
        db_name = 'classes'
        
 class Middle(Base):
    category = models.CharField(max_length=10, **Base.set_null)
    name = models.CharField(max_length=10, **Base.set_null)
    value = models.CharField(max_length=10, **Base.set_null)

    class Meta:
        db_table = 'tb_middle'


class A(Base):
    model = models.ForeignKey(
        'Middle', related_name='a_model', **Base.set_fk, **Base.set_null
    )

    class Meta:
        db_table = 'tb_a'


sql 中 where 使用

SELECT name FROM student WHERE age > 18;

在执行这条 SQL 语句的时候,DBMS 会扫描 student 表中的每一条记录,然后把符合 age 大于 18 这个条件的所有记录筛选出来,并放到结果集里面去。也就是说 WHERE 关键字的作用就是判断后面的逻辑表达式的值是否为 True。如果为 True,则将当前这条记录(经过 SELECT 关键字处理后)放到结果集里面去,如果逻辑表达式的值为 False 则不放。

sql 中的 exists

有个哥们讲的很适合初学者

概念:EXISTS 运算符用于判断查询子句是否有记录,如果有一条或多条记录存在返回 True,否则返回 False。

执行流程:先执行子查询,得到一个集合(或值),然后将这个集合(或值)作为一个常量带入到父查询的 WHERE 子句中去。

案例

查询选择课程是语文课程的所有学生名称

SELECT
	"app1_student"."name" 
FROM
	"app1_student" 
WHERE
	EXISTS (
	SELECT
		U0."id",
		U0."name" 
	FROM
		"app1_class" U0
		INNER JOIN "app1_student_classes" U1 ON ( U0."id" = U1."class_id" ) 
	WHERE
	( U0."name" = 语文 AND U1."student_id" = "app1_student"."id" ) 
	)

django 中 exists

官方文档

官方描述:Exists 是一个 Subquery 子类,它使用 SQL EXISTS 语句。在许多情况下,它的性能比子查询更好,因为当找到第一条匹配的记录时,数据库能够停止对子查询的执行。

案例一

上面的案例 Django 中可以这样写

# 方法一 原生sql 见上
queryset = Student.objects.filter(Exists(Class.objects.filter(student_class=OuterRef('pk'), name='语文'))).values('name')
# 方法二
queryset = Student.objects.filter(classes__name='语文').values('name')

方法二 sql

SELECT
	"app1_student"."name" 
FROM
	"app1_student"
	INNER JOIN "app1_student_classes" ON ( "app1_student"."id" = "app1_student_classes"."student_id" )
	INNER JOIN "app1_class" ON ( "app1_student_classes"."class_id" = "app1_class"."id" ) 
WHERE
	"app1_class"."name" = 语文

根据真实情况选择不同查询方式。遵循尽可能使用一次数据库交互数据数据。

案例二

获取 tb_middle 中 category 为 a_model 且排除已经和tb_a 表存在关联的表 tb_middle 字段 name value的数据

思路

  • 查询表 tb_middle 已经被使用的数据
  • filter中过滤掉被使用的数据

达到了效果,却需要查询两次数据库。而且,在书写python比较繁琐

Middle.objects.filter(category=category).filter(
                ~Exists(A.objects.filter(model=OuterRef('pk')))
            )
# Exists 存在 | ~Exists 不存在

下次更新 annotate subquery 使用方法...