合并具有类似where子句但具有不同聚合函数目标的2个查询

时间:2016-05-04 16:35:48

标签: mysql sql performance join

我有2个查询可以单独运行。鉴于它们相似,我想将它们合并为一个高性能查询。看起来很简单,因为where子句是相似的。但是sum,count和min函数都适用于不同的行并妨碍它们。

上下文:

  • 用户可以对某个位置进行评分(或评分)并获得积分
  • 当用户B首次提交分数
  • 时,用户A可以推荐用户B并获得推荐点
  • 点数在特定日期之后到期
  • 目标是建立一个用户排行榜及其得分和参考特定位置(地区/国家/地区)的总分。
  • 位置参数使用“马萨诸塞州”,“美国”和scoreDateTime到期日期的硬值填充,但不幸的是在两个选择的子查询中都重复。

问题:

如何重组下面的查询以组合约束?必须有一种方法可以在特定日期之后从特定位置开始获得分数列表。唯一的复杂因素是获得用户B的第一个得分日期,并且只有在用户A到期日之后才向其提供推荐积分。

select scoring.userId, scoring.points + referring.points as leaderPoints
from (
    select   userId, sum(ratingPoints) as points
    from     scores s, locations l
    where    s.locationId = l.locationId and
             l.locationArea = 'Massachusetts' and
             l.locationCountry = 'United States' and
             s.scoreDateTime > '2016-04-16 18:50:53.154' and
             s.userId != 0
    group by s.userId
) as scoring

join (
    select u1.userId, count(*) * 20 as points
    from users u0
    join users u1 on u0.userId = u1.userId
    join users u2 on u2.referredByEmail = u1.emailAddress
    join scores s on u2.userId = s.userId
    join locations l on s.locationId = l.locationId
    where    l.locationArea = 'Massachusetts' and
             l.locationCountry = 'United States' and
             scoreDateTime = (
                 select min(scoreDateTime)
                 from   scores
                 where  userId = u2.userId
             ) and
             scoreDateTime >= '2016-04-16 18:50:53.154'
    group by u1.userId
) as referring on scoring.userId = referring.userId
order by leaderPoints desc
limit 10;

2 个答案:

答案 0 :(得分:0)

这是未经测试的代码,但它应该可以解决问题。 Cross Apply是为了提高可读性......它会损害性能,但这似乎不是一个特别针对流程的查询,所以我会保留它。

如果您有任何疑问,请试试看。

SELECT  U.UserID, 
        ISNULL(SUM(CASE WHEN S.UserID IS NULL THEN 0 ELSE S.ratingPoints END), 0) AS [Rating Points], 
        ISNULL(SUM(CASE WHEN SS.userID IS NULL THEN 0 ELSE 20 END), 0) AS [Referral Points]
FROM Users U
LEFT OUTER JOIN scores S
    ON S.userID = U.userID
    AND S.scoreDateTime >= '2016-04-16 18:50:53.154'
LEFT OUTER JOIN locations L
    ON S.locationID = L.locationID
    AND L.locationArea = 'Massachusetts'
    AND L.LocationCountry = 'United States'
LEFT OUTER JOIN Users U2
    ON U2.referredByEmail = U.emailAddress
LEFT OUTER JOIN scores SS
    ON SS.userID = U2.userID
LEFT OUTER JOIN locations LL
    ON SS.locationID = LL.locationID
    AND LL.locationArea = 'Massachusetts'
    AND LL.locationCountry = 'United States'
    AND SS.scoreDateTime >= '2016-04-16 18:50:53.154'
    AND SS.scoreDateTime = 
        (
            SELECT MIN(scoreDateTime)
            FROM scores
            where userID = U2.userID
        )
GROUP BY U.userID

编辑:

删除交叉申请的修改后的答案

答案 1 :(得分:0)

感谢Stan Shaw,但我无法让您的查询在MySQL上运行以测试结果。但是,我确实注意到我的原始查询未涵盖的特殊情况。用户可以从他们自己未提交分数的区域获得参考点。只要新用户在该区域得分,他们就会获得参考点。

这是我正在使用的最终查询。我无法以一种看似高效的方式将副本合并到where子句中。

select userId, sum(points) as leaderPoints
from (

    select   s.userId, sum(s.ratingPoints) as points
    from     scores s, locations l
    where    s.locationId = l.locationId and  
             l.locationArea = 'Georgia' and  
             l.locationCountry = 'United States' and  
             s.scoreDateTime >= '2016-04-05 03:00:00.000' and  
             s.userId != 1
    group by userId

    union

    select   u1.userId, 20 as points
    from     users u0, users u1, users u2, scores s, locations l
    where    u0.userId = u1.userId and
             u2.referredByEmail = u1.emailAddress and
             u2.userId = s.userId and
             s.locationId = l.locationId and
             l.locationArea = 'Georgia' and  
             l.locationCountry = 'United States' and  
             scoreDateTime >= '2016-04-05 03:00:00.000' and
             scoreDateTime = (
                 select min(scoreDateTime) 
                 from   scores  
                 where  userId = u2.userId  
             )

) as pointsEarned
group by userId
order by leaderPoints desc
limit 10
order by leaderPoints desc  
limit 100;