首页 > 编程学习 > 正反向查询进阶操作、聚合查询、分组查询、F与Q查询、ORM查询优化

目录
  • 正反向查询进阶操作
  • 聚合查询
    • 1.使用聚合函数之前需要导入模块
    • 2.聚合函数的使用
    • 3.关键字aggregate
  • 分组查询
    • 1.分组查询
    • 2.返回值
    • 3.分组查询关键字
  • 分组练习题
  • F与Q查询
    • F查询
    • Q查询
      • Q查询的进阶用法
  • ORM查询优化
    • 1.ORM查询优化之only(单表)
    • 2.ORM查询优化之defer(单表)
    • 3.ORM查询优化之select_related(多表)
    • 4.ORM查询优化之prefetch_related(多表)
  • 额外补充个知识:当表中已经有数据的情况下 添加新的字段需要指定一些参数
  • 报错问题

正反向查询进阶操作

    '''正反向查询进阶操作'''
    # 1.查询主键为1的书籍对应的出版社名称及书名
    res = models.Publish.objects.filter(book__pk=1).values('name', 'book__title')
    print(res)  # <QuerySet [{'name': '清华大学出版社', 'book__title': '女人你成功引起了我的注意'}]>

    # 2.查询主键为3的书籍对应的作者姓名及书名
    res1 = models.Author.objects.filter(book__pk=3).values('name', 'book__title')
    print(res1)  # <QuerySet [{'name': 'jfw', 'book__title': '凉生,我们可不可以不忧伤'}]>

    # 3.查询zxb的作者的电话号码和地址
    res2 = models.AuthorDetail.objects.filter(author__name='zxb').values('phone', 'addr')
    print(res2)  # <QuerySet [{'phone': 119, 'addr': '浙江'}]>

    # 4.查询东方出版社出版的书籍名称和价格
    res4 = models.Book.objects.filter(publish__name='东方出版社').values('title', 'price')
    print(res4)  # <QuerySet [{'title': '桃疆', 'price': Decimal('14.67')}, {'title': '三生三十十里桃花', 'price': Decimal('35.98')}, {'title': '玄霜', 'price': Decimal('54.21')}]>

    # 5.查询zxr写过的书的名称和日期
    res5 = models.Book.objects.filter(author__name='zxb').values('title', 'publish_time')
    print(res5)  # <QuerySet [{'title': '女人你成功引起了我的注意', 'publish_time': datetime.datetime(2022, 9, 5, 13, 16, 43, 303992, tzinfo=<UTC>)}]>

    # 6.查询电话是110的作者姓名和年龄
    res6 = models.Author.objects.filter(author_detail__phone=110).values('name', 'age')
    print(res6)  # <QuerySet [{'name': 'zxr', 'age': 18}]>

    # 7.查询主键为1的书籍对应的作者电话号码
    res7 = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
    print(res7)  # <QuerySet [{'phone': 120}, {'phone': 119}]>
    res8 = models.Author.objects.filter(book__pk=1).values('author_detail__phone')
    print(res8)  # <QuerySet [{'author_detail__phone': 120}, {'author_detail__phone': 119}]>

聚合查询

聚合函数:max、min、sum、avg、count

1.使用聚合函数之前需要导入模块

from django.db.models import Max, Min, Sum, Avg, Count

2.聚合函数的使用

聚合函数通常情况下是配合分组一起使用的

3.关键字aggregate

没有分组之前如果单纯的使用聚合函数 需要关键字aggregate

# 1.查询所有书的平均价格
res = models.Book.objects.aggregate(Avg('price'))
print(res)  # {'price__avg': Decimal('183.117778')}

image

res1 = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
print(res1)

image

分组查询

image
报错原因
image

打开MySQL
输入>>>:show variables like '%mode%';

分组有一个特性 默认只能够直接获取分组的字段 其他字段需要使用方法
我们也可以忽略掉该特性 将sql_mode中only_full_group_by配置移除即可
# 示例1:统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('author__pk')).values('title', 'author_num')
print(res)

"""
    1.按照整条数据分组
        models.Book.objects.annotate()  按照一条条书籍记录分组
    2.按照表中某个字段分组()
        models.Book.objects.values('title').annotate()  按照annotate之前values括号中指定的字段分组
"""

