2022.5.18 ORM查詢及操作MySQL
阿新 • • 發佈:2022-05-18
2022.5.18 ORM查詢及操作MySQL
- 聚合查詢
- 分組查詢
- F與Q查詢
- ORM查詢優化
- ORM常見欄位及引數
- ORM事務操作
- ORM執行原生SQL語句
- 多對多三種建立方式
一、聚合查詢
MySQL聚合函式:max\min\sum\count\avg # 匯入模組 from django.db.models import Max, Min, Sum, Avg, Count # ORM操作 1. res = models.Book.objects.aggregate(Max('price')) print(res) # {'price__max': Decimal('56777.98')} ps:沒有分組也可以使用聚合函式,預設整體就是一組; 2.aggregate括號內也可使用多個聚合函式: res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk')) print(res) # {'price__max': Decimal('56777.98'), 'price__min': Decimal('16987.22'), 'price__sum': Decimal('179408.51'), 'price__avg': 29901.418333, 'pk__count': 6}
二、分組查詢
主要用於有外來鍵欄位的多張表的資料查詢,可以通過按照一張表的分組獲取另一張表的資料
MySQL分組操作:group by django ORM操作:annotate() ps:ORM執行分組操作時如果報錯,可能需要修改sql_mode,然後移除only_full_group_by; 案例展示: # 1.統計每本書的作者個數 from django.db.models import Count res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num') # 使用表物件點annotate,就是按照表的資料分組,括號裡面是分組後的操作或者獲取資料 # 2.統計每個出版社賣的最便宜的書的價格 res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price') # 按照出版社每個物件分組,並獲取最便宜書籍的最小值,使用values獲取值 # 3.統計不止一個作者的圖書 from django.db.models import Count res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title','author_num') # 4.統計每個作者出的書的總價格 from django.db.models import Sum res = models.Author.objects.annotate(book_sum_price=Sum('book__price')).values('name','book_sum_price') """上述操作都是以表為單位做分組 如果想要以表中的某個欄位分組如何操作""" models.Author.objects.values('age').annotate() 案例: # 統計每個出版社主鍵值對應的書籍個數 from django.db.models import Count 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
在遷移命令提示中直接給預設值
1、F查詢
案例分析: # 1.查詢庫存大於銷量的書籍 res = models.Book.objects.filter(kucun > maichu) 不行 res = models.Book.objects.filter(kucun__gt=maichu) 不行 # 當查詢條件的左右兩表的資料都需要表中的資料,可以使用F查詢 from django.db.models import F res = models.Book.objects.filter(kucun__gt=F('maichu')) # 2.將所有書的價格提升1000塊 res = models.Book.objects.update(price=F('price') + 1000) # 3.將所有書的名稱後面加上_爆款字尾 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('爆款')))
2、Q查詢
案例分析:
# 查詢價格大於20000或者賣出大於1000的書籍
res = models.Book.objects.filter(price__gt=20000,maichu__gt=1000)
print(res)
print(res.query)
'''filter括號內多個條件預設是and關係,無法直接修改'''
'''使用Q物件,就可以支援邏輯運算子'''
from django.db.models import 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物件:
q_obj = Q() # 獲取一個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) # q_obj可以承載多個條件
print(res.query)
四、ORM查詢優化
1、ORM瞭解
"""
在IT行業 針對資料庫 需要儘量做 能不'麻煩'它就不'麻煩'它
"""
# 1.orm查詢預設都是惰性查詢(能不消耗資料庫資源就不消耗)
光編寫orm語句並不會直接指向SQL語句,只有後續的程式碼用到了才會執行;
# 2.orm查詢預設自帶分頁功能(儘量減輕單次查詢資料的壓力)
2、only與defer
# only
案例分析:
res = models.Book.objects.values('title','price')
# 需求:讓上述單個結果還是以物件的形式展示,可以直接通過句點符操作
for i in res:
print(i.get('title')) # 結果不是一個物件
res = models.Book.objects.only('title', 'price') # 結果是物件
for obj in res:
print(obj.title)
print(obj.price)
print(obj.publish_time)
"""
only會產生物件結果集,物件點括號內出現的欄位不會再走資料庫查詢;
但是如果點選了括號內沒有的欄位也可以獲取到資料,但是每次都會走資料庫查詢;
"""
# defer
案例分析
res = models.Book.objects.defer('title','price')
for obj in res:
print(obj.title)
print(obj.price)
print(obj.publish_time)
"""
defer與only剛好相反,物件點括號內出現的欄位會走資料庫查詢;
如果點選了括號內沒有的欄位也可以獲取到資料,每次都不會走資料庫查詢;
"""
總結:
(1)only和defer都可以通過欄位名產生物件,而不是對應資料;
(2)only和defer產生的物件在和資料庫互動走資料庫查詢是完全相反的,only產生的物件後臺可以不通過資料庫查詢獲取only指定的資料,而defer產生的物件後臺必須通過資料庫查詢獲取defer指定的資料;
3、select_related和prefetch_related
# select_related
res = models.Book.objects.select_related('publish')
for obj in res:
print(obj.title) # 獲取book表內容
print(obj.publish.name) # 獲取publish表內容
print(obj.publish.addr)
"""
(1)select_related括號內只能傳一對一和一對多欄位,不能傳多對多欄位;
(2)效果是內部直接連線表(inner join),然後將連線之後的大表中所有的資料全部封裝到資料物件中;
(3)後續物件通過正反向查詢跨表,內部不會再走資料庫查詢;
"""
# prefetch_related
res = models.Book.objects.prefetch_related('publish')
for obj in res:
print(obj.title)
print(obj.publish.name)
print(obj.publish.addr)
"""
(1)prefetch_related主要針一對多和多對多關係進行優化;
(2)prefetch_related通過分別獲取各個表的內容,然後用Python處理他們之間的關係來進行優化;
(3)效果類似於多次子查詢,將多次查詢之後的結果封裝到資料物件中,後續物件通過正反向查詢跨表,內部不會再走資料庫查詢;
"""
總結:
(1)select_related是通過join來關聯多表,一次獲取資料,存放在記憶體中,但如果關聯的表太多,會嚴重影響資料庫效能。
(2)prefetch_related是通過分表,先獲取各個表的資料,存放在記憶體中,然後通過Python處理他們之間的關聯。
五、ORM常見欄位及引數
1、常見欄位
AutoField()
int auto_increment
CharField()
必須提供max_length引數 對應的資料庫中是varchar型別
IntergerField()
int
DecimalField()
decimal
DateField()
date auto_now auto_now_add
DateTimeField()
datetime auto_now auto_now_add
BigIntergerField()
bigint
BooleanField()
傳佈爾值 存0和1
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
2、重要引數
primary_key # 主鍵 primary_key=True
"字元型欄位燦數:"
max_length # 字元長度 max_length=32
verbose_name # 欄位別名 verbose_name='xxx'
null # 預設為空 null=True
default # 設定預設值 default='xxx'
"int型欄位引數:"
max_digits
decimal_places
"DateField等時間欄位型別引數"
unique # 設定唯一欄位 unique=True
db_index
auto_now # 自動獲取當前時間,並在修改時更新
auto_now_add # 自動獲取當前時間,後續不自動改變auto_now_add=True
choices
用於可以被列舉完全的資料,類似於列舉;
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()
# 欄位預設設定為數字,通過get_gender_display()方法獲取元組內指定資料,元組內有對應關係就拿,沒有就還是本身的數字;
"外來鍵欄位引數:"
to # to=外來鍵表名
to_field # to_field=外來鍵表的欄位名
db_constraint
ps:外來鍵欄位中可能還會遇到related_name引數
"""
外來鍵欄位中使用related_name引數可以修改正向查詢的欄位名,相當於給外來鍵起別名;
"""
六、ORM事務操作
# 回顧MySQL
MySQL事務:四大特性(ACID)
原子性
一致性
獨立性
永續性
start transcation;
rollback;
commit;
# ORM事務操作
from django.db import transaction
try:
with transaction.atomic():
pass # 事務執行的程式碼一旦出現異常整個with內部的資料庫操作都會自動回滾(rollback)
except Exception:
pass
七、Django執行原生SQL語句
# 方式1:獲取cursor執行
from django.db import connection, connections
cursor = connection.cursor()
cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1]) # 執行sql語句
cursor.fetchone() # 獲取資料
# 方式2:ORM操作執行--extra()
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操作中利用extra操作sql語句
八、多對多三種建立方式
# 全自動(常見)
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')