MySQL - 使用表连接过滤计数查询

时间:2013-02-09 16:49:55

标签: mysql join count

我有一个用户表,一个帖子表,一个私有表和一个user_stats表:

users                     
user_id |  user_name
--------------------
   1    |    tony
   2    |    steph
   3    |    lizzy
   4    |    adam

posts
post_id   user_id     sugg_by   private (0 is public, 1 is private to authorized users)
-----------------------------------------------------
   1         1           2         0
   2         2           2         1
   3         2           2         1            
   4         2           4         1
   5         2           2         1
   6         2           3         0

private
private_id   post_id   authorized_user_id
-----------------------------------------------
    1           2               4
    2           2               3
    3           4               4
    4           5               1
    5           5               3

user_stats                     
user_id  orig_posts_count(user_id=sugg_by in posts)  sugg_posts_count(user_id<>sugg_by in posts) 
-----------------------------------------------------------------------------------------------------
   1             0                                          1
   2             3                                          2
   3             0                                          0
   4             0                                          0

我遇到的问题是在这个查询中。在这个例子中,'4'是$ logged_in_id adam,'2'是我们为steph计算的帖子的user_id。如果steph是logged_in,则COUNT将不会运行,我们将提供'posterview',这是有效的。返回任何其他登录用户或任何退出的人,问题就开始了。所以对于亚当'4':

SELECT u.user_id, us.orig_posts_count, us.sugg_posts_count,
IF(us.user_id='4', 'posterview',
COUNT(case when ISNULL(pv.post_id) AND p.private='1' AND p.user_id='2' AND p.sugg_by='2' then null else 1 end)
) as display_orig_posts_count,
IF(us.user_id='4', 'posterview',
COUNT(case when ISNULL(pv.post_id) AND p.private='1' AND p.user_id='2' AND p.sugg_by<>'2' then null else 1 end)
) as display_sugg_posts_count
FROM users u 
JOIN user_stats us ON u.user_id=us.user_id 
JOIN posts p ON p.user_id=us.user_id
LEFT JOIN private pv on pv.post_id = p.post_id AND pv.authorized_user_id='4' 
WHERE u.user_id='2' LIMIT 1

输出应为:

user_id  orig_posts_count  sugg_posts_count  display_orig_posts_count  display_sugg_posts_count
   2            3                  2                    1                          2

然而outputs为:

user_id  orig_posts_count  sugg_posts_count  display_orig_posts_count  display_sugg_posts_count
   2            3                  2                    3                          5

我认为原因在于JOIN posts p ON p.user_id=us.user_id

如果我将其转换为JOIN posts p ON p.user_id=us.user_id AND p.sugg_by='2'(与display_orig_posts_count的第一个COUNT匹配),则display_orig_posts_count为1,这是正确的,但display_sugg_posts_count在3处不正确。我得到正确的display_orig_posts_count和不正确的display_sugg_posts_count。 Outputs as:

user_id  orig_posts_count  sugg_posts_count  display_orig_posts_count  display_sugg_posts_count
   2            3                  2                    1                          3

如果我将其转换为JOIN posts p ON p.user_id=us.user_id AND p.sugg_by<>'2'(与display_sugg_posts_count的第二个COUNT匹配),则display_orig_posts_count为2,这是不正确的,但display_sugg_posts_count在2. Outputs处正确为:

user_id  orig_posts_count  sugg_posts_count  display_orig_posts_count  display_sugg_posts_count
   2            3                  2                    2                          2

基本上,登录用户如果steph应该返回'posterview',但是任何其他使用COUNT的用户应该只看到公开(私有0)或(私有1)的帖子,只有它们是它的一部分,然后根据COUNT子句中的条件进行计数,以获得所述的正确输出。我已经坚持了几个小时。知道如何让查询正常工作吗? 注意:每个示例输出都包含小提琴。

1 个答案:

答案 0 :(得分:3)

