将MySql查询转换为Django ORM查询

时间:2016-02-23 00:21:06

标签: django django-models django-admin django-orm

我在MySql中有一个查询,我需要将其翻译成Django ORM。它涉及连接两个表,其中一个表有两个计数。我在Django中非常接近它,但我得到了重复的结果。这是查询:

SELECT au.id, 
       au.username, 
       COALESCE(orders_ct, 0) AS orders_ct, 
       COALESCE(clean_ct, 0)  AS clean_ct, 
       COALESCE(wash_ct, 0)   AS wash_ct 
FROM   auth_user AS au 
       LEFT OUTER JOIN 
           ( SELECT user_id,
                    Count(*)  AS orders_ct                    
             FROM   `order`
             GROUP  BY user_id
           ) AS o 
                    ON au.id = o.user_id 
       LEFT OUTER JOIN  
           ( SELECT user_id,
                    Count(CASE WHEN service = 'clean' THEN 1 
                          END)  AS clean_ct,
                    Count(CASE WHEN service = 'wash' THEN 1 
                          END)  AS wash_ct                            
             FROM   job
             GROUP  BY user_id
           ) AS j
                    ON au.id = j.user_id 
ORDER  BY au.id DESC 
LIMIT  100 ;

我当前的Django查询(带回不需要的重复项):

User.objects.annotate(
    orders_ct = Count( 'orders', distinct = True )
).annotate(
    clean_ct = Count( Case(
        When( job__service__exact = 'clean', then = 1 )
    ) )
).annotate(
    wash_ct = Count( Case(
        When( job__service__exact = 'wash', then = 1 )
    ) )
)

上面的Django代码生成以下查询,该查询很接近但不正确:

SELECT DISTINCT `auth_user`.`id`, 
                `auth_user`.`username`, 
                Count(DISTINCT `order`.`id`) AS `orders_ct`, 
                Count(CASE 
                        WHEN `job`.`service` = 'clean' THEN 1 
                        ELSE NULL 
                      end)                   AS `clean_ct`, 
                Count(CASE 
                        WHEN `job`.`service` = 'wash' THEN 1 
                        ELSE NULL 
                      end)                   AS `wash_ct` 
FROM   `auth_user` 
       LEFT OUTER JOIN `order` 
                    ON ( `auth_user`.`id` = `order`.`user_id` ) 
       LEFT OUTER JOIN `job` 
                    ON ( `auth_user`.`id` = `job`.`user_id` ) 
GROUP  BY `auth_user`.`id` 
ORDER  BY `auth_user`.`id` DESC 
LIMIT  100 

我可以通过做一些raw sql subqueries来实现它,但我希望尽可能保持抽象。

3 个答案:

答案 0 :(得分:0)

我认为这样可行,链接的作业注释可能会产生重复的用户。

如果没有,你可以详细说明你看到的副本。

User.objects.annotate(
    orders_ct = Count( 'orders', distinct = True )
).annotate(
    clean_ct = Count( Case(
        When( job__service__exact = 'clean', then = 1 )
    ) ),
    wash_ct = Count( Case(
        When( job__service__exact = 'wash', then = 1 )
    ) )
)

答案 1 :(得分:0)

基于this answer,您可以写:

User.objects.annotate(
    orders_ct = Count( 'orders', distinct = True ),
    clean_ct = Count( Case(
        When( job__service__exact = 'clean', then = F('job__pk') )
    ), distinct = True ),
    wash_ct = Count( Case(
        When( job__service__exact = 'wash', then = F('job__pk') )
    ), distinct = True )
)

表格(加入后):

user.id  order.id  job.id  job.service  your case/when  my case/when
1        1         1       wash         1               1
1        1         2       wash         1               2
1        1         3       clean        NULL            NULL
1        1         4       other        NULL            NULL
1        2         1       wash         1               1
1        2         2       wash         1               2
1        2         3       clean        NULL            NULL
1        2         4       other        NULL            NULL

wash_ct的所需输出为2.在my case/when中计算不同的值,我们将获得2.

答案 2 :(得分:0)

尝试添加values()distinct=True combine Count()'s in one annotation() {。}}。

Users.objects.values("id").annotate(
    orders_ct = Count('orders', distinct = True)
    ).annotate(
    clean_ct = Count(Case(When(job__service__exact='clean', then=1)),
               distinct = True),
    wash_ct = Count(Case(When(job__service__exact='wash',then=1)),
              distinct = True)
    ).values("id", "username", "orders_ct", "clean_ct", "wash_сt")

使用values("id")应为注释添加GROUP BY 'id',从而防止出现重复,请参阅docs

此外,还有Coalesce,但看起来并不需要,因为Count()无论如何都会返回intdistinctdistinct Count()中的Case就足够了。

不确定Count()是否需要<div id="parent"> Text text text text text text text text text text text text text text text <div id="child"></div> //100px from top Text text text text text text text text text text text text text text text <div id="child"></div> //200px from top Text text text text text text text text text text text text text text text </div> ,因为无论如何都应该计算它们。