问题
I have: Model:
id = PrimaryKey()
value = IntegerField()
I would like to get list of ids group by value range between 1-7
and 8-30
and >30
.
What would be the efficient way to do so?
Or shall I write query for each range like:
one= Model.objects.filter(value__gt=0,value__lte=7).values_list(id)
two= Model.objects.filter(value__gt=7,value__lte=30).values_list(id)
three= Model.objects.filter(value__gt=30).values_list(id)
...
can I do so via aggregate Count
? I want minimum database transactions.
Updated:
Actually am trying to get how many products are sold, If they expires soon, then weekly count else monthly like:
- Milk expires in 3 days. How many milk packets were sold in past 3
days
- Bread expires in 7 days. How many bread were sold in past 7 days.
here value
is expires_in
i.e. in how many days a product expires in (expiry date - mfg date)
Model is like:
InvoiceBreakup
product_id
name
desctiption
expires_in
回答1:
With groupby
If you need the ids
, we can use groupby
of itertools
for this:
from itertools import groupby
from operator import itemgetter
def rank(itm):
vl = itm['value']
if vl <= 7:
return 0
elif vl <= 30:
return 1
return 2
dat = {
k: list(map(itemgetter('id'),v))
for k, v in groupby(
Model.objects.filter(value__gt=0).values('id', 'value').order_by('value'),
rank)
}
Now dat
will contain three elements: 0
contains the list of elements in the range of 1-7
, 1
for elements in the range 8-30
and 2
in are id
s with value 30+.
We can thus then obtain the list of ids
, with:
dat[0] # ids with value 1-7
dat[1] # ids with value 8-30
dat[2] # ids with value 31+
This will result in a single query, and we let Python do the grouping. This can be slow if the data is huge, but it will already get quite slow, since the deserializing of the elements also will take comparable effort.
Optimizing the checks
We can let the database perform the logic to determine the category, and thus move some work from Python to the database:
from django.db.models import Case, IntegerField, Value
qs = Model.objects.filter(value__gt=0).annotate(
cat = Case(
When(value__lt=8, then=Value(0)),
When(value__lt=31, then=Value(1)),
default=Value(2),
output_field=IntegerField(),
))
).values('id', 'cat').order_by('cat')
and then construct the dictionary with:
from operator import itemgetter
from itertools import groupby
data = {
k: list(map(itemgetter('id'), v))
for k, v in groupby(qs, itemgetter('cat'))
}
来源:https://stackoverflow.com/questions/51928253/get-ids-group-by-range-django