2

我有一个页面可以选择数据库中的所有用户。只有一千或两个。没什么大不了。

但是,在选择它的同时,它还使用该查询中的 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 而抨击我。我还没有时间转换它。我知道我是一个可怕的人。

4

2 回答 2

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告诉数据库通过查找用户表和日志表相同的记录来匹配用户和日志uidLEFTinLEFT JOIN告诉数据库为用户返回一个结果(连接的左侧,即使他们没有与他们关联的任何日志(连接的右侧)。如果您不想在没有用户日志的情况下看到结果,您可以执行INNER JOIN,这将只显示联接两边都匹配的结果(用户和至少一个日志消息)。

通过...分组

有必要按用户 ID 对结果进行分组 - 否则您只会获得与任何GROUP BY用户关联的日志消息的总数,这可能没有帮助,因为您只能.SELECT COUNT(*) FROM logs

我使用表别名来缩短查询,因为它是我一直使用的样式,但您可以轻松地输入表的全名(logs.uid等)。您甚至可以不包括表名而逃脱,但是当您在查询中引用存在于多个表中的列时,您的数据库会感到困惑,因此我发现始终明确说明您的列是最简单的再谈。

索引

除非您有一个非常大的数据库,否则这个新查询应该会立即完成。如果没有,请接受@charly 的建议并尝试一些索引。不幸的是,您l.time + l.date在使用该值之前添加,我认为 MySQL 不会让您在 上创建索引l.time + l.date,但您可以通过l.date首先过滤(可索引)获得不错的结果:

ON l.uid = u.uid AND l.date > $timeNow AND l.time + l.date > $timeNow

这看起来很重复,但它为数据库提供了更多可使用的功能,因为它可以:

  1. 使用 index获取结果 where l.dateis 。$timeNow
  2. 用 过滤那组(希望很小的)结果l.time + l.date > $timeNow

代替:

  1. 对于表中的每条记录,添加l.time + l.date.
  2. 检查该结果是否在$timeNow

PHP

要在 PHP 中执行此操作,您需要执行以下操作:

$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查询版本进行一些优化,并且知道任何不在其中的用户$counts的计数为 0。

对不起,如果我的语法错误,但我认为这说明了这个想法。

安全说明

一个小切线:看起来您将变量直接放入查询中,这通常是一个坏主意。有许多非常复杂的解决方案,但最简单的方法是只使用参数化查询,并且永远不要将变量直接放入 SQL 中。

于 2012-10-03T20:57:05.280 回答
0

我真的不确定,但可能会在 uid 列上添加 BTREE 索引。然后您的查询将更加有效,因为它不会扫描所有不属于指定 uid 的日志。

虽然我不是 100% 确定

于 2012-10-03T20:58:51.180 回答