"""
<QuerySet [{'title': '女人你成功引起了我的注意爆款~爆款', 'author_num': 2},
{'title': '凉生,我们可不可以不忧伤爆款~爆款', 'author_num': 1},
{'title': '桃疆爆款~爆款', 'author_num': 2},
{'title': '我是你的,生生世世爆款~爆款', 'author_num': 2},
{'title': '国民大侦探爆款~爆款', 'author_num': 1},
{'title': '十宗罪爆款~爆款', 'author_num': 1},
{'title': '三生三十十里桃花爆款~爆款', 'author_num': 1},
{'title': '玄霜爆款~爆款', 'author_num': 1},
{'title': '一生一世爆款~爆款', 'author_num': 1}]>
"""

1.分组查询

annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数,所有使用前要从django.db.models引入Avg,Max,Count,Sum(首字母大写))。

2.返回值

分组后,用values取值,则返回值是QuerySet书籍类型里面为一个个字典
分组后,用values_list取值,则返回值是QuerySet数据类型里面为一个个元组

MySQL中的limit相当于ORM中的QuerySet数据类型的切片。

3.分组查询关键字

annotate()  里面放聚合函数

1.values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。
2.values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。
3.filter放在 annotate 前面:表示where条件
4.filter放在annotate后面:表示having

跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询

分组练习题

models后面点什么 就按什么分组 

author_num 是我们自己定义的字段 用来存储统计出来的每本书对应的作者个数

1.统计每一本书的作者个数

res = models.Book.objects.annotate(author_num=Count('author__pk')).values('title', 'author_num')
print(res)
res1 = models.Book.objects.values('publish_id').annotate(book_num=Count('pk')).values('publish_id','book_num')
print(res1)
"""
    1.按照整条数据分组
        models.Book.objects.annotate()  按照一条条书籍记录分组
    2.按照表中某个字段分组()
        models.Book.objects.values('title').annotate()  按照annotate之前values括号中指定的字段分组
"""
"""
<QuerySet [{'title': '女人你成功引起了我的注意', 'author_num': 2},
{'title': '凉生,我们可不可以不忧伤', 'author_num': 1},
{'title': '桃疆', 'author_num': 2},
{'title': '我是你的,生生世世', 'author_num': 2},
{'title': '国民大侦探', 'author_num': 1},
{'title': '十宗罪', 'author_num': 1},
{'title': '三生三十十里桃花', 'author_num': 1},
{'title': '玄霜', 'author_num': 1},
{'title': '一生一世', 'author_num': 1}]>
"""

image

2.统计每个出版社卖的最便宜的书的价格

res1 = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res1)
"""
<QuerySet [{'name': '清华大学出版社',
'min_price': Decimal('28.98')},
{'name': '北方出版社', 'min_price': Decimal('34.98')},
{'name': '南方出版社', 'min_price': Decimal('56.29')},
{'name': '东方出版社', 'min_price': Decimal('14.67')}]>
"""

image
3.统计不止一个作者的图书

'''filter在annotate前面则是where 在annotate后面则是having'''
res = models.Book.objects.annotate(author_num=Count('author__pk')).filter(author_num__gt=1).values('title', 'author_num')
print(res)

image
4.查询各个作者出的书的总价格

res = models.Author.objects.annotate(book_sum_price=Sum('book__price')).values('name', 'book_sum_price')
print(res)

"""
<QuerySet [{'name': 'zxr', 'book_sum_price': Decimal('159.25')},
{'name': 'wyn', 'book_sum_price': Decimal('1365.63')},
{'name': 'zxb', 'book_sum_price': Decimal('1383.86')},
{'name': 'jfw', 'book_sum_price': Decimal('103.95')}]>
"""

image

F与Q查询

F查询

能够帮助你直接获取到列表中某个字段对应的数据

注意:
在操作字符串类型的数据的时候, F不能够直接做到字符串的拼接

1.查询卖出书大于库存数的书籍

res = models.Book.objects.filter(storage_num__gt=F('sale_num'))
print(res)

image
将所有书籍的价格上涨1000快

res = models.Book.objects.update(price=F('price') + 1000)

image
image
给所有书籍名称后面加上爆款后缀
针对字符串数据无法直接拼接
image

