首页 > 其他分享 >ORM跨表查询

ORM跨表查询

时间:2023-12-05 15:27:01浏览次数:30  
标签:__ name objects ORM ret 查询 跨表 book

 

ORM跨表查询、join查询、聚合查询、分组查询、FQ查询

跨表查询分为两类: 基于对象查询(子查询) 基于双下划线查询(join查询)

1、基于对象的跨表查询(sql语句:子查询)

子查询: 基于一个查询结果作为另一个查询的条件

1.1 一对多

"""
正向查询:多找一,按字段
反向查询:一找多,按表名称小写_set,其中set表示集合的意思
"""
正向查询(按字段)
# 查询西游记出版社的名字和邮箱
book = Book.objects.get(title='西游记')
print(book.publish.name)
print(book.publish.email)

 


本质上翻译两条sql如下:

(0.000) SELECT "book_book"."id", "book_book"."title", "book_book"."pub_date", "book_book"."price", "book_book"."publish_id" FROM "book_book" WHERE "book_book"."title" = '西游记' LIMIT 21; args=('西游记',)
# 小橘子出版社
(0.000) SELECT "book_publish"."id", "book_publish"."name", "book_publish"."city", "book_publish"."email" FROM "book_publish" WHERE "book_publish"."id" = 3 LIMIT 21; args=(3,)
# [email protected]



反向查询(按表名:book_set,按 表名称小写_set, set是集合的意思,返回queryset集合)
# 查询小橘子出版社的所有书籍
pub = Publish.objects.get(name='小橘子出版社')
print(pub.book_set.all())    # 与这个出版社关联的所有书籍,即返回一个queryset
# <QuerySet [<Book: 西游记>, <Book: 赳赳老秦>]>

print(pub.book_set.values('title', 'price')) 
# <QuerySet [{'title': '西游记', 'price': Decimal('199.00')}, {'title': '赳赳老秦', 'price': Decimal('110.00')}]>

 

1.2 多对多
"""
正向查询:按字段
反向查询:按表名称小写_set,其中set表示集合的意思
"""
正向查询(按字段)
# 查询西游记所有作者的名字
book = Book.objects.get(title='西游记')
ret = book.authors.all().values('name')
print(ret)   # <QuerySet [{'name': '强子'}, {'name': '乖乖快回家'}]>

反向查询(按表名:book_set,按 表名称小写_set, set是集合的意思,返回queryset集合)
# 查询 强子 所有出版过的书籍名称
author_obj = Author.objects.get(name='强子')
ret = author_obj.book_set.all()
print(ret)   # <QuerySet [<Book: 西游记>, <Book: 三国志>]>


1.3 一对一
"""
正向查询:按字段,返回model对象,属性取值
反向查询:按表名称小写,不用加_set;一对一查询,仅返回一个对象;返回model对象,属性取值
"""
正向查询(按字段)
# 查询强子的手机号
author_obj = Author.objects.get(name='强子')
ret = author_obj.author_detail.telephone
print(ret)   # 111

反向查询(注意返回一个model对象,所以不用加 _set !!!,查到之后对象的属性取值)
# 查询手机号为111的作者名字
tel_obj = AuthorDetail.objects.get(telephone='111')
ret = tel_obj.author.name
print(ret)   # 强子

 

 

1.4 related_name 覆写 FOO_set

可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Book model 中做一下更改

publish = ForeignKey(Book, related_name='bookList')

接下来如下骚操作:

# 查询 人民出版社出版过的所有书籍
 
publish=Publish.objects.get(name="人民出版社")
book_list=publish.bookList.all()  # 与人民出版社关联的所有书籍对象集合

 

2、基于双划线的跨表查询(sql:join语句)

join查询:按照哪两个字段拼表,两张表拼成一张大表,一定程度下会影响查询效率

2.0 join简介

Publish表
id      name             email         addr
 1   北京出版社          [email protected]      bj
 2   南京出版社          [email protected]      nj

