我正在构建一个与 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 注入攻击,攻击者也无法访问脚本中的密钥,也无法伪造更危险的查询。
我有两个问题:
- 表中的 SQL 真的是一个非常糟糕的方法吗?如果是这样,为什么?我对替代品持开放态度。
- 是否有更高效的查询来实现相同的查询?根据我的经验,子查询通常很慢。
我也很想知道 StackExchange 代码是如何做到的。
如果您有任何问题随时问。
干杯。