django查询之聚合函数

django查询之聚合函数,第1张

Django 的django.db.models 模块提供以下聚合函数

警告

SQLite 不能直接处理日期/时间字段的聚合。这是因为SQLite 中没有原生的日期/时间字段,Django 目前使用文本字段模拟它的功能。在SQLite 中对日期/时间字段使用聚合将引发NotImplementedError。

注 :

在QuerySet 为空时,聚合函数函数将返回None。 例如,如果QuerySet 中没有记录,Sum 聚合函数将返回None 而不是0。Count 是一个例外,如果QuerySet 为空,它将返回0

aggregate是monogodb的聚合函数。完成类似于sql中的sum,count,group by等相关 *** 作

1:count(*)

/**

* select count(*) count from zipsController

*/

var pipeline = [

{

$group: {

_id: null,

count: {$sum: 1}

}

}

]

2:sum(*)

/**

* select sum(pop) total from zipsController

*/

var pipeline = [

{

$group: {

_id: null,

total: {$sum: "$pop"}

}

}

]

3:group by

/**

* select sum(pop) total from zipsController group by state

*/

var pipeline =[

{

$group: {

_id: "$state",

totalpop: {$sum: "$pop"}

}

}

]

假设现在关联关系 一个是UserEntity(用户),一个是DepartmentEntity(部门) 它们直接是多对多关系! 因为采取的是hibernate的注解 @JoinTable 。 所以中间实体(Entity)就没有去建立。

扩展资料:

当计算区域中存在错误时,MIN、MAX 和 PERCENTILE 函数不进行计算。出于相同的原因,LARGE、SMALL 和 STDEVP 函数也会影响某些条件格式规则的相应功能。

通过使用 AGGREGATE 函数,这些错误将被忽略,因而可以实现这些函数。此外,AGGREGATE 函数可将不同的聚合函数应用于列表或数据库,并提供忽略隐藏行和错误值的选项。

参考资料来源:百度百科-aggregate

最近做了一个小的需求,在django模型中通过前台页面的表单的提交

(post),后台对post的参数进行解析,通过models模型查询MySQL,将数据结构进行加工,返回到前台页面进行展示。由于对django中

QuerySet特性的不熟悉,所以测试过程中发现了很多问题。

开始的阶段没有遇到什么问题,我们举例,在models有一张员工表

employee,对应的表结构中,postion列表示员工职位,前台post过来的参数赋给position,加上入职时间、离职时间,查询 *** 作通过

models.filter(position=params)完成,获取的员工信息内容由QuerySet和当前展示页与每页展示的记录数进行简单的计

算,返回给前台页面进行渲染展示。编码如下:

1 def get_employees(position, start, end):

2 return employee.objects.filter(alert_time__lt=end,alert_time__gt=start).filter(position__in=position)

3

4

5 @login_required

6 def show(request):

7 if not validate(request):

8 return render_to_response('none.html',

9 context_instance=RequestContext(request, 'msg':'params error')

10 )

11

12 position = request.REQUEST.get('position')

13 time_range = request.REQUEST.get('time')

14 start, end = time_range[0], time_range[1]

15

16 num_per_page, page_num = get_num(request)

17 all_employees = get_employees(position, start, end)

18 # 根据当前页与每页展示的记录数,取到正确的记录

19 employees = employees_events[(page_num-1)*num_per_page:page_num*num_per_page]

20

21 return render_to_response('show_employees.html',

22 context_instance=RequestContext(

23 request,

24 'employees': employees,

25 'num_per_page': num_per_page,

26 'page_num':page_num,

27 'page_options' : [50, 100, 200]

28 )

29 )

运行之后可以正确的对所查询的员工信息进行展示,并且查询速度很快。

employee表中存放着不同职位的员工信息,不同类型的详细内容也不相同,假设employees有一列名为infomation,存储的是员工的详