Book表
id  title    price   pub_date      publish_id       
1   西游记    123    2012-12-12         1                   
2   三国演义  234    2012-12-12         1                  
3   三体      45     2012-3-12          1      
4   水壶      45     2012-3-12          2

join之后的大表 Book
id  title    price   pub_date      publish_id     Publish.id   Publish.name          email         addr 
1   西游记    123    2012-12-12        1                1      北京出版社          [email protected]    bj   
2   三国演义  234    2012-12-12        1                1      北京出版社          [email protected]    bj    
3   三体      45     2012-3-12         1                1      北京出版社          [email protected]    bj  
4   水壶      45     2012-3-12         2                2      南京出版社          [email protected]    nj

正跨:关联字段publish 所在 表Book 进行查询 其所关联的表Publish的记录

反跨:关联表Publish 查询 其关联字段publish所在的表Book的记录

 

2.1 一对多

"""
   正向跨表(由关联字段所在表查询其关联的表):按关联字段__查询字段
   反向跨表:按表名称小写__查询字段
   以谁为基表 没所谓
"""
正跨查询,按字段;格式:外键字段__跨表字段;返回一个集合列表 [{},{}]
ret = Book.objects.filter(title='西游记').values('publish__name', 'publish__email')
print(ret)   # <QuerySet [{'publish__name': '小橘子出版社', 'publish__email': '[email protected]'}]>

ret = Book.objects.filter(publish__name='小橘子出版社').values('title')
print(ret)   # <QuerySet [{'title': '西游记'}, {'title': '赳赳老秦'}]>



反跨查询,按表名称;格式:小写表名称__跨表字段;返回一个集合列表 [{},{}]
ret = Publish.objects.filter(book__title='西游记').values('name', 'email')
print(ret)   # <QuerySet [{'name': '小橘子出版社', 'email': '[email protected]'}]>

ret = Publish.objects.filter(name='小橘子出版社').values('book__title')
print(ret)   # <QuerySet [{'book__title': '西游记'}, {'book__title': '赳赳老秦'}]>

 

 

2.2 多对多

关于多对多的join,sql中是三张表(Book、Author、book_authors第三张关联id表)拼接而成一张大表

  • 正跨查询
"""
   正向跨表(由关联表字段所在表查询其关联的表):按关联表字段__查询字段
   反向跨表:按表名称小写__查询字段
   以谁为基表 没所谓
"""
 
# 查询西游记所有作者的名字

ret = Book.objects.filter(title='西游记').values('authors__name')
print(ret)     # <QuerySet [{'authors__name': '强子'}, {'authors__name': '乖乖快回家'}]>

ret = Author.objects.filter(book__title='西游记').values('name')
print(ret)     # <QuerySet [{'name': '强子'}, {'name': '乖乖快回家'}]>

 

 

反跨查询
# 查询 强子 所有出版过的书籍名称
ret = Author.objects.filter(name='强子').values('book__title')
print(ret)     # <QuerySet [{'book__title': '西游记'}, {'book__title': '三国志'}]>

ret = Book.objects.filter(authors__name='强子').values('title')
print(ret)  # <QuerySet [{'title': '西游记'}, {'title': '三国志'}]>

 

 

2.3 一对一

"""
   正向跨表(由关联字段所在表查询其关联的表):按关联字段__查询字段
   反向跨表:按表名称小写__查询字段
   以谁为基表 没所谓
"""
正跨查询
# 查询强子的手机号
ret = Author.objects.filter(name='强子').values('author_detail__telephone')
print(ret)   # <QuerySet [{'author_detail__telephone': 111}]>

ret = AuthorDetail.objects.filter(telephone='111').values('author__name')
print(ret)  # <QuerySet [{'author__name': '强子'}]>

反跨查询
# 查询手机号为111的作者名字
ret = AuthorDetail.objects.filter(author__name='强子').values('telephone')
print(ret)   # <QuerySet [{'telephone': 111}]>

