6

我目前正在使用一个积分系统,该系统使用下面的查询来列出谁在一个类别或直接在它下面的任何类别中拥有最多的点(使用“category_relations 表”中的“links_to”和“links_from”)。

SELECT  u.name, 
        pa.user_id, 
        SUM(IF(pa.plus = '1', pa.points_amount, 0)) - 
        SUM(IF(pa.plus = '0', pa.points_amount, 0)) AS points
FROM    points_awarded pa, Users u
WHERE   u.user_id = pa.user_id AND 
        (
           category_id = '" . $category_id . "' OR 
           category_id IN
               (
                   SELECT links_to
                   FROM category_relations
                   WHERE links_from = '" . $category . "'
               )
        ) 
GROUP BY user_id 
ORDER BY points DESC 
LIMIT 50

我现在要做的是切换此查询以查找特定用户的所有类别,而不是查找特定类别的所有用户。以下是我尝试使用的查询,但这仅包括该类别的点数,而不包括下面的子类别。

SELECT   u.name, 
         pa.user_id, 
         pa.category_id,
         SUM(IF(pa.plus = '1', pa.points_amount, 0)) - 
         SUM(IF(pa.plus = '0',pa.points_amount, 0)) AS points
FROM     points_awarded pa, Users u
WHERE    u.user_id = pa.user_id AND 
         u.user_id = '" . $user_id . "'
GROUP BY pa.category_id 
ORDER BY points DESC 
LIMIT 50
4

2 回答 2

5

解决方案

从您的示例查询中,我能够简化您的表结构,它为我提供了足够的信息来回答您的问题。您可以使用此解决方案:

SELECT   a.user_id, 
         a.name, 
         b.category_id,
         SUM(IF(d.plus = '1', d.points_amount, 0)) -
         SUM(IF(d.plus = '0', d.points_amount, 0)) AS points
FROM     Users a
JOIN     points_awarded b ON a.user_id = b.user_id
JOIN     (
         SELECT links_from, links_to 
         FROM   category_relations 
         UNION
         SELECT links_from, links_from
         FROM   category_relations 
         ) c ON b.category_id = c.links_from
JOIN     points_awarded d ON c.links_to = d.category_id
WHERE    a.user_id = $user_id
GROUP BY a.user_id,
         a.name,
         b.category_id
ORDER BY points DESC
LIMIT    50

$user_id查询的用户 ID 参数在哪里。


分解

基本上这个查询的关键部分是我们选择category_relations表的方式。

由于您想要子类别点的总和(按每个父类别)加上它们各自的父类别的点,我们可以通过垂直添加父类别UNION并基本上使其成为其自身的子类别。然后,这将很好地纳入GROUP BYand SUM

假设我们有一些(简化的)数据,如下所示:

Users
------------------
user_id  |  name
433      |  Zane

points_awarded
------------------
user_id  |  category_id  |  plus  |  points_amount
433      |  1            |  1     |  785
433      |  2            |  1     |  871
433      |  3            |  1     |  236
433      |  4            |  0     |  64
433      |  5            |  0     |  12
433      |  7            |  1     |  897
433      |  8            |  1     |  3
433      |  9            |  0     |  48
433      |  10           |  1     |  124
433      |  14           |  0     |  676

category_relations
------------------
links_from | links_to
1          | 2
1          | 3
1          | 4
5          | 8
5          | 9
7          | 10
7          | 14

为了区分父类别和子类别,查询points_awarded使用表中的两个字段对表进行自交叉引用连接category_relations

如果我们只是在category_relations没有 的情况下加入表UNION,我们得到的连接看起来像(简化的列):

points_awarded ⋈ (links_from) category_relations ⋈ (links_to) points_awarded:

category_id  |  points_amount  |  category_id  |  points_amount
1            |  785            |  2            |  871
1            |  785            |  3            |  236
1            |  785            |  4            |  64
5            |  12             |  8            |  3
5            |  12             |  9            |  48
7            |  897            |  10           |  124
7            |  897            |  14           |  676

如您所见,最左边category_id是父类别,最右边category_id是各自的子类别。我们可以轻松地对第一个 category_id 和SUM第二个进行分组points_amount...

但是等等,我们也需要包含父类别points_amount!我们如何让第一个points_amount明显进入第二个points_amount?这就是UNION发挥作用的地方。

在我们执行我们的自交叉引用连接之前,我们选择category_relations表以便稍微改变它:

SELECT links_from, links_to FROM category_relations
UNION
SELECT links_from, links_from FROM category_relations

然后导致:

category_relations (subselected)
------------------
links_from | links_to
1          | 1       <-- parent category
1          | 2
1          | 3
1          | 4
5          | 5       <-- parent category
5          | 8
5          | 9
7          | 7       <-- parent category
7          | 10
7          | 14

我们基本上将每个父类别都设为其自身的子类别。我们在我们的连接中使用这个结果,然后应该产生:

category_id  |  points_amount  |  category_id  |  points_amount
1            |  785            |  1            |  785
1            |  785            |  2            |  871
1            |  785            |  3            |  236
1            |  785            |  4            |  64
5            |  12             |  5            |  12             
5            |  12             |  8            |  3
5            |  12             |  9            |  48
7            |  897            |  7            |  897            
7            |  897            |  10           |  124
7            |  897            |  14           |  676

在那里,现在父类别被考虑在内SUM,我们现在可以对第一个进行分组category_id,对第二个求和points_amount(此时忽略第一个points_amount,因为它无关紧要),为您提供所需的输出。

于 2012-08-01T12:03:22.283 回答
2

我很确定有一种更简单的方法可以通过加入 category_relations 表来做到这一点,但很难看不到表的任何视觉示例......我认为这应该可行:

SELECT   u.name
        ,pa.user_id
        ,pa.category_id,
        SUM(IF(pa.plus = '1', pa.points_amount, 0)) - SUM(IF(pa.plus = '0',pa.points_amount, 0)) AS points
FROM        points_awarded pa
JOIN        Users u
USING       (user_id)
WHERE       category_id IN(
    SELECT      links_to
    FROM        category_relations
    WHERE       links_from = (
        SELECT      category_id
        FROM        points_awarded
        WHERE       user_id = '" . $user_id . "'
    )
)       
GROUP BY    pa.category_id
ORDER BY    points DESC
LIMIT       50

您在上面发布的示例中没有看到子类别,因为您没有包含 category_relations 表中的记录。我们不是通过查询的 category_id 来查找链接的类别,而是使用子子查询来查找与用户关联的所有 category_id。试一试,如果这不正确,请发布一些显示表格数据的小片段。

于 2012-08-01T08:21:30.730 回答