1

我正在构建一个查询,该查询用于Items根据 selected生成索引页面,通过计算过去 24 小时内将项目添加到 a 的次数和次数,Category按相对受欢迎程度排序。查询的单个输入是主要类别 ID。LikesList

这涉及到总共 4 个表,其中一个是嵌套集,所以它并不完全是微不足道的。我通常非常擅长编写相当有效的 SQL,但我很难让 JOIN 以我想要的方式工作。

类别

由于类别是嵌套的并且项目被分配给单个类别,因此有必要首先选择查询输入中位于指定类别之下的所有类别。

我正在使用awesome_nested_set gem 来完成这项工作。它添加了lftrgt列,可用于从层次结构中毫无困难地进行选择:

SELECT c2.*
FROM categories c1
JOIN categories c2
    ON c2.lft >= c1.lft AND c2.rgt <= c1.rgt
WHERE c1.id = [MAIN CATEGORY ID]

项目

然后扩展上述内容以选择项目非常简单:

SELECT i.*
FROM categories c1
JOIN categories c2
    ON c2.lft >= c1.lft AND c2.rgt <= c1.rgt
JOIN items i
    ON i.category_id = c2.id
WHERE c1.id = [MAIN CATEGORY ID]

到目前为止,一切工作正常且执行迅速。最后要做的事情(当然忽略分页)是订购它们。

人气

项目按受欢迎程度排序。计算一个项目的受欢迎程度的方法是:

(number of likes) + (number of times added to list) * 5

例如,如果一个项目已被添加到 32 个列表并被点赞 483 次,那么受欢迎度指标将为 643。

根据用户是查看“所有时间最受欢迎”还是“趋势”,我们可能会将这些指标的计算限制为过去一天发生的喜欢/列表。

我认为这将是相对微不足道的,但最终并非如此。COUNT当您与 s 一起使用时显然会出现问题,JOIN我需要使用 LEFT JOIN 以防项目有 0 个喜欢/列表。

当前工作代码如下:

SELECT
    q.*,
    (q.likes + q.lists * 5) AS popularity
FROM
(
    SELECT
        i.*,
        (SELECT COUNT(*) FROM likes l WHERE i.id = l.item_id AND l.created_at > DATE_SUB(NOW(), INTERVAL 1 day)) AS likes,
        (SELECT COUNT(*) FROM list_items li WHERE i.id = li.item_id AND li.created_at > DATE_SUB(NOW(), INTERVAL 1 day)) AS lists
    FROM categories c1
    JOIN categories c2
        ON c2.lft >= c1.lft AND c2.rgt <= c1.rgt
    JOIN items i
        ON i.category_id = c2.id
    WHERE c1.id = 37
) q
ORDER BY popularity

然而,这显然是非常可怕的代码。每个项目都需要进行两个子查询,然后整个事情都需要被包装以进行一些算术运算(尽管我认为这还不错)。

我尝试了以下事情,但由于各种原因它们没有奏效:

SELECT
    i.*,
    (SELECT COUNT(*) FROM likes l WHERE i.id = l.item_id AND l.created_at > DATE_SUB(NOW(), INTERVAL 1 day)) AS likes,
    (SELECT COUNT(*) FROM list_items li WHERE i.id = li.item_id AND li.created_at > DATE_SUB(NOW(), INTERVAL 1 day)) AS lists,
    (likes + lists * 5) AS popularity

出于某种原因,您无法对您选择的其他列进行数学运算。

SELECT
    i.*,
    COUNT(l.id) as likes,
    COUNT(li.id) as lists
FROM categories c1
JOIN categories c2
    ON c2.lft >= c1.lft AND c2.rgt <= c1.rgt
JOIN items i
    ON i.category_id = c2.id
LEFT JOIN likes l
    ON l.item_id = i.id
LEFT JOIN list_items li
    ON li.item_id = i.id
WHERE c1.id = 37

由于某种原因,您只能得到一个结果。我不明白这是什么原因。

SELECT
    i.*,
    COUNT(l.id) as likes,
    COUNT(li.id) as lists
FROM categories c1
JOIN categories c2
    ON c2.lft >= c1.lft AND c2.rgt <= c1.rgt
JOIN items i
    ON i.category_id = c2.id
LEFT JOIN likes l
    ON l.item_id = i.id
LEFT JOIN list_items li
    ON li.item_id = i.id
WHERE c1.id = 37
GROUP BY i.id

添加GROUP BY使所有项目返回,但喜欢/列表的数字现在完全错误。我认为这是将它们加起来之类的。

基本上,我有点卡住了。上面带有子查询的示例确实有效,但我认为它的工作方式并不理想。我想让它只与JOINs 一起工作,但我很难理解如何。

任何帮助深表感谢 :)

4

1 回答 1

2

执行按 item_id 分组的子查询以获取计数,并针对这些子查询进行 LEFT JOIN。

像这样的东西: -

SELECT
    q.*,
    (q.likes + q.lists * 5) AS popularity
FROM
(
    SELECT
        i.*,
        IFNULL(likes_count, 0) AS likes,
        IFNULL(lists_count, 0) AS lists
    FROM categories c1
    JOIN categories c2
        ON c2.lft >= c1.lft AND c2.rgt <= c1.rgt
    JOIN items i
        ON i.category_id = c2.id
    LEFT OUTER JOIN
    (
        SELECT item_id, COUNT(*) AS likes_count FROM likes WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 day) GROUP BY item_id
    ) likes
    ON likes.item_id = i.id
    LEFT OUTER JOIN
    (
        SELECT item_id, COUNT(*) AS lists_count FROM list_items li WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 day) GROUP BY item_id
    ) lists
    ON lists.item_id = i.id
    WHERE c1.id = 37
) q
ORDER BY popularity
于 2013-07-23T12:13:35.063 回答