ret = Author.objects.filter(author_detail__telephone='111').values('name')
print(ret)  # <QuerySet [{'name': '强子'}]>

 

 

2.4 related_name 覆写 FOO_set

反向查询时,如果定义了related_name ,则用related_name替换表名,例如:

# 练习: 查询人民出版社出版过的所有书籍的名字与价格(一对多)

# 反向查询 不再按表名:book,而是 related_name:bookList


queryResult=Publish.objects
           .filter(name="人民出版社")
           .values_list("bookList__title","bookList__price")

 

3、 跨表查询进阶

3.1 多个跨表嵌套

正向跨表
# 跨表查询小橘子出版社出版过的所有书籍名字以及作者的姓名
ret = Book.objects.filter(publish__name='小橘子出版社').values('title', 'authors__name')
print(ret)
# <QuerySet [{'title': '西游记', 'authors__name': '强子'}, {'title': '西游记', 'authors__name': '乖乖快回家'}, {'title': '赳赳老秦', 'authors__name': '大猩猩'}, {'title': '赳赳老秦', 'authors__name': '马大哈'}]>
反向跨表
# 跨表查询小橘子出版社出版过的所有书籍名字以及作者的姓名
ret = Publish.objects.filter(name='小橘子出版社').values('book__title', 'book__authors__name')
print(ret)
# <QuerySet [{'book__title': '西游记', 'book__authors__name': '强子'}, {'book__title': '西游记', 'book__authors__name': '乖乖快回家'}, {'book__title': '赳赳老秦', 'book__authors__name': '大猩猩'}, {'book__title': '赳赳老秦', 'book__authors__name': '马大哈'}]>

 

3.2 连续跨表查询

查询手机号以111开头的作者出版过的所有书籍名称以及出版社名称
# 方式1:
query_ret = Book.objects.filter(authors__author_detail__telephone__regex=r'^111').\
    values_list('title',  'publish__name')
print(query_ret)  # <QuerySet [('西游记', '小橘子出版社'), ('三国志', '小苹果出版社')]>

# 方式2:
ret = Author.objects\
    .filter(author_detail__telephone__regex=r'^111')\
    .values('book__title', 'book__publish__name')
print(ret)
# <QuerySet [{'book__title': '西游记', 'book__publish__name': '小橘子出版社'}, {'book__title': '三国志', 'book__publish__name': '小苹果出版社'}]>

 

 

4、F查询 与 Q查询

4.0 Book表模型

 

class Book(models.Model):
    # id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    pub_date = models.DateTimeField()
    price = models.DecimalField(max_digits=9, decimal_places=2)  # 9999999.99

    keep_nums = models.IntegerField(default=0)      # 收藏数
    comment_nums = models.IntegerField(default=0)   # 评论数

    # 与Publish建立一对多的关系,外键字段ForeignKey会生成publisher_id建立在多的一方
    # publish = models.ForeignKey(to="Publish", to_field="pk", on_delete=models.CASCADE)  # 级联删除
    publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE)  # 级联删除

    # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表book_authors实现多对多的关系
    authors = models.ManyToManyField(to='Author')

    def __str__(self):
        return self.title

 

 

 

4.1 F 查询

表内的两个字段的值如何进行比较??且看Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

F(‘字段名’) 表示字段含义并非命名空间内变量的意思

from django.db.models import F

 # 查询 评论数 大于 收藏数 的书籍
 books = Book.objects.filter(comment_nums__gt=F('keep_nums'))
 print(books)  # <QuerySet [<Book: 赳赳老秦>]>

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作

# 查询评论数大于收藏数2倍的书籍
Book.objects.filter(comment_nums__gt=F('keep_nums')*2)

修改操作也可以使用F函数,比如将每一本书的价格提高30元:

Book.objects.all().update(price=F("price")+30) 

 

 

4.2 Q 查询

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。

Q(字段条件) 进行 与或非 的 多条件过滤

与:  &
或:  |
非:  ~
from django.db.models import Q

