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 使用方法...