将您的查询更改为:

  SELECT u.user_id, us.orig_posts_count, us.sugg_posts_count,
    IF(us.user_id='4', 'posterview',
      COUNT(CASE
        WHEN p.sugg_by='2' AND (p.private='0' OR pv.post_id IS NOT NULL)
        THEN 1
        ELSE NULL
      END)) AS display_orig_posts_count,
    IF(us.user_id='4', 'posterview',
      COUNT(CASE
        WHEN p.sugg_by<>'2' AND (p.private='0' OR pv.post_id IS NOT NULL)
        THEN 1
        ELSE NULL
      END)) AS display_sugg_posts_count
    FROM users AS u
         JOIN user_stats AS us ON u.user_id=us.user_id
         JOIN posts AS p ON p.user_id=us.user_id
         LEFT JOIN private AS pv ON pv.post_id=p.post_id AND pv.authorized_user_id='4'
   WHERE u.user_id='2'
GROUP BY u.user_id, us.orig_posts_count, us.sugg_posts_count

详细说明

在您的第一个查询中,正在计算以下帖子(有理由):

display_orig_posts_count = 3:
  post_id=2: pv.post_id is not null (fails ISNULL() check)
  post_id=4: pv.post_id is not null (fails ISNULL() check)
  post_id=6: p.private='0'          (fails p.private='1' check)

display_sugg_posts_count = 5:
  post_id=2: pv.post_id is not null (fails ISNULL() check)
  post_id=3: p.sugg_by='2'          (fails p.sugg_by<>'2' check)
  post_id=4: pv.post_id is not null (fails ISNULL() check)
  post_id=5: p.sugg_by='2'          (fails p.sugg_by<>'2' check)
  post_id=6: p.private='0'          (fails p.private='1' check)

在您的第二个查询中,正在计算以下帖子(有理由):

display_orig_posts_count = 1:
  post_id=2: pv.post_id is not null (fails ISNULL() check)

display_sugg_posts_count = 3:
  post_id=2: pv.post_id is not null (fails ISNULL() check)
  post_id=3: p.sugg_by='2'          (fails p.sugg_by<>'2' check)
  post_id=5: p.sugg_by='2'          (fails p.sugg_by<>'2' check)'

在您的第三个查询中,正在计算以下帖子(有理由):

display_orig_posts_count = 2:
  post_id=4: pv.post_id is not null (fails ISNULL() check)
  post_id=6: p.private='0'          (fails p.private='1' check)

display_sugg_posts_count = 2:
  post_id=4: pv.post_id is not null (fails ISNULL() check)
  post_id=6: p.private='0'          (fails p.private='1' check)

我们想要算什么:

display_orig_posts_count = 1:
  post_id=2: because p.sugg_by=2, p.private=1 AND (is_authorized)

display_sugg_posts_count = 3:
  post_id=4: because p.sugg_by<>'2', p.private=1 AND (is_authorized)
  post_id=6: because p.sugg_by<>'2', p.private=0

所以让我们清理一下:

  1. 从`CASE1语句中删除p.user_id='2'

    无论如何,这始终是正确的,因为您通过user_id='2'语句将帖子限制为只有JOIN的帖子。

  2. 使用肯定性支票

    如果是display_orig_posts_count,我们想要某些事情。我们不是试图过滤掉我们不想要的东西,而是明确地寻找我们想要的东西:

     COUNT(CASE
       WHEN p.sugg_by='2' AND (p.private='0' OR pv.post_id IS NOT NULL)
       THEN 1
       ELSE NULL
     )
    

    display_sugg_posts_count的情况下,让我们再次明确地查找这些内容:

     COUNT(CASE
       WHEN p.sugg_by<>'2' AND (p.private='0' OR pv.post_id IS NOT NULL)
       THEN 1
       ELSE NULL
     )
    
  3. 使用汇总功能时使用GROUP BY

    如果您正确地对聚合函数进行分组,则您也不需要LIMIT 1。每个用户只能返回一行。