1. 程式人生 > >ORM的多表查詢詳述

ORM的多表查詢詳述

正向查詢 ces 體會 with 出版 不同 例子 關聯表 都是

ORM的多表查詢

ORM最核心與用的最多的地方就是跨表查詢了。這裏的“跨表查詢”大分為以下幾種:基於對象的跨表查詢基於雙下劃線的跨表查詢聚合查詢F與Q查詢以及分組查詢
下面就為大家詳細闡述這幾種查詢的具體細節及用法。
另外,本文省去了Django與MySQL數據庫之間建立連接以及創建表、添加表記錄的過程。如果大家有興趣可以回顧下我之前的兩篇文章:

https://www.cnblogs.com/paulwhw/p/9395085.html
https://www.cnblogs.com/paulwhw/p/9405168.html

本文在這兩篇文章的基礎上進行詳述。

基於對象的跨表查詢

基於對象的跨表查詢最終翻譯成我們的SQL語句其實是子查詢
的語句,也就是說我們利用一個查詢的結果作為另外一個查詢的條件進行查詢。數據庫中的一個表記錄其實就是篩選出來的“對象”的一個對象————可以利用.操作符操作。

一對多關系的查詢

我們在前面建立的Book表與Publish表就是一對多的關系:一本書只能有一個出版社,而同一個出版社可以出版多本書。
查詢要點:正向查詢按字段;反向查詢按表名小寫_set.all()。以Book表為基準,由於我們將關聯的字段定義在了Book表中,也就是說“關聯字段”在Book表中,所以從Book開始查是“正向”,從Publish開始查是“反向”。
這裏列舉一個正向查詢的例子:查詢主鍵為1的書籍的出版社的城市
book_obj = Book.objects.filter(pk=1).first()
ret = book_obj.publish.city
print(ret)
這裏再列舉一個反向查詢的例子,大家可以類比的記憶,查詢“蘋果出版社”出版過的所有書籍的書名
publish_obj = Publish.objects.filter(name=‘蘋果出版社‘).first()
book_list = publish.book_set.all()
for book_obj in book_list:
    print(book_obj.title)

多對多關系查詢:

我們在前面建立的Book表與Author表,就是“多對多”的關系。
查詢要點:正向查詢按字段;反向查詢按表名小寫_set.all()。以Book表為基準,由於我們將關聯的字段定義在了Book表中,也就是說“關聯字段”在Book表中,所以從Book開始查是“正向”,從Author開始查是“反向”。
這裏還是列舉一個正向查詢的例子,“三國群英”所有的作者及手機號
book_obj = Book.objects.filter(title=‘三國群英‘).first()
authors = book_obj.authors.all()
for author_obj in authors:
    name = author_obj.name
    ##手機號在“作者詳細表”中,而且“作者表”相對於“作者詳細表”是正向,關聯字段為authordetail
    telephone = author_obj.authordetail.telephone
    print(‘作者:%s,手機號:%s‘%(name,telephone))
下面是一個“反向查詢”的例子,查詢作者whw出版過的所有書籍的名字
author_obj = Author.objects.filter(name=‘whw‘).first()
book_list = author_obj.book_set.all()
for book_obj in book_list:
    print(book_obj.title)

一對一關系的查詢

Author表與AuthorDetail表是一對一的關系。
查詢要點:正向查詢按字段;反向查詢按表名小寫。以Author表為基準,由於我們將關聯的字段定義在了Author表中,也就是說“關聯字段”在Author表中,所以從Author開始查是“正向”,從AuthorDetail開始查是“反向”。
正向查詢的例子,查詢作者whw的電話
author = Author.objects.filter(name=‘whw‘).first()
##正向查詢按字段
ret = author.authordetail.telephone
print(ret)
反向查詢的例子:查詢電話是12312312的作者名字
add = AuthorDetail.objects.filter(telephone=12312312).first()
#反向查詢按表名小寫:
print(add.author.name)

基於雙下劃線的跨表查詢

基於雙下劃線的跨表查詢————最終翻譯成SQL語句都是“join查詢” 。
這裏直接給出查詢規則——正向查詢按字段,反向查詢按表名小寫——用來告訴ORM引擎join哪張表。其實本質上就是先join成一張表,再執行“單表查詢”。這裏還需要註意的一點是:查詢中用到的APIvalues()等同於SQL語句中的select;filter()等同於SQL語句中的where。

一對多關系的雙下劃線查詢

這裏我們還是以BookPublish表為例。需要註意的是,正向查詢按字段,反向查詢按表名小寫用來告訴ORM引擎join哪張表。
為了幫助大家更好的理解,下面的每個例子都與我們的SQL語句類比,幫助大家更容易的理解,因為基於雙下滑先的跨表查詢在實際中用的非常多!
例:查詢水滸傳這本書的出版社的名字
在SQL中我們是這樣實現的:
select publish.name from book inner join publish
on book.publish_id = publish.nid
where book.title=‘水滸傳‘
正向查詢:
ret = Book.objects.filter(title=‘水滸傳‘).values(‘publish__name‘)
print(ret)
反向查詢:
ret = Publish.objects.filter(book__title=‘水滸傳‘).values(‘name‘)

print(ret)

多對多關系的雙下劃線查詢

