连接3个表的MySQL查询给出错误的结果

时间:2018-08-10 15:26:45

标签: mysql sql join

我与请求对象(请求表)或回复请求(操作表)的用户一起使用数据库(下面附有结构)。

Database structure

(注意:local snowPart = game.Workspace.Snow.SnowPart -- part I want to change while snowPart.Size.Y <= 0 do wait(10) snowPart.Size.Y = snowPart.Size + Vector3.new(0, 0.7, 0) --increment if the part gets too small end function onTouch(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChildWhichIsA("Humanoid") if humanoid then wait(5) snowPart.Size = snowPart.Size - Vector3.new(0, 0.7, 0) --increment the part's size when touched by a player end end snowPart.Touched:Connect(onTouch) 是请求者的ID)

我想找到一个查询,该查询可以为我提供在一个月内执行请求或已答复请求的用户,以及请求数,答复数和总数(请求+答复)。

我正在查询2018年3月的请求数量:

request.user_request_id

再加上一个答复数:

SELECT user.email, COUNT(request.user_request_id) AS requests 
FROM request LEFT JOIN user ON request.user_request_id = user.id 
WHERE DATE_FORMAT(STR_TO_DATE(request.created_at, '%m-%d-%Y'), '%Y-%m') 
= '2018-03' 
GROUP BY user_request_id 
ORDER BY number desc;

但是,如果我尝试对3个表使用单个查询,则会出错。 (例如,对于发出3个请求和4个回复的用户,它给了我9个请求和9个回复。) 这是我使用的查询:

SELECT user.email, COUNT(action.lender_id) AS replies 
FROM action LEFT JOIN user ON action.lender_id = user.id 
WHERE DATE_FORMAT(STR_TO_DATE(action.created_at, '%m-%d-%Y'), '%Y-%m') = 
'2018-03' 
GROUP BY lender_id 
ORDER BY number desc;

有人知道我在做什么吗?

3 个答案:

答案 0 :(得分:1)

您没有做同样的LEFT JOIN

在第二个请求中,您使用"action.lender_id = user.id"进行操作和用户之间的联接,但是在第三个查询中,您使用"request.object_id=action.object_id"进行请求和操作之间的联接,因此基本上您不会获得相同的结果< / p>

答案 1 :(得分:1)

您缺少JOIN条件。您在request上加入了actionobject_id,但没有在request_id上加入。您当前的查询将JOIN的每一行请求都满足object_id上所有条件的所有操作。

SELECT user.email, 
    COUNT(request.user_request_id) AS requests, 
    COUNT(action.lender_id) AS replies, 
    COUNT(request.user_request_id) + COUNT(action.lender_id) AS total 
FROM request 
LEFT JOIN user ON user.id = request.user_request_id 
LEFT JOIN action ON request.object_id = action.object_id  AND request.user_request_id = action.request_id
WHERE DATE_FORMAT(STR_TO_DATE(request.created_at, '%m-%d-%Y'), '%Y-%m') = '2018-03' 
    OR DATE_FORMAT(STR_TO_DATE(action.created_at, '%m-%d-%Y'), '%Y-%m') = '2018-03' 
GROUP BY user.email 
ORDER BY total desc;

答案 2 :(得分:1)

对于基于日期的查询,以及在您的方案中,寻找2018年3月的任何内容(通过2018-03字符串值),当您必须将每个日期转换为字符串时,日期字段都不会得到任何优化然后看看是否合适。

相反,您知道月/年,因此将where子句设置为第一个,然后将LESS设置为结束后的第二天。例如:您想要2018年3月。所以> ='2018-03-01'没问题。最后,如果您的日期有时间,并且您希望包括截至3月31日晚上11:59:59 pm,那么您将比“ 2018-04-01”少。因此,现在您可以解决整个月的问题,并且可以根据您的created_at日期/时间字段使用索引。

下一部分是您加入动作。作为左联接的动作意味着所有请求,无论找到了什么动作。但是,您随后将操作日期添加为where子句的一部分,从而将其更改为INNER JOIN(即使条件之间是OR的一部分)。这将导致DID在该时间段内未执行从结果集中排除的操作的任何请求。您可能需要将其转移到JOIN子句。其中,真的需要吗?该操作将永远不会出现在请求之前,但是3月29日的请求又在3月30日,4月1日,4月2日完成了操作,您想要的结果计数是1(3月29日)还是3(包括4月1日和4月2日)。

其他简单调整。所有请求都必须具有用户ID,但您必须将其作为左联接。其比例为1:1,因此应该将其设置为普通的INNER JOIN。

SELECT 
      u.email, 
      COUNT(r.user_request_id) AS requests, 
      COUNT(a.lender_id) AS replies, 
      COUNT(r.user_request_id) + COUNT(a.lender_id) AS total 
   FROM 
      request r
         INNER JOIN user u
            ON r.user_request_id = u.id
         LEFT JOIN action a
            ON r.object_id = a.object_id 
            -- if you explicitly want to RESTRICT Actions within same date range
            AND a.created_at >= '2018-03-01'
            AND a.created_at < '2018-04-01'
   WHERE 
          r.created_at >= '2018-03-01'
      AND r.created_at < '2018-04-01'
   GROUP BY 
      u.email 
   ORDER BY 
      total desc;

现在,当您尝试上述类似查询尝试一次尝试时,您会提到错误答案。这可能是由于笛卡尔结果。对于请求中具有回复的每条记录,您现在都可以多次应用该计数。您可能需要将每个单独的计数保留为自己的查询,因此对于给定的用户,每个用户/贷方仅对其一次正确计数一次

SELECT 
      u.email,
      coalesce( R1.RequestCnt, 0 ) Requests,
      coalesce( A1.ActionCnt, 0 ) Replies,
      coalesce( R1.RequestCnt, 0 ) 
         + coalesce( A1.ActionCnt, 0 ) AS total 
   FROM 
      user u
         LEFT JOIN 
         ( select 
                 r.user_request_id as userID, 
                 count(*) requestCnt
              from 
                 request r
              WHERE 
                     r.created_at >= '2018-03-01'
                 AND r.created_at < '2018-04-01'
              group by 
                 r.user_request_id ) R1
            ON u.id = R1.userID

         LEFT JOIN
         ( select 
                 a.lender_id as userID, 
                 count(*) actionCnt
              from 
                 action a
              WHERE 
                     a.created_at >= '2018-03-01'
                 AND a.created_at < '2018-04-01'
              group by 
                 a.lender_id ) A1
            ON u.id = A1.userID
   where 
      coalesce( R1.RequestCnt, 0 ) 
         + coalesce( A1.ActionCnt, 0 ) > 0
   ORDER BY 
      coalesce( R1.RequestCnt, 0 ) 
         + coalesce( A1.ActionCnt, 0 ) desc;

现在,如果您有大量用途(例如数百万种),我们可以通过在相关日期范围内预先获取不同用户的列表来进一步缩短查询的时间...

   from
      ( select distinct
              r.user_request_id as userID
           from 
              request r
           WHERE 
                  r.created_at >= '2018-03-01'
              AND r.created_at < '2018-04-01'
        UNION
        select a.lender_id as userID
           from 
              action a
           WHERE 
                  a.created_at >= '2018-03-01'
              AND a.created_at < '2018-04-01' ) as UniqUsers
          JOIN user u
             on UniqUsers.UserID = u.id
             -- rest of the other pre-queries above...