细信息,infomation = {'age': 33, 'gender': 'male', 'nationality': 'German',

'degree': 'doctor', 'motto': 'just do

it'},现在的需求是要展示出分类更细的员工信息,前台页面除了post职位、入职离职时间外,还会对infomation中的内容进行筛选,这里以查

询中国籍的设计师为例,在之前的代码基础上,需要做一些修改。员工信息表employee存放于MySQL中,而MySQL为ORM数据库,它并未提供类

似mongodb一样更为强大的聚合函数,所以这里不能通过objects提供的方法进行filter,一次性将所需的数据获取出来,那么需要对type

进行过滤后的数据,进行二次遍历,通过information来确定当前记录是否需要返回展示,在展示过程中,需要根据num_per_page和

page_num计算出需要展示数据起始以及终止位置。

1 def get_employees(position, start, end):

2 return employee.objects.filter(alert_time__lt=end,alert_time__gt=start).filter(position__in=position)

3

4

5 def filter_with_nation(all_employees, nationality, num_per_page, page_num):

6 result = []

7

8 pos = (page_num-1)*num_per_page

9 cnt = 0

10 start = False

11 for employee in all_employees:

12 info = json.loads(employee.information)

13 if info.nationality != nationality:

14 continue

15

16 # 获取的数据可能并不是首页,所以需要先跳过前n-1页

17 if cnt == pos:

18 if start:

19 break

20 cnt = 0

21 pos = num_per_page

22 start = True

23

24 if start:

25 result.append(employee)

26

27 return employee

28

29

30 @login_required

31 def show(request):

32 if not validate(request):

33 return render_to_response('none.html',

34 context_instance=RequestContext(request, 'msg':'params error')

35 )

36

37 position = request.REQUEST.get('position')

38 time_range = request.REQUEST.get('time')

39 start, end = time_range[0], time_range[1]

40

41 num_per_page, page_num = get_num(request)

42 all_employees = get_employees(position, start, end)

43

44 nationality = request.REQUEST.get('nationality')

45

46 employees = filter_with_nation(all_employees, num_per_page, page_num)

47

48 return render_to_response('show_employees.html',

49 context_instance=RequestContext(

50 request,

51 'employees': employees,

52 'num_per_page': num_per_page,

53 'page_num':page_num,

54 'page_options' : [50, 100, 200]

55 )

56 )

当编码完成之后,在数据employee表数据很小的情况下测试并未发现问

题,而当数据量非常大,并且查询的数据很少时,代码运行非常耗时。我们设想,这是一家规模很大的跨国公司,同时人员的流动量也很大,所以employee

表的数据量很庞大,而这里一些来自于小国家的员工并不多,比如需要查询国籍为梵蒂冈的员工时,前台页面进入了无尽的等待状态。同时,监控进程的内存信息,

发现进程的内存一直在增长。毫无疑问,问题出现在filter_with_nation这个函数中,这里逐条遍历了employee中的数据,并且对每条

数据进行了解析,这并不是高效的做法。

在网上查阅了相关资料,了解到:

1 Django的queryset是惰性的,使用filter语句进行查询,实际上并没有运行任何的要真正从数据库获得数据

2 只要你查询的时候才真正的 *** 作数据库。会导致执行查询的 *** 作有:对QuerySet进行遍历queryset,切片,序列化,对 QuerySet 应用 list()、len()方法,还有if语句

3 当第一次进入循环并且对QuerySet进行遍历时,Django从数据库中获取数据,在它返回任何可遍历的数据之前,会在内存中为每一条数据创建实例,而这有可能会导致内存溢出。

上面的原来很好的解释了代码所造成的现象。那么如何进行优化是个问题,网上有

说到当QuerySet非常巨大时,为避免将它们一次装入内存,可以使用迭代器iterator()来处理,但对上面的代码进行修改,遍历时使用

employee.iterator(),而结果和之前一样,内存持续增长,前台页面等待,对此的解释是:using iterator()

will save you some memory by not storing the result of the cache

internally (though not necessarily on PostgreSQL!)but will still

retrieve the whole objects from the database。

这里我们知道不能一次性对QuerySet中所有的记录进行遍历,那么只能对

QuerySet进行切片,每次取一个chunk_size的大小,遍历这部分数据,然后进行累加,当达到需要的数目时,返回满足的对象列表,这里修改下

filter_with_nation函数:

1 def filter_with_nation(all_employees, nationality, num_per_page, page_num):

2 result = []

3

4 pos = (page_num-1)*num_per_page

5 cnt = 0

6 start_pos = 0

7 start = False

8 while True:

9 employees = all_employees[start_pos:start_pos+num_per_page]

10 start_pos += num_per_page

11

12 for employee in employees:

13 info = json.loads(employee.infomation)

14 if info.nationality != nationality:

15 continue

16

17 if cnt == pos:

18 if start:

19 break

20 cnt = 0

21 pos = num_per_page

22 start = True

23

24 if start:

25 result.append(opt)

26

27 cnt += 1

28

29 if cnt == num_per_page or not events:

30 break

31

32 return result

运行上述代码时,查询的速度更快,内存也没有明显的增长,得到效果不错的优

化。这篇文章初衷在于记录自己对django中queryset的理解和使用,而对于文中的例子,其实正常业务中,如果需要记录员工详细的信息,最好对

employee表进行扩充,或者建立一个字表,存放详细信息,而不是将所有信息存放入一个字段中,避免在查询时的二次解析。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/10828833.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-11
下一篇 2023-05-11

发表评论

登录后才能评论

评论列表(0条)

保存