models.Book.objects.filter().all().update(title=F('title') + '爆款')  # 针对字符串数据无法直接拼接
    # 导入Concat模块
    from django.db.models.functions import Concat
    # 导入Value模块
    from django.db.models import Value
    models.Book.objects.filter().all().update(title=Concat(F('title'), Value('~爆款')))

image

Q查询

可以改变filter括号内多个条件之间的逻辑运算符,还可以将查询的结果改为字符串形式

filter()等方法中的关键字参数查询都是一起进行"and",如果你需要执行更复杂的查询(列如ORM语句),你可以使用Q对象。

查询卖出数大于100并且价格小于60的书籍

res = models.Book.objects.filter(Q(sale_num__gt=100), Q(price__lt=600))
print(res)

image

查询卖出数大于130或者价格小于30的书籍

res = models.Book.objects.filter(Q(sale_num__gt=130) | Q(price__lt=30))
print(res)

image

查询卖出数不是大于80或者价格大于600的书籍

res = models.Book.objects.filter(~Q(sale_num__gt=80) | Q(price__gt=600))
print(res)

image
总结

,               and关系
|               or关系
~               not关系

Q 默认是and,可以进行修改
    q = Q()
    q.connector = 'or'

Q查询的进阶用法

可以将查询的结果改为字符串形式

先产生一个对象
    q = Q()
修改默认的对象
    q.connector = 'or'
添加查询条件,第一个写一个字符串的字段名 第二个元素写一些具体的数值
    q.children.append(('pk', 1))
    q.children.append(('publish_id', 3))
添加好以后q对象支持直接用filter进行筛选
    res = models.Book.objects.filter(q)
    print(res)

image

image

ORM查询优化

django orm默认都是惰性查询
    当orm的语句在后续的代码中真正需要使用的时候才会执行
django orm自带limit分页
    减轻数据库端以及服务端的压力

1.ORM查询优化之only(单表)

res = models.Book.objects.only('title','price')
for obj in res:
     print(obj.title)
     print(obj.price)
     print(obj.publish_time)
    """
    only会将括号内填写的字段封装成一个个数据对象 对象在点击的时候不会再走数据库查询
    但是对象也可以点击括号内没有的字段 只不过每次都会走数据库查询 
    如果看到有人只有only查询的话 尽量不要去点括号内没有的字段 因为它每次都会去查询一次
    """

image
image

2.ORM查询优化之defer(单表)

res = models.Book.objects.defer('title', 'price')
for obj in res:
    # print(obj.title)
    # print(obj.price)
    print(obj.publish_time)
    """
    defer与only刚好相反
        数据对象点击括号内出现的字段 每次都会走数据库查询
        数据对象点击括号内没有的字典 不会走数据库查询
    """

image

3.ORM查询优化之select_related(多表)

res = models.Book.objects.all()
for obj in res:
     print(obj.publish.name)  # 频繁走数据库查询

res = models.Book.objects.select_related('publish')
"""
    select_related括号内只能接收外键字段(一对多 一对一) 它会在内部自动拼表 得出的数据对象在点击表中数据的时候都不会再走数据库查询
"""

image
image

4.ORM查询优化之prefetch_related(多表)

for obj in res:
     print(obj.publish.name)
"""
    prefetch_related底层其实是子查询 将查询之后的结果也一次性封装到数据对象中 用户在使用的时候是感觉不出来的
"""
res = models.Book.objects.prefetch_related('publish')
for obj in res:
    print(obj.publish.name)

image

额外补充个知识:当表中已经有数据的情况下 添加新的字段需要指定一些参数

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_time = models.DateTimeField(auto_now=True)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')
    '''
    当表中已经有数据的情况下 添加新的字段需要指定一些参数
        1.设置字段值存于为空    null=True
        2.设置字段默认值    default=1000
        3.在终端直接给出默认值
    '''
    storage_num = models.IntegerField(verbose_name='库存数', null=True)
    sale_num = models.IntegerField(verbose_name='卖出数目', default=1000)

    def __str__(self):
        return f'对象:{self.title}'

报错问题

django.db.utils.OperationalError: (2002, "Can't connect to server on '127.0.0.1' (10061)")
image
解决方法:查看数据库是否启动,导致这个错误很可能是MySQL停止了服务

Copyright © 2010-2022 mfbz.cn 版权所有 |关于我们| 联系方式|豫ICP备15888888号