?。。?/code>for循環(huán)執(zhí)行sql操作,那樣是最愚蠢的做法?。?!
業(yè)務(wù)場(chǎng)景
有這樣一個(gè)場(chǎng)景:兩張表,文件表 和 文件內(nèi)容表/query表,表結(jié)構(gòu)如下,由于公司規(guī)定,沒(méi)有辦法使用外鍵,所以使用了邏輯關(guān)聯(lián)(文件和query是一對(duì)多的關(guān)系)。
class File(models.Model):
id = models.AutoField(primary_key=True, editable=False)
file_name = models.CharField(max_length=255, db_index=True)
create_time = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(default=1, db_index=True) # 0:已經(jīng)標(biāo)完,頁(yè)面不可見(jiàn) 1:未標(biāo)完,頁(yè)面可見(jiàn)
hash = models.CharField(max_length=255)
in_mysql = models.IntegerField(default=0, db_index=True) # 0:默認(rèn)是0,沒(méi)有同步。后期同步后,變?yōu)?
count = models.IntegerField() # 文件中query的條數(shù)
def __unicode__(self):
return self.file_name
class Meta:
db_table = 'mark_file'
class Query(models.Model):
id = models.AutoField(primary_key=True, editable=False)
query = models.CharField(max_length=255)
status = models.IntegerField(default=0) # 0:未標(biāo)注,1:白名單,2:黑名單,3:廢棄,4:待擴(kuò)充
update_time = models.DateTimeField(auto_now=True)
file_id = models.IntegerField(db_index=True) # 根據(jù)此字段進(jìn)行邏輯關(guān)聯(lián)
def __unicode__(self):
return self.query
class Meta:
db_table = 'mark_query'
def save(self, *args, **kwargs):
self.update_time = datetime.datetime.now()
super(Query, self).save(*args, **kwargs)
現(xiàn)在需要查詢出id為 1、2、3、4、5、6的文件信息,并且計(jì)算出各個(gè)文件中對(duì)應(yīng)的各個(gè)狀態(tài)的query(status=0,status=3,status=4)的數(shù)量。以達(dá)到下面的效果。

file.png
簡(jiǎn)單實(shí)現(xiàn),不考慮性能
如果是不考慮性能問(wèn)題,可能會(huì)使用,先查出id為1、2、3、4、5、6的文件,再for循環(huán)一個(gè)個(gè)count,例如:
for file_obj in file_list:
file_id = file_obj.id
# 未標(biāo)注
no_recall_count = Query.objects.filter(Q(file_id=file_id) & Q(status=0)).count()
# 已廢棄
no_use_count = Query.objects.filter(Q(file_id=file_id) & Q(status=3)).count()
# 待擴(kuò)充
no_use_count = Query.objects.filter(Q(file_id=file_id) & Q(status=4)).count()
但是這樣做是最笨,最不合理的做法。這才是6個(gè)文件,如果是100個(gè)文件,應(yīng)該查詢100?3=300次數(shù)據(jù)庫(kù),才能得出最終結(jié)果。對(duì)于一個(gè)接口來(lái)說(shuō),數(shù)據(jù)庫(kù)操作次數(shù)越少越好,查這么多次簡(jiǎn)直是一個(gè)天文數(shù)字。
其實(shí)可以這樣做,一次查詢就可以得出統(tǒng)計(jì)結(jié)果。(django1.10.8)
from django.db.models import Q, F, Count, When, Case
files_id = [1,2,3,4,5,6]
counts = []
files_count = Query.objects.filter(file_id__in=files_id).values('file_id').annotate(
no_recall_count=Count(Case(When(status=0, then=0))),
no_use_count=Count(Case(When(status=3, then=0))),
expand=Count(Case(When(status=4, then=0))))
for files_count_obj in files_count:
counts.append({
"file_id": files_count_obj.get('file_id'),
"no_recall_count": files_count_obj.get('no_recall_count'),
"no_use_count": files_count_obj.get('no_use_count'),
"expand_count": files_count_obj.get('expand'),
})
sql語(yǔ)句就是
SELECT `mark_query`.`file_id`,
COUNT(CASE WHEN `mark_query`.`status` = 0 THEN 0 ELSE NULL END) AS `no_recall_count`,
COUNT(CASE WHEN `mark_query`.`status` = 3 THEN 0 ELSE NULL END) AS `no_use_count`,
COUNT(CASE WHEN `mark_query`.`status` = 4 THEN 0 ELSE NULL END) AS `expand`
FROM `mark_query` WHERE `mark_query`.`file_id` IN (1, 2, 3, 4, 5, 6)
GROUP BY `mark_query`.`file_id`;
如果是django2.0以上,還可以這樣(沒(méi)有驗(yàn)證是否可行)
https://www.bbsmax.com/A/KE5QKvLPzL/