假设我有一个表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子句...并且都在单个查询中返回
答案 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;
旁注:
LIMIT
没有明确的 ORDER BY
是不确定的。它可能会在多次运行之间返回不同的结果。
答案 2 :(得分:2)
有很多事情需要讨论。
没有 LIMIT
的 ORDER 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