我有一个页面可以选择我数据库中的所有用户。只有一两千个。没什么大不了的。
然而,当它选择它时,它还使用来自该查询的uid来检查另一个包含大约25,000个条目的表。
SELECT COUNT(id)
FROM logs
WHERE time+date > {$timeNow} AND uid={$row['id']}
为每个用户条目执行 。可以想象,这会占用大量资源。
上面的WHERE
条款仅适用于最后一天的条目,最多可能是500-1000。但是,它可以产生更多的影响。
我在想我可以设置一个cronjob,将每天一次或两次与WHERE
子句不匹配的所有条目导出到另一个表。我知道这会以一种有效的方式帮助甚至解决问题。但是,我真的不喜欢有两个表用于相同(相对)目的。
我有更好的方法吗?我现在好好搜索一下,我找不到任何东西,但我想我会问你们,以防你遇到同样的问题并找到一种独特的方法来解决它。
编辑对于Brendan Long: 我的新疑问:
$SQL = "SELECT u.id, COUNT(l.id) " .
"FROM users u " .
"INNER JOIN logs l " .
"ON l.uid = u.id " .
"WHERE l.time+l.date > {$timeNow} " .
"GROUP BY u.id";
另外,请不要因为缺乏PDO而抨击我。我没有时间把它转换过来。我知道我是一个可怕的人。
答案 0 :(得分:4)
使用JOIN,以便数据库可以作为一个查询为您优化它:
SELECT u.uid, COUNT(l.id)
FROM Users u -- or whatever your users table is named
LEFT JOIN logs l
ON l.uid = u.uid AND l.time + l.date > $timeNow
GROUP BY u.uid
在英语中,这告诉数据库“给我一个用户ID列表和与之关联的日志数量,其中time + date
在$timeNow
之后”。这显然更有效率,因为您一次为数据库提供所有工作,因此它可以找出获取所有信息的最佳方式,而不是一次抓取一个
LEFT JOIN
通过查找users表和logs表具有相同uid
的记录,告诉数据库将用户与日志进行匹配。 LEFT
中的LEFT JOIN
告诉数据库返回用户的结果(连接的左侧侧),即使它们没有与之关联的任何日志(连接的右侧)。如果您不希望看到没有用户日志的结果,您可以执行INNER JOIN
,这只会显示联接两侧匹配的结果(用户和至少一个)记录消息)。
GROUP BY
是按用户ID对结果进行分组所必需的 - 否则您只需获取与任何用户相关联的日志消息总数,这可能是没有用的你可以SELECT COUNT(*) FROM logs
。
我正在使用表别名来缩短查询次数,因为它是我一直使用的样式,但您可以轻松地放置表格的全名(logs.uid
等)。您甚至可以在不包括表名的情况下逃脱,但是当您引用查询中多个表中存在的列时,您的数据库会变得混乱,因此我发现最简单的是始终明确您的哪个列再谈。
除非你有一个庞大的数据库,否则这个新查询应该立即完成。如果没有,请参考@ charly的建议并尝试一些索引。不幸的是,在使用该值之前添加l.time + l.date
,我认为MySQL不会让你在l.time + l.date
上创建索引,但是你可以通过过滤{{1}来获得不错的结果} first(可索引):
l.date
这看起来很重复,但它使数据库更易于使用,因为它可以:
ON l.uid = u.uid AND l.date > $timeNow AND l.time + l.date > $timeNow
之后l.date
的结果。$timeNow
过滤掉(希望很小)一组结果。而不是:
l.time + l.date > $timeNow
。l.time + l.date
要在PHP中执行此操作,您需要执行以下操作:
$timeNow
或者,如果您需要以更复杂的方式使用它,请提前获取所有内容:
$sql = // that query above
$result = mysql_query($sql);
while($row = mysql_fetch_array($result)) {
echo "User " . $row[0] . " posted " . $row[1] . " times.";
}
如果你这样做“先取出所有”的方式,你也可以使用$counts = array();
$sql = // that query above
$result = mysql_query($sql);
while($row = mysql_fetch_array($result)) {
$counts[$row[0]] = $row[1];
}
// later
$user = 5; // some user we care about
echo "User " . $user . " posted " . $counts[$user] . " times.";
版本的查询进行优化,知道任何不在INNER JOIN
的用户都有计数0。
很抱歉,如果我的语法错误,但我认为这表明了这个想法。
在次要切线上:看起来您将变量直接放入查询中,即generally a bad idea。有一些incredibly complicated solutions,但最简单的只是use parametrized queries,并且永远不会将变量直接放入SQL中。
答案 1 :(得分:0)
我真的不确定,但可能在uid列上添加BTREE索引。然后,您的查询将更加高效,因为它不会扫描所有不属于指定uid的日志。
虽然我不是百分百肯定