django-ORM之聚合函數(shù)和CASE WHEN的使用

?。。?/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/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容