表中的SQL查询

时间:2011-12-13 09:07:50

标签: mysql security subquery badge

我正在构建一个与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列表。

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

我有两个问题:

  1. 表中的SQL是非常糟糕的方法吗?如果是这样,为什么?我愿意接受替代方案。
  2. 是否有更高效的查询来实现相同的查询?根据我的经验,子查询通常很慢。
  3. 我也很想知道StackExchange代码是如何做到的。

    如果您有任何疑问,请随时提出。

    干杯。

1 个答案:

答案 0 :(得分: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

理论上,您可以为每个返回的结果插入徽章记录。这不是我的头脑,但你明白了。

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