如何告诉MySQL只查询查询期间的最新x条目?

时间:2012-10-03 20:52:47

标签: mysql sql

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

然而,当它选择它时,它还使用来自该查询的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而抨击我。我没有时间把它转换过来。我知道我是一个可怕的人。

2 个答案:

答案 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

这看起来很重复,但它使数据库更易于使用,因为它可以:

  1. 使用索引获取ON l.uid = u.uid AND l.date > $timeNow AND l.time + l.date > $timeNow 之后l.date 的结果。
  2. 使用$timeNow过滤掉(希望很小)一组结果。
  3. 而不是:

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

      PHP

      要在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的日志。

虽然我不是百分百肯定