1

我正在构建一个与 StackExchange 的徽章工作方式非常相似的徽章系统。为了查询个人徽章资格,似乎最佳解决方案是将 SQL 查询放在表中,以便它可以作为 cron 作业的一部分执行。

这是我正在考虑使用的查询样式:

SELECT u.user_id
FROM users u
WHERE (
    SELECT COUNT(*)
    FROM comments c
    WHERE c.user_id = u.user_id
) > 0
AND [$badge_id] NOT IN (
    SELECT b.badge_id
    FROM user_badges b
    WHERE b.user_id = u.user_id
)

该表最终将如下所示:

badge_id |    name         |  description  | level |     hash     | query
---------+-----------------+---------------+-------+-----------------------------------------------------------------------------------------------------------------
1        | Hello World     | First comment |   1   | 50a7c570f... | u.user_id FROM users u WHERE ( SELECT COUNT(*) FROM comments c WHERE c.user_id = u.user_id ) > 0
2        | Into the Breach | First thread  |   1   | 6b01e9348... | u.user_id FROM users u WHERE ( SELECT COUNT(*) FROM threads t WHERE t.user_id = u.user_id ) > 0

查询从数据库中获取,前缀为SELECT,然后AND [$badge_id] NOT IN (...)附加子查询条件。然后它会替换[$badge_id]为当前的徽章 ID。查询被执行并返回应该被赋予徽章的用户 ID 列表。

哈希字段用于存储查询的 HMAC-SHA1 哈希,使用 PHP 脚本中定义的键。这背后的想法是,即使发生 SQL 注入攻击,攻击者也无法访问脚本中的密钥,也无法伪造更危险的查询。

我有两个问题:

  1. 表中的 SQL 真的是一个非常糟糕的方法吗?如果是这样,为什么?我对替代品持开放态度。
  2. 是否有更高效的查询来实现相同的查询?根据我的经验,子查询通常很慢。

我也很想知道 StackExchange 代码是如何做到的。

如果您有任何问题随时问。

干杯。

4

1 回答 1

0

这听起来是个坏主意。如果你改变了你的架构怎么办?现在你必须去使表中的每一行都无效。如果您愿意,我可能会使用触发器来记录“触发”徽章的操作。然后,一个 cron 脚本可以来授予徽章。

在您的情况下,判断某人是否需要获得评论徽章是一个非常简单的查询,因此我将直接从 CRON 处理:

SELECT * FROM comments
LEFT JOIN users_badges ON users_badges.user_id = comments.user_id AND users_badges.description = 'First Comment'
WHERE users_badges.badge_id IS NULL
GROUP BY comments.user_id

理论上,您可以为每个返回的结果插入一个标记记录。这不是我的想法,但你明白了。

对于更复杂的查询,我仍然可能会插入某种活动表以简化事情。这也有助于将您的实现分开一点——如果将来徽章的“描述”发生变化,您会怎么做?规范化将有助于这种事情。

于 2011-12-13T09:27:12.060 回答