Q(title__startswith='Py')



Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

# 查询 价格 大于 100 或  评论数 大于 3000
books = Book.objects.filter(Q(price__gt=100)|Q(comment_nums__gt=3000))
print(books.query)
print(books)  # <QuerySet [<Book: 三国志>, <Book: 西游记>, <Book: 赳赳老秦>]>

等同于下面的SQL WHERE 子句:

WHERE price > 100 OR comment_nums =3000

可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用 ~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询

bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(pub_date__year=2020)).values_list("title")

查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:

bookList=Book.objects.filter(Q(pub_date__year=2016) | Q(pub_date__year=2017),
                              title__icontains="python"
                             )

 

5、聚合与分组查

5.1 聚合查询

aggregate(*args, **kwargs)

# 计算所有图书的平均价格
    >>> from django.db.models import Avg
    >>> Book.objects.all().aggregate(Avg('price'))
    {'price__avg': 34.35}

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}

如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

 

5.2 分组查询

5.2.1 分组介绍
###################################--单表分组查询--#######################################################

查询每一个部门名称以及对应的员工数

emp:

id  name age   salary    dep
1   alex  12   2000     销售部
2   egon  22   3000     人事部
3   wen   22   5000     人事部


sql语句:
select dep,Count(*) from emp group by dep;

ORM:
emp.objects.values("dep").annotate(c=Count("id")

###################################--多表分组查询--###########################


多表分组查询:

查询每一个部门名称以及对应的员工数


emp:

id  name age   salary   dep_id
1   alex  12   2000       1
2   egon  22   3000       2
3   wen   22   5000       2


dep

id   name 
1    销售部
2    人事部



emp-dep:

id  name age   salary   dep_id   id   name 
1   alex  12   2000       1      1    销售部
2   egon  22   3000       2      2    人事部
3   wen   22   5000       2      2    人事部

 




sql语句:
select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id

ORM:
dep.objetcs.values("id").annotate(c=Count("emp")).values("name","c")

 

5.2.2 分组 类 实现
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)

 

 

annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。

总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。

5.2.3 查询练习
(1) 统计每一个出版社的最便宜的书
publishList=Publish.objects.annotate(MinPrice=Min("book__price"))
for publish_obj in publishList:
    print(publish_obj.name,publish_obj.MinPrice)

annotate的返回值是querySet,如果不想遍历对象,可以用上valuelist:
queryResult= Publish.objects
            .annotate(MinPrice=Min("book__price"))
            .values_list("name","MinPrice")
print(queryResult)

'''

SELECT "app01_publish"."name", MIN("app01_book"."price")  AS "MinPrice" FROM "app01_publish" 
LEFT  JOIN "app01_book" ON ("app01_publish"."nid" = "app01_book"."publish_id") 
GROUP BY "app01_publish"."nid", "app01_publish"."name", "app01_publish"."city", "app01_publish"."email" 

'''

(2) 练习:统计每一本书的作者个数
ret=Book.objects.annotate(authorsNum=Count('authors__name'))

1
(3) 统计每一本以py开头的书籍的作者个数
 queryResult=Book.objects
           .filter(title__startswith="Py")
           .annotate(num_authors=Count('authors'))

(4) 统计不止一个作者的图书
queryResult=Book.objects
          .annotate(num_authors=Count('authors'))
          .filter(num_authors__gt=1)

(5) 根据一本图书作者数量的多少对查询集 QuerySet进行排序
Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

(6) 查询各个作者出的书的总价格
#   按author表的所有字段 group by
    queryResult=Author.objects
              .annotate(SumPrice=Sum("book__price"))
              .values_list("name","SumPrice")
    print(queryResult)

 

标签:__,name,objects,ORM,ret,查询,跨表,book
From: https://www.cnblogs.com/shiyitongxue/p/17877342.html

