1. 程式人生 > 實用技巧 >聚合分組查詢、F與Q查詢、常用欄位知識

聚合分組查詢、F與Q查詢、常用欄位知識

今日內容概要

  • 聚合查詢(聚合函式的使用)aggregate

    max、min、sum、count、avg

  • 分組查詢(group by的使用)annotate

  • F與Q查詢

  • django中如何開啟事務

  • orm中常用欄位及引數

  • 資料庫查詢優化(only與defer、select_related與prefetch_related)

  • 圖書管理系統作業講解

聚合查詢

"""
聚合查詢通常情況下都是配合分組一起使用的,只要是跟資料庫相關的模組 ,基本上都在django.db.models裡面,如果上述沒有那麼應該在django.db裡面
"""

 #聚合查詢 aggregate
from django.db.models import Max,Min,Sum,Avg,Count #所有書的平均價格 res = models.Book.objects.aggregate(Avg('price')) # print(res) res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Count('pk'), Avg('price')) print(res)

分組查詢

    # 分組查詢  annotate
    # 1.統計每一本書的作者個數
res = models.Book.objects.annotate(author_num = Count('author')).values('title','author_num') print(res) #2.正常情況下應該是需要在Count內部的author後面加上__id,因為是反向查詢,需要跨表到作者表中去查個數,如下所示,但是orm自動簡化,只需要寫author即可 res = models.Book.objects.annotate(author_num = Count('author__id')).values('title','author_num') print(res)
#統計每個出版社賣的最便宜的書的價格 res = models.Publish.objects.annotate(min_price = Min('book__price')).values('name','min_price') print(res) # 3.統計不止一個作者的圖書 # """ # 只要你的orm語句得出的結果還是一個queryset物件 # 那麼它就可以繼續無限制的點queryset物件封裝的方法 # """ res = models.Book.objects.annotate(author_num = Count('author')).filter(author_num__gt=1).values('title','author_num') print(res) # 4.查詢每個作者出的書的總價格 res = models.Author.objects.annotate(price_sum = Sum('book__price')).values('name','price_sum') print(res) #補充幾個額外的知識點: #1.如果我想按照指定的欄位分組該如何處理呢? # models.Book.objects.values('price').annotate() 先將要指定的欄位拿出來,再進行分組 #2.你們的機器上如果出現分組查詢報錯的情況怎麼辦?你需要修改資料庫嚴格模式

F與Q查詢



    # F查詢
    """
            能夠幫助你直接獲取到表中某個欄位對應的資料
    """
    #1.查詢賣出數大於庫存數的書籍
    from django.db.models import F
    # #不需要查詢兩次,先查詢書籍的賣出數,再查詢庫存數,然年後將兩者進行比較。利用F查詢可以直接將要查詢的欄位的資料拿出
    # res = models.Book.objects.filter(maichu__gt=F('kucun'))
    # print(res)
    # 2.將所有書籍的價格提升100塊
    res = models.Book.objects.update(price = F('price')+100)
    # 3.將所有書的名稱後面加上爆款兩個字
    """
        在操作字元型別的資料的時候 F不能夠直接做到字串的拼接
    """
    from django.db.models.functions import Concat
    from django.db.models import Value
    #利用Concat和Value完成字串的拼接
    models.Book.objects.update(title = Concat(F('title'),Value('爆款')))
    # models.Book.objects.update(title=F('title') + '爆款')  # 所有的名稱會全部變成空白
    #Q查詢
    from django.db.models import Q
    #(與 ,)    (或  |)   (非   ~)
    #1.查詢賣出數大於100或者價格小於600的書籍
    #res = models.Book.objects.filter(maichu__gt=100,price__lt=600) 無法查詢,因為filter後引數還是and關係
    # # res = models.Book.objects.filter(Q(maichu__gt=100),Q(price__lt=600))#Q包裹逗號分割 還是and關係
    # res = models.Book.objects.filter(Q(maichu__gt=100) | Q(price__lt=600))  # | or關係
    # res = models.Book.objects.filter(~Q(maichu__gt=100) | Q(price__lt=600))  # ~ not關係

    # Q的高階用法  能夠將查詢條件的左邊也變成字串的形式
    q = Q()
    q.connector = 'or'
    q.children.append(('maichu__gt', 100))
    q.children.append(('price__lt', 600))
    res = models.Book.objects.filter(q)  # 預設還是and關係
    print(res)

django中如何開啟事務

  首先複習一下mysql中事物的四大特性:ACID A:原子性 C:一致性 I:隔離性 D:永續性 事物的回滾:roolback 事物的確認:commit,目前我們只需要學習如何在django中開啟事務

