【问题标题】:How to count the number of rows when using SQL joins and group by使用SQL连接和分组时如何计算行数
【发布时间】:2011-09-28 01:23:02
【问题描述】:

我有以下疑问:

SELECT a.HotelID,a.Hotelname,GROUP_CONCAT(DISTINCT b.OperatorName) AS Operators
FROM hotels AS a
INNER JOIN operators AS b
ON a.HotelID = b.HotelID
GROUP BY a.HotelID
ORDER BY a.HotelID
LIMIT 100

我需要这个查询来实现简单的搜索功能。结果表应包含分页。所以我所做的是我运行这个查询(没有 LIMIT)来获取行数(我需要计算页面等等),然后我用 LIMIT 重新运行那个查询。

事实上,查询本身需要 4-5 秒(针对 300k 表,所有字段都有索引),这意味着它目前需要 10 秒来加载,因为它运行了两次。

我想知道是否有一个 SQL 语句我可以简单地使用来获取行数并且可能更快。我以为我可以使用 COUNT(a.HotelID) 但这不起作用。

【问题讨论】:

    标签: php mysql


    【解决方案1】:

    试一试:

    SELECT *
    FROM (
        SELECT a.HotelID,a.Hotelname,GROUP_CONCAT(DISTINCT b.OperatorName) AS Operators, COUNT(a.HotelID) AS total
        FROM hotels AS a
        INNER JOIN operators AS b
        ON a.HotelID = b.HotelID
        GROUP BY a.HotelID
        ) AS a
    ORDER BY a.HotelID
    LIMIT 100
    

    另外,为了速度,您应该确保您的索引是有序的。

    【讨论】:

    • 你能解释一下我如何检查索引是否有序,如果不是如何纠正这个问题?
    • 我对数据库索引的了解非常有限,建议你在网上查一下,这个databasejournal.com/features/mysql/article.php/1382791/…似乎是一个比较简单的解释。我猜你会想要同时索引酒店和运营商的 hotelID。
    • 我已经在两个表中的所有字段上都有索引。还尝试选择不同的索引类型(目前我使用 BTREE,但我也测试了哈希)
    【解决方案2】:

    更新

    select count(*) from (
    SELECT distinct b.HotelID
    FROM hotels AS a
    INNER JOIN operators AS b
    ON a.HotelID = b.HotelID    
    )
    

    这样可以更快吗?

    【讨论】:

    • 是的,但它需要大约 4 秒。这是正常的还是可以更快?
    • 不,它并不快。还是 4 秒
    • 好吧,我犯了一个错误,我用 mac 客户端测试过。似乎这里的连接不是最好的。在服务器本身上,我得到以下信息(使用 group by 等):1.64sec,没有 group by ... 0.46sec。在 Webapp (UI) 中感觉还是 10 秒
    【解决方案3】:

    the manual中有明确描述:

    SQL_CALC_FOUND_ROWS 告诉 MySQL 计算有多少行 在结果集中,忽略任何LIMIT 子句。的数量 然后可以使用SELECT FOUND_ROWS() 检索行。见章节 11.13,“信息功能”。

    如果您点击第 11.13 节的链接,则有一个示例:

    FOUND_ROWS()

    SELECT 语句可能包含LIMIT 子句来限制服务器返回给客户端的行数。在某些情况下,最好知道在没有LIMIT 的情况下该语句将返回多少行,但无需再次运行该语句。要获取此行数,请在SELECT 语句中包含SQL_CALC_FOUND_ROWS 选项,然后调用FOUND_ROWS()

    mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name
        -> WHERE id > 100 LIMIT 10;
    mysql> SELECT FOUND_ROWS();
    

    第二个SELECT 返回一个数字,表示如果没有LIMIT 子句,第一个SELECT 将返回多少行。

    如果最近成功的 SELECT 语句中没有 SQL_CALC_FOUND_ROWS 选项,FOUND_ROWS() 返回该语句返回的结果集中的行数。如果语句包含LIMIT 子句,则FOUND_ROWS() 返回达到限制的行数。例如,如果语句包含LIMIT 10LIMIT 50, 10FOUND_ROWS() 将分别返回1060

    请使用文档作为您的第一个停靠点。

    【讨论】:

    • 哇。谢谢!是的,我知道,这个周末我打算浏览整个文档,看看还有哪些我不知道的很酷的东西。
    • @WorldSignia:你已经在使用LIMIT,所以如果你想知道它是如何工作的以及它还能做什么,你应该已经阅读了它的文档。正如我所说,第一个停靠港......不是最后一个!
    • 是的,正确。我刚刚还弄清楚了 SQL_CACHE 参数。结果现在很快就出来了:)
    • @WorldSignia:我什至不知道那个缓存。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多