這裏我們還是以BookAuthor表為例。需要註意的是,正向查詢按字段,反向查詢按表名小寫用來告訴ORM引擎join哪張表。
這裏的例子我們還是用SQL進行類比
例:查詢三國群英這本書所有作者的名字
註意這裏總共涉及三張表:BookAuthorbook_authors。但是第三張表我們是用Django在Book中與Author建立關聯生成的表,也就是說:Book查Author是“正向查詢”,Author查Book是“反向查詢”。
SQL語句實現:
select author.name from book inner join book_authors 
on book.nid = book_author.book_id
inner join author 
on book_authors.author_id = author.nid
where book.title = "三國群英"
正向查詢:
ret = Book.objects.filter(title=‘三國群英‘).values(‘authors__name‘)
print(ret)
反向查詢:
ret = Author.objects.filter(book__title=‘三國群英‘).values(‘name‘)
print(ret)

一對一關系的雙下劃線查詢

一對一的關系相對的很好理解,我們這裏直接給出例子,查詢whw的手機號
正向查詢
ret = Author.objects.filter(name=‘whw‘).values(‘authordetail__telephone‘)
print(ret)
反向查詢
ret = AuthorDetail.objects.filter(author__name=‘whw‘).values(‘telephone‘)
print(ret)

聚合查詢

在做聚合查詢之前我們需要先引入下列模塊:
from django.db.models import Max,Min,Avg,Count
這裏直接給出一個例子大家體會一下它的用法,真的很簡單。
例:查詢所有書籍的平均價格以及最高的價格
from django.db.models import Avg,Max
ret = Book.objects.all().aggregate(avg_price=Avg(‘price‘),max_price=Max(‘price‘))
print(ret)

F與Q查詢

F查詢

在上面所有的例子中,我們構造的過濾器都只是將字段值與某個常量做比較。如果我們要對兩個字段的值做比較,那該怎麽做呢?
Django 提供 F() 來做這樣的比較。F() 的實例可以在查詢中引用字段,來比較同一個model實例中兩個不同字段的值。
我們在使用前應先引入:
from django.db.models import F
比如我們想查詢content_num大於read_num的書籍的名字:
ret = Book.objects.filter(content_num__gt=F(‘read_num‘)).values(‘title‘)
print(ret)
我們還可以將每個書籍的價格加10元:
Book.objects.all().update(price=F(‘price‘)+10)

Q查詢

filter() 等方法中的關鍵字參數查詢都是一起進行“AND” 的。 如果我們需要執行更復雜的查詢(例如OR語句),我們可以使用Q對象。
當然使用前必須引入:
from django.db.models import Q
這裏直接給出三個例子:
1、查找書名以“三國”開頭或者價格等於100的書籍名稱
ret = Book.objects.filter(Q(title__startswith=‘三國‘)|Q(price=100)).values(‘title‘)
print(ret)
2、查找書名不以“三國”開頭的書籍名稱
ret = Book.objects.filter(~Q(title__startswith=‘三國‘)).values(‘title‘)
print(ret)
Q查詢與鍵值對的關系:先寫Q再寫鍵值對,而且是“且”的關系:
ret = Book.objects.filter(~Q(title__startswith=‘三國‘),title__startswith=‘水‘).values(‘title‘)
print(ret)

分組查詢

分組查詢的思想跟我們SQL中group by的思路是一模一樣的,也就是說,我們先按照某個字段為數據進行分組,然後再進行進一步的查詢。
ORM的分組查詢包括:單表下的分組查詢與多表下的分組查詢。

單表下的分組查詢

準備工作:我們新建一張員工表emp,包含的字段有——:id、name、age、salary、dep(部門名)、province(省份)。models.py文件的類這樣寫:
class Emp(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8,decimal_places=2)
    dep = models.CharField(max_length=32)
    province = models.CharField(max_length=32)
單表下的分組查詢語法:

單表模型.objects.values(‘group by的字段‘).annotate(聚合函數(‘統計字段‘))

例:查詢每一個部門的名稱以及員工的平均薪水
from django.db.models import Avg
ret = Emp.objects.values(‘dep‘).annotate(Avg(‘salary‘))
print(ret)
另外還需要註意的是:在單表分組下,按著主鍵進行分組是沒有任何意義的!這與我們SQL中是一樣的道理。

多表下的分組查詢

單表下的分組查詢語法:
每一個後表模型.objects.values(‘pk‘).annotate(聚合函數(‘關聯表__統計字段‘)).values(‘表模型的所有字段以及統計字段‘)
註意以哪張表中的字段分組,哪一張表就是“後表”。
為了方便大家理解,下面的例子還是用SQL語句進行類比
例:查詢每一個作者的名字以及出版過的書籍的最高價格
SQL:
select author.name,Max(book.price) from book inner join book_authors
on book.nid = book_authors.book_id
inner join author 
on autohr.nid = book_authors.author_id
group by author.nid
annotate方法:註意主鍵可以用 pk 表示;Author找Book是“反向查詢”按表名小寫
ret = Author.objects.values(‘pk‘).annotate(max_price=Max(‘book__price‘)).values(‘name‘,‘max_price‘)
print(ret) 

ORM的多表查詢詳述