Django聚合、分組查詢、ORM查詢優化
阿新 • • 發佈:2022-05-19
內容概要
- 聚合查詢
- 分組查詢
- F與Q查詢
- ORM查詢優化
- ORM欄位型別及引數
- ORM事務操作
聚合查詢
MySQL聚合函式:max\min\sum\count\avg from django.db.models import Max, Min, Sum, Avg, Count res = models.Book.objects.aggregate(Max('price')) print(res) # {'price__max': Decimal('13330.11')} '''沒有分組也可以使用聚合函式 預設整體就是一組''' res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk') ) print(res) # {'price__max': Decimal('13330.11'), 'price__min': Decimal('6666.66'), 'price__sum': Decimal('29996.76'), 'price__avg': 9998.92, 'pk__count': 3}
分組查詢
MySQL分組操作:group by """ORM執行分組操作 如果報錯 可能需要去修改sql_mode 移除only_full_group_by""" ORM操作分組關鍵字:annotate from django.db.models import Max, Min, Sum, Avg, Count # 統計每本書的作者個數 res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num') # 統計每個出版社買的最便宜的書的名稱和價格 res = models.Publish.objects.annotate(Min_price=Min('book__price')).values( 'name', 'book__title', 'Min_price' ) print(res) # 統計不止一個作者的圖書 res = models.Book.objects.annotate( author_num=Count('authors__pk') ).filter(author_num__gt=1).values('title','author_num') # 統計每個作者出的書的總價格 res = models.Author.objects.annotate(book_sum_price=Sum('book__price')).values( 'name', 'book_sum_price' ) """上述操作都是以表為單位做分組 如果想要以表中的某個欄位分組如何操作""" # values('age')在分組前 類似於SQL的where 在分組之後 having models.Author.objects.values('age').annotate() # 統計每個出版社主鍵值對應的書籍個數 res = models.Book.objects.values('publish_id').annotate( book_num=Count('pk') ).values('publish_id','book_num') print(res)
F與Q查詢
""" 當表中已經有資料的情況下 新增額外的欄位 需要指定預設值或者可以為null 方式1 IntegerField(verbose_name='銷量',default=1000) 方式2 IntegerField(verbose_name='銷量',null=True) 方式3 在遷移命令提示中直接給預設值 """ # 查詢庫存大於銷量的書籍 # res = models.Book.objects.filter(kucun > maichu) 不行 # res = models.Book.objects.filter(kucun__getmaichu) 不行# 當查詢條件的左右兩表的資料都需要
F查詢
表中的資料 可以使用F查詢
from django.db.models import F
res = models.Book.objects.filter(kucun__gt=F('maichu'))
# 將所有書的價格提升1000塊
res = models.Book.objects.update(price=F('price') + 1000)
# 將所有書的名稱後面加上_爆款字尾
res = models.Book.objects.update(title=F('title') + '_爆款') # 不可以
'''如果要修改char欄位咋辦(千萬不能用上面對數值型別的操作!!!) 需要使用下列兩個方法'''
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.update(name=Concat(F('title'), Value('爆款')))
Q查詢
# 查詢價格大於20000或者賣出大於1000的書籍
# res = models.Book.objects.filter(price__gt=20000,maichu__gt=1000)
# print(res)
# print(res.query)
'''filter括號內多個條件預設是and關係 無法直接修改'''
from django.db.models import Q
'''使用Q物件 就可以支援邏輯運算子'''
# res = models.Book.objects.filter(Q(price__gt=20000), Q(maichu__gt=1000)) # 逗號是and關係
# res = models.Book.objects.filter(Q(price__gt=20000) | Q(maichu__gt=1000)) # 管道符是or關係
# res = models.Book.objects.filter(~Q(price__gt=20000)) # ~是not操作
# print(res.query)
'''
Q物件進階用法
filter(price=100)
filter('price'=100)
當我們需要編寫一個搜尋功能 並且條件是由使用者指定 這個時候左邊的資料就是一個字串
'''
q_obj = Q()
q_obj.connector = 'or' # 預設是and 可以改為or
q_obj.children.append(('price__gt',20000))
q_obj.children.append(('maichu__gt',1000))
res = models.Book.objects.filter(q_obj)
print(res.query)
ORM常見欄位
# ORM MySQL
AutoField() 自增 int auto_increment
CharField() 字元型別 varchar
'必須提供max_length引數'
IntergerField() 整型 int
BigIntergerField() 長整型 bigint
DecimalField() 10進位制小數 decimal
DateField() 日期格式 date
DateTimeField() 日期+時間格式 datetime
BooleanField() 存0和1 布林值 boolean
TextField() 儲存大段文字
FileField() 傳檔案自動儲存到指定位置並存檔案路徑
EmailField() 本質還是varchar型別
# 自定義欄位型別
class MyCharField(models.Field):
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super().__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
return 'char(%s)' % self.max_length
ORM重要引數
primary_key 主鍵
max_length 字元長度
verbose_name 說明
null 用於表示某個欄位可以為空
default 為該欄位設定預設值。
max_digits 總位數(不包括小數點和符號)
decimal_places 小數位數
unique 如果設定為unique=True 則該欄位在此表中必須是唯一的 。
db_index 如果db_index=True 則代表著為此欄位設定索引。
auto_now 配置上auto_now=True,每次更新資料記錄的時候會更新該欄位。
auto_now_add 配置auto_now_add=True,建立資料記錄的時候會把當前時間新增到資料庫。
choices 用於可以被列舉完全的資料,用來給欄位提供選擇項。
to 設定要關聯的表。
to_field 設定要關聯的欄位。
db_constraint 是否在資料庫中建立外來鍵約束,預設為True。
ps:外來鍵欄位中可能還會遇到related_name引數
"""
外來鍵欄位中使用related_name引數可以修改正向查詢的欄位名
"""
chioces使用
用於可以被列舉完全的資料
eg:性別 學歷 工作經驗 工作狀態
class User(models.Model):
username = models.CharField(max_length=32)
password = models.IntegerField()
gender_choice = (
(1,'男性'),
(2,'女性'),
(3,'變性')
)
gender = models.IntegerField(choices=gender_choice)
user_obj.get_gender_display()
# 有對應關係就拿 沒有還是本身
事務操作
MySQL事務:四大特性(ACID)
原子性
一致性
獨立性
永續性
start transcation; # 開啟事務
rollback; # 事務回滾
commit; # 事務提交
ORM事務操作
# 事務會自動提交
from django.db import transaction
try:
with transaction.atomic():
pass
except Exception:
pass
ORM執行原生SQL
# 方式1
from django.db import connection, connections
cursor = connection.cursor()
cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
cursor.fetchone()
# 方式2
models.UserInfo.objects.extra(
select={'newid':'select count(1) from app01_usertype where id>%s'},
select_params=[1,],
where = ['age>%s'],
params=[18,],
order_by=['-age'],
tables=['app01_usertype']
)
多對多三種建立方式
# 全自動(常見)
orm自動建立第三張表 但是無法擴充套件第三張表的欄位
authors = models.ManyToManyField(to='Author')
# 全手動(使用頻率最低)
優勢在於第三張表完全自定義擴充套件性高 劣勢在於無法使用外來鍵方法和正反向
class Book(models.Model):
title = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
# 半自動(常見)
正反向還可以使用 並且第三張表可以擴充套件
唯一的缺陷是不能用: add\set\remove\clear四個方法
class Book(models.Model):
title = models.CharField(max_length=32)
authors = models.ManyToManyField(
to='Author',
through='Book2Author', # 指定表
through_fields=('book','author') # 指定欄位
)
class Author(models.Model):
name = models.CharField(max_length=32)
'''多對多建在任意一方都可以 如果建在作者表 欄位順序互換即可'''
books = models.ManyToManyField(
to='Author',
through='Book2Author', # 指定表
through_fields=('author','book') # 指定欄位
)
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')