选择行以及mysql中一个查询的总计数

时间:2012-09-28 18:23:34

标签: php mysql sql select

假设我有一个表t,表t有15000个条目

假设查询

SELECT * FROM t WHERE t.nid <1000

返回1000行

然后我只想要前10行,所以我做了LIMIT

SELECT * FROM t WHERE t.nid <1000 LIMIT 10

是否可以构造单个查询,其中除了使用上面的LIMIT子句返回10行信息外,它还返回满足WHERE子句中设置的条件的行的总数,因此除了返回上面的10行,它也返回1000,因为总共有1000行满足WHERE子句...并且都在单个查询中返回

7 个答案:

答案 0 :(得分:4)

您可以尝试SQL_CALC_FOUND_ROWS,这可以获得总记录数,而无需再次运行该语句。

SELECT SQL_CALC_FOUND_ROWS * FROM t WHERE t.nid <1000 LIMIT 10;   -- get records
SELECT FOUND_ROWS();                -- get count

参考:http://dev.mysql.com/doc/refman/5.0/en/information-functions.html

答案 1 :(得分:4)

<块引用>

“是否可以构造一个查询,其中除了使用上面的 LIMIT 子句返回 10 行信息外,还返回满足 WHERE 子句中设置的条件的行的总数”

是的,通过使用窗口函数,即 COUNT(*) OVER()(MySQL 8.0+):

SELECT t.*, COUNT(*) OVER() AS cnt 
FROM t 
WHERE t.nid <1000 
LIMIT 10;  

db<>fiddle demo


旁注:

LIMIT 没有明确的 ORDER BY 是不确定的。它可能会在多次运行之间返回不同的结果。

答案 2 :(得分:2)

有很多事情需要讨论。

  • 没有 LIMITORDER BY 有点不可预测,因此有点无意义。

  • 但如果您添加 ORDER BY,它可能需要查找所有行,对它们进行排序,然后仅提供您想要的 10 行。

  • 或者,ORDER BY 可以由 INDEX 充分处理。

您的特定查询,如果变成 2 个查询(根据 8.0.17 之后的需要),将是

SELECT * FROM t WHERE t.nid < 1000 LIMIT 10;
SELECT COUNT(*) FROM t WHERE t.nid < 1000;

请注意,这些都将从 INDEX(nid) 中受益。第一个将从索引的 BTree 中选取 10 个项目,然后在数据的 BTree 中查找它们——每个项目仅涉及 10 行。第二个将扫描 INDEX 直到达到 1000,并且不接触数据 BTree。

如果您按照建议添加 ORDER BY,则第一个查询

SELECT * FROM t WHERE t.nid < 1000 ORDER BY t.nid LIMIT 10;

的工作方式与上述相同。但是

SELECT * FROM t WHERE t.nid < 1000 ORDER BY t.abcd LIMIT 10;

需要扫描很多行,而且速度很慢。并且可能使用临时表和文件排序。 (查看 EXPLAIN 了解详细信息。)INDEX(nid, abcd) 会有所帮助,但作用不大。

还有其他变体,例如当索引可以“覆盖”时。

“一个查询”的目标是什么?

  • 速度? -- 如上所述,还有其他更相关的因素。

  • 一致性? -- 您可能需要一个事务来避免,例如,从第一个查询中获取 N 行,而从 COUNT 中获取更少的行数。

     BEGIN;
     SELECT * ...
     SELECT COUNT(*) ...
     COMMIT;
    
  • 单个命令? -- 考虑一个组合了 2 个语句的存储过程。或者

     SELECT * FROM t WHERE t.nid < 1000 LIMIT 10
     UNION ALL
     SELECT COUNT(*) FROM t WHERE t.nid < 1000;
    

但这会变得棘手,因为列数不同,因此需要一些杂项才能使第二个查询具有相同的列数。另一种变体涉及GROUP BY WITH ROLLUP。 (但它可能更难制造。)

Lukasz 的回答看起来很有希望。但是,它提供了一个额外的列(这可能很好)并且需要测试其性能。如果您使用的是 8.0 并且他们的答案适合您,请接受该答案。

答案 3 :(得分:1)

最快的方法是使用SQL_CALC_FOUND_ROWS和两个查询

SELECT SQL_CALC_FOUND_ROWS * FROM t WHERE t.nid <1000 LIMIT 10
SELECT FOUND_ROWS()

答案 4 :(得分:1)

你可以,但我认为这将是一个性能杀手。

您最好的选择是使用SQL_CALC_FOUND_ROWS子句并发出第二个查询来恢复它。

 SELECT SQL_CALC_FOUND_ROWS * FROM t WHERE t.nid <1000 LIMIT 10;
 SELECT FOUND_ROWS();

参见例如http://www.arraystudio.com/as-workshop/mysql-get-total-number-of-rows-when-using-limit.html

或者您只需运行不带LIMIT子句的查询,并只检索前十行。然后,您可以根据需要使用一个查询,并通过mysql_num_rows()获取行数。

答案 5 :(得分:0)

听起来像你想要FOUND_ROWS()

SELECT SQL_CALC_FOUND_ROWS * FROM t WHERE t.nid <1000 LIMIT 10;
SELECT FOUND_ROWS();

答案 6 :(得分:0)

Count(*) 时间复杂度是 O(1),所以你可以使用子查询

SELECT *, (SELECT COUNT(*) FROM t WHERE t.nid <1000) AS cnt
FROM t 
WHERE t.nid <1000 
LIMIT 10