zoukankan      html  css  js  c++  java
  • Django model 层之聚合查询总结

    Django model 层之聚合查询总结

    by:授客 QQ1033553122

     

    实践环境

    Python版本:python-3.4.0.amd64

    下载地址:https://www.python.org/downloads/release/python-340/

     

     

    Win7 64位

     

    Django  1.11.4

    下载地址:https://www.djangoproject.com/download/

     

     

    聚合查询

    MySQL数据库为例,假设项目目录结构如下:

    mysite/

    myapp/

        __init__.py

    admin.py

    apps.py

    migrations/

        __init__.py

    models.py

    tests.py

    views.py

        manage.py
        mysite/
            __init__.py
            settings.py
            urls.py
            wsgi.py

     

     

    models.py内容如下:

    from django.db import models

    # Create your models here.

    class Person(models.Model):

       first_name = models.CharField(max_length=30)

       last_name = models.CharField(max_length=30)

     

    class Book(models.Model):

        book_name = models.CharField(max_length=30)

        borrower = models.ForeignKey(Person, to_field='id', on_delete=models.CASCADE)

     

    class Store(models.Model):

        id = models.AutoField(primary_key=True)

        name = models.CharField(max_length=50)

        last_update = models.DateField(auto_now=True)

     

     

    class Production_addr(models.Model):

        addr = models.CharField(max_length=50)

        distance = models.IntegerField()

       

    class Fruit(models.Model):

        store = models.ManyToManyField(Store)

        production_addr = models.ForeignKey(Production_addr, to_field='id', on_delete=models.CASCADE)

        name = models.CharField(max_length=100)

        onsale_date = models.DateField()

        price = models.IntegerField()

     

    class Taste(models.Model):

        taste = models.CharField(max_length=50)

        fruit=models.ManyToManyField(Fruit)

     

    class News(models.Model):

       title = models.CharField(max_length=20)

       n_comments = models.IntegerField()

       n_pingbacks = models.IntegerField()

       rank = models.IntegerField()

     

    class Blog(Book):

       author = models.CharField(max_length=50)

     

    针对整个QuerySet生成聚合

    例:查询myapp_news表中,rank值大于26的记录,其n_comments平均值

    >>> from myapp.models import News

    >>> from django.db.models import Avg

    >>> News.objects.filter(rank__gt=26).aggregate(Avg('n_comments'))

    {'n_comments__avg': 17.0}

     

    如果是针对所有记录求均值,我们可以这样

    >>> News.objects.all().aggregate(Avg('n_comments'))

    {'n_comments__avg': 23.0}

     

    也可以去掉all()

    >>> News.objects.aggregate(Avg('n_comments'))

    {'n_comments__avg': 23.0}

     

    返回结果说明 {'聚合结果值标识':'聚合结果值'}

     

    自定义聚合结果标识

    >>> News.objects.all().aggregate(average_price = Avg('n_comments'))

    {'average_price': 23.0}

     

    >>> from django.db.models import Avg, Max, Min

    >>> News.objects.aggregate(Avg('n_comments'), Max('n_comments'), Min('n_comments'))

    {'n_comments__max': 35, 'n_comments__min': 14, 'n_comments__avg': 23.0}

     

    针对整个QuerySet的每项生成聚合

    可以理解为mysql中的分组统计,Model.objects.annotate(……) ,不过不一样的是,这里没有指定分组字段,是按每个model对象分组。

     

    例子:Fruit和Store model存在多对多关系。现在需要查询,myapp_fruit表中某条记录(可以理解为每类水果),有多少家商店在出(myapp_store表中每条记录对应一个商店)

    >>> from myapp.models import Fruit, Production_addr, Store, Taste

    >>> from django.db.models import Count

    >>> q = Fruit.objects.annotate(Count('store'))

    >>> q[0]

    <Fruit: Fruit object>

    >>> q[0].store__count

    1

    >>> q[1].store__count

    3

    >>> q[2].store__count

    1

     

    默认的,annotation标识由aggregate函数及被聚合field而来(例中为store__count),类似aggregate, 可以自定义annotation标识

    >>> q = Fruit.objects.annotate(store_num = Count('store'))

    >>> q[0].store_num

    1

    >>> 

     

    和aggregate不同的是,annotate()语句输出结果为QuerySet,支持其它QuerySet操作,包括filter(),order_by(),甚至是再次调用annotate()

    >>> q = Fruit.objects.annotate(store_num = Count('store')).filter(id__gt=3)

    >>> q[0]

    <Fruit: Fruit object>

    >>> q[0].store_num

    3

     

    混用多个聚合函数

    使用annotate()函数,混用多个聚合函数,会返回错误的结果,因为实现使用的是join查询,而非子查询。针对count聚合函数,可以使用distinct=True参数避免这个问题

     

    例子:检索myapp_fruit表中第一个条记录,查询出售该类水果的商店数及该类水果的口味总数。

    >>> fruit = Fruit.objects.first()

    >>> fruit.store.count()

    2

    >>> fruit.taste_set.count()

    3

     

    >>> from django.db.models import Count

    >>> q = Fruit.objects.annotate(Count('store'), Count('taste'))

    >>> q[0].store__count

    6

    >>> q[0].taste__count

    6

     

    解决方法:

    >>> q = Fruit.objects.annotate(Count('store', distinct=True), Count('taste', distinct=True))

    >>> q[0].taste__count

    3

    >>> q[0].store__count

    2

    >>> 

     

    联合查询与聚合

    有时候,需要获取和当前正在查询模块关联的另一个模块的相关聚合值,这个时候,可在聚合函数中,指定字段使用双下划线方式,关联相关模块进行join查询

     

    例子:检索myapp_store表,查询每个商店正在出售水果种类中,最高价和最低价。

    >>> q = Store.objects.annotate(min_price=Min('fruit__price'), max_price=Max('fruit__price'))

    >>> for item in q:

    ...     print(item.min_price, item.max_price)

    ...

    10 20

    19 20

    None None

    None None

    None None

    None None

     

    联合查询的深度取决于你的查询要求。

    例子:检索myapp_store表,查询每个商店正在出售水果种类中,产地最远是多远。

    >>> from django.db.models import Min, Max

    >>> q = Store.objects.annotate(max_distance=

    Min('fruit__production_addr__distance'))

     

    >>> for item in q:

    ...     print(item.name, item.max_distance)

    ...

    aimi 40

    ximi 20

    xima None

    masu None

    gami None

    gama None

     

    反向关联查询

    例:查询每个产地的水果种类数量(myapp_production_addr.id是myapp_fruit表的外键)

    >>> q = Production_addr.objects.annotate(cnt=Count('fruit'))

    >>> for item in q:

    ...     print(item.addr, item.cnt)

    ...

    changting 1

    shanghang 1

    longyan 1

     

    例,检索所有产地产出的水果种类,最小价格

    >>> q = Production_addr.objects.aggregate(min_price=Min('fruit__price'))

    >>> print(q)

    {'min_price': 10}

     

    对比(分组统计):

    >>> q = Production_addr.objects.annotate(min_price=Min('fruit__price'))

    >>> for item in q:

    ...     print(item.addr, item.min_price)

    ...

    changting 20

    shanghang 16

    longyan 10

     

    不仅仅是针对外键,针对多对多关系也可以

    >>> from django.db.models import Avg

    >>> q = Taste.objects.annotate(avg_price=Avg('fruit__price'))

    >>> for item in q:

    ...     print(item.taste, item.avg_price)

    ...

    sour 20.0

    sweet 20.0

    bitter 20.0

     

     

    聚合及其它QuerySet语句

    filter()exclude()

    例子:统计myapp_fruit表中banana除外的水果种类的最小价

    >>> Fruit.objects.exclude(name='banana').aggregate(Min('price'))

    {'price__min': 16}

     

    filter也支持类似用法

     

    Filtering on annotations

    例子:检索myapp_store表,查询每个商店正在出售水果种类中最低价,过滤最低价小于等于10的。

    >>> Store.objects.annotate(min_price=Min('fruit__price')).filter(min_price__gt=10)

     

    说明:先执行annotate,得到结果集,然后执行filter语句,得出结果。

     

    注意:annotationsfilter()exclude()语句是有先后顺序之分的,后一步的处理依赖前一步的结果,顺序不一样,结果可能也会也不一样。

     

    order_by()

    例子:检索myapp_store表,查询每个商店正在出售水果种类中最低价,并按最低价升许排序。

    >>> Store.objects.annotate(min_price=Min('fruit__price')).order_by('min_price')

    <QuerySet [<Store: Store object>, <Store: Store object>, <Store: Store object>,<Store: Store object>, <Store: Store object>, <Store: Store object>]>

     

    values()

    values()结合annotate的使用

     

    例子:检索myapp_store表,按商店名称分组查询商店正在出售水果种类中最低价

    >>> Store.objects.values('name').annotate(min_price=Min('fruit__price'))

    <QuerySet [{'min_price': 10, 'name': 'aimi'}, {'min_price': 19, 'name': 'ximi'}, {'min_price': None, 'name': 'xima'}, {'min_price': None, 'name': 'masu'}, {'min_price': None, 'name': 'gami'}, {'min_price': None, 'name': 'gama'}]>

    >>> 

     

     

    可以理解为mysql中的分组统计,values('filed')中指定filed即为分组统计字段

     

    注意:类似filter()valuesannotate也有先后顺序之分。

     

    annotateaggregate配合使用

    例:

    >>> Store.objects.values('name').annotate(min_price=Min('fruit__price')).aggregate(Avg('min_price'))

    {'min_price__avg': 14.5}

     

    说明,链式处理

     

    其它例子

    参考链接:https://www.cnblogs.com/YingLai/p/6601243.html

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

     

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))

    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

     

    v= models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)

    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

     

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)

    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

     

     

    更多详情,参考链接:

    https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#

     

  • 相关阅读:
    arm,iptables: No chain/target/match by that name.
    Windows7-USB-DVD-tool提示不能拷贝文件的处理
    WPF实现WORD 2013墨迹批注功能
    windows下实现屏幕分享(C#)
    Owin WebAPI上传文件
    js 下不同浏览器,new Date转换结果时差
    jquery 动态增加的html元素,初始化设置在id或class上的事件无效
    WPF DataGrid模拟click实现效果
    基于Bootstrap的步骤引导html页面
    XWalkView+html 开发Android应用
  • 原文地址:https://www.cnblogs.com/shouke/p/13462945.html
Copyright © 2011-2022 走看看