相关文章

  • Multi-view Information Integration and Propagation for Occluded Person Re-identi
     这篇从多视角这个思路出发,提出多视图信息集成模块,包括定位、量化和集成。给定具有相同身份的多个图像,MVI2P:i)定位特征图中的空间辨别区域以过滤掉噪声信息。ii)量化不同图像的显着性信息的相对重要性。iii)通过执行逐元素加法来集成多视图信息。iv)通过知识蒸馏将多个图像隐......
  • spark-sql查询Iceberg时处理流程
    1、查询表结构showcreatetabledata_lake_ods.testCREATETABLEspark_catalog.data_lake_ods.test(`user_number`BIGINTNOTNULL,`subclazz_number`BIGINTNOTNULL,`clazz_number`BIGINT,`clazz_lesson_number`BIGINTNOTNULL,`lesson_live_property`......
  • Power BI Report Server自定义Form登录
    一、条件1、windowsserver主机一台,我是windowsserver2019(当然windows10或者10月份5日更新的windows11也是可以行的)。 2、SQLSever,我用的是SQLServer2019。3、PowerBIReportServer默认位置安装(默认位置:C:\ProgramFiles\MicrosoftPowerBIReportServer)。......
  • C#中的并行处理、并行查询的方法你用对了吗?
    Parallel.ForEachParallel.ForEach 是一个用于在集合上并行执行迭代操作的强大工具。它通过有效地利用多核处理器的能力来提高性能。Parallel.ForEach 不仅能够简化并行编程,而且它在执行简单循环时可以提供比传统迭代更好的性能。下面是一个简单的示例,演示了如何使用 Paralle......
  • Laravel开发中的数据库迁移Database Migrations和 对象关系映射Eloquent ORM
    当涉及到Laravel中的数据库操作时,数据库迁移和EloquentORM是两个重要的概念。它们用于管理数据库结构和进行数据操作,但在功能和用途上有所不同。数据库迁移(DatabaseMigrations)是Laravel中用于管理数据库结构变化的工具。它允许您通过编写简单的代码来创建、修改或删除数......
  • SQL SERVER 查询死锁
    DECLARE@SessionNameSysNameSELECT@SessionName='system_health'IFOBJECT_ID('tempdb..#Events')ISNOTNULLBEGINDROPTABLE#EventsENDDECLARE@Target_FileNVarChar(1000),@Target_DirNVarChar(1000),@Target_File......
  • OpenCV4.1.0与CUDAcuda_10.1.105联合进行图像特征点提取和特征匹配时,运行程序时错误提
    问题描述:OpenCV4.1.0与CUDAcuda_10.1.105联合进行图像特征点提取和特征匹配时,运行程序时错误提示:无法定位程序输入点?createBFMatchercv@DescriptorMatcher@cuda@cv......于动态链接库......,如下图所示:解决办法:如果include、lib和dll的路径都配置正确的话,可以尝试将编译好的带......
  • 多表查询
    多表查询:就是输出的字段来自很多表,不单单是一张表 1:交叉连接不适用任何匹配条件,生成笛卡尔积select*fromcoursecrossjoinclass; 2个表的字段都连接在一张大表上2:内连接含义:取出2张表的公共部分,相当于就是从笛卡尔积中筛选出正确的结果就是输出来自多张表,且筛......
  • mybatis sql查询后,返回回来的字段顺序变了;在项目中通过mybatis查询数据库,同样的查询
    问题描述:过程就不看了直接上结果查询语句中的字段顺序信息和返回的字段信息不一致如图:realSql是查询语句,result是查询结果查询语句中的字段顺序信息和返回的字段信息不一致解决方案:转载地址这里复制一份防删......
  • 一次elasticsearch 查询瞬间超时案例分析
    大家好,我是蓝胖子,有段时间没有做性能分析案例讲解了,正好前两天碰到一个性能问题,今天就来对它探讨探讨。问题背景在晚上9点左右,刚从外面逛街回到家,就接到了电话报警(幸好前不久刚好把电话报警机制加上,不然可能我就要去洗澡了......