from django.db import transaction
    try:
        with transaction.atomic():
            #sql1
            ...
            #sql2
    except Exception as e:
        print(e)

    print('執行其他操作')

ORM中常用的欄位及引數

AutoField
    主鍵欄位 primary_key=True
  
CharField              varchar
    verbose_name    欄位的註釋
    max_length 長度
IntegerField int BigIntegerField bigint DecimalField     max_digits
=8     decimal_places=2 EmailFiled varchar(254) DateField date DateTimeField datetime     auto_now:每次修改資料的時候都會自動更新當前時間     auto_now_add:只在建立資料的時候記錄建立時間後續不會自動修改了 BooleanField(Field) - 布林值型別     該欄位傳佈爾值(False/True) 資料庫裡面存0/1 TextField(Field) - 文字型別     該欄位可以用來存大段內容(文章、部落格...) 沒有字數限制     後面的bbs作業 文章欄位用的就是TextField FileField(Field) - 字元型別     upload_to = "/data"     給該欄位傳一個檔案物件,會自動將檔案儲存到/data目錄下然後將檔案路徑儲存到資料庫中(/data/a.txt) 後面bbs作業也會涉及 # 更多欄位 直接參考部落格:https://www.cnblogs.com/Dominic-Ji/p/9203990.html


django中自定義欄位:
class My_field(models.Field):
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        # 呼叫父類的init方法
        super().__init__(max_length=max_length,*args,**kwargs)# 呼叫父類的init方法

    def db_type(self, connection):
        return 'char(%s)' % self.max_length

# 外來鍵欄位及引數
unique=True
ForeignKey(unique=True) === OneToOneField()
# 你在用前面欄位建立一對一 orm會有一個提示資訊 orm推薦你使用後者但是前者也能用

db_index
如果db_index=True 則代表著為此欄位設定索引
(複習索引是什麼)

to_field
設定要關聯的表的欄位 預設不寫關聯的就是另外一張的主鍵欄位

on_delete
當刪除關聯表中的資料時,當前表與其關聯的行的行為。
"""
django2.X及以上版本 需要你自己指定外來鍵欄位的級聯更新級聯刪除
"""

資料庫查詢優化

only與defer
select_related與prefetch_related

"""
orm語句的特點:
惰性查詢
如果你僅僅只是書寫了orm語句 在後面根本沒有用到該語句所查詢出來的引數
那麼orm會自動識別 直接不執行
"""

# only與defer
# res = models.Book.objects.all()
    # print(res)  # 要用資料了才會走資料庫

    # 想要獲取書籍表中所有數的名字
    # res = models.Book.objects.values('title')
    # for d in res:
    #     print(d.get('title'))
    # 你給我實現獲取到的是一個數據物件 然後點title就能夠拿到書名 並且沒有其他欄位
    # res = models.Book.objects.only('title')
    # res = models.Book.objects.all()
    # print(res)  # <QuerySet [<Book: 三國演義爆款>, <Book: 紅樓夢爆款>, <Book: 論語爆款>, <Book: 聊齋爆款>, <Book: 老子爆款>]>
    # for i in res:
        # print(i.title)  # 點選only括號內的欄位 不會走資料庫
        # print(i.price)  # 點選only括號內沒有的欄位 會重新走資料庫查詢而all不需要走了

    res = models.Book.objects.defer('title')  # 物件除了沒有title屬性之外其他的都有
    for i in res:
        print(i.price)
    """
    defer與only剛好相反
        defer括號內放的欄位不在查詢出來的物件裡面 查詢該欄位需要重新走資料
        而如果查詢的是非括號內的欄位 則不需要走資料庫了

    """
    

# select_related與prefetch_related
# select_related與prefetch_related  跟跨表操作有關
    # res = models.Book.objects.all()
    # for i in res:
    #     print(i.publish.name)  # 每迴圈一次就要走一次資料庫查詢

    # res = models.Book.objects.select_related('authors')  # INNER JOIN
    """
    select_related內部直接先將book與publish連起來 然後一次性將大表裡面的所有資料
    全部封裝給查詢出來的物件
        這個時候物件無論是點選book表的資料還是publish的資料都無需再走資料庫查詢了
    
    select_related括號內只能放外來鍵欄位    一對多 一對一
        多對多也不行
    
    """
    # for i in res:
    #     print(i.publish.name)  # 每迴圈一次就要走一次資料庫查詢

    res = models.Book.objects.prefetch_related('publish')  # 子查詢
    """
    prefetch_related該方法內部其實就是子查詢
        將子查詢查詢出來的所有結果也給你封裝到物件中
        給你的感覺好像也是一次性搞定的
    """
    for i in res:
        print(i.publish.name)