【问题标题】:Two seemingly identical queries returning different results when using count (pagination)使用计数(分页)时,两个看似相同的查询返回不同的结果
【发布时间】:2012-07-01 04:31:47
【问题描述】:

出于分页原因,我正在对我的数据库运行两个查询。因此,每个查询几乎是相同的。我的 COUNT(*) 查询没有返回非计数查询的结果数。我很困惑为什么会这样。查询如下。

SELECT p.host_id, p.rating_support, p.rating_tech, MAX(p.rating_overall) AS rating_overall, p.publish_rating, h.name, prices.price, prices.term_duration
FROM plans p
INNER JOIN hosts AS h ON h.id = p.host_id
INNER JOIN (SELECT plan_id, price, term_duration FROM prices WHERE price > 0 AND price < 50 AND term_duration = 1) prices ON prices.plan_id = p.id
WHERE p.published = 1 AND h.published = 1
GROUP BY p.host_id
ORDER BY rating_overall desc LIMIT 0, 12

SELECT COUNT(*) AS count
FROM plans p
INNER JOIN hosts AS h ON h.id = p.host_id
INNER JOIN (SELECT plan_id, price, term_duration FROM prices WHERE price > 0 AND price < 50 AND term_duration = 1) prices ON prices.plan_id = p.id
WHERE p.published = 1 AND h.published = 1
GROUP BY p.host_id

我不是 MySQL 专家。除了计数没有提供正确数量的结果之外,非计数查询也能完美运行。

任何关于这个问题的观点都会很棒。

【问题讨论】:

  • 我看到你的第一个查询有限制,但第二个没有。这可能会限制您想要考虑的结果。
  • 您好@Andrew,是的,由于分页原因,第一个查询必须限制结果。例如,第一页将显示多达 12 个结果。第二页将显示另外 12 个结果,使用限制进行偏移。第二个查询不应该受到限制,因为我们需要知道结果的全部数量来确定我们将拥有多少页。我的问题是,第一个查询返回 6 个结果,第二个查询 COUNT(*) 返回 3。这怎么会发生?
  • 我怀疑是 MAX 不在计数查询中。这将消除计数没有的记录。
  • @AdrianCornish - 这一点都不奇怪。 SELECT COUNT(*) FROM table GROUP BY x 将为每个不同的 x 值返回一行,并报告为每个不同的 x 值聚合了多少行数据。就像SELECT x, COUNT(*) FROM table GROUP BY x。因此,如果第一个查询给出 6 行,则第二个查询也应该给出 6 行,结果是每个 p.host_id 的不同值聚合了多少行数据。
  • @Akaishen - 您的COUNT(*) 查询有GROUP BY p.host_id。这意味着return one row per distinct value of p.host id。这就是 both 查询返回 6 行的原因。此外,COUNT(*) 告诉您(通过 GROUP BY) 聚合了多少行以形成该一个输出行。 COUNT(distinct p.host_id) 而是告诉您在该输出行中存在多少 p.host_id 的不同值。所以,要么使用COUNT(distinct p.host_id删除GROUP BY,或者按照@biziclop的建议去做。使用嵌套查询计算结果中的行数,而不是计算输入中的行数

标签: php mysql sql


【解决方案1】:

在 Dems 的评论 (hunt down and upvote him 某处 :) 的帮助下,我创建了这个查询。请注意,我删除了子查询,因为它似乎没有必要:

SELECT
  COUNT( DISTINCT p.host_id )
FROM       plans p
INNER JOIN hosts h ON h.id = p.host_id
INNER JOIN prices  ON prices.plan_id = p.id
                  AND prices.price > 0
                  AND prices.price < 50
                  AND prices.term_duration = 1
WHERE p.published = 1
  AND h.published = 1

我原来的答案:

要获得总行数,您必须将GROUP BY 查询包装到外部SELECT

SELECT COUNT(*)
FROM (
  SELECT NULL -- we are just counting, so we need no actual data -> a bit faster
  FROM       plans p
  INNER JOIN hosts h ON h.id = p.host_id
  INNER JOIN prices  ON prices.plan_id = p.id
                    AND prices.price > 0
                    AND prices.price < 50
                    AND prices.term_duration = 1
  WHERE p.published = 1
    AND h.published = 1
  GROUP BY p.host_id
) AS all_rows_without_data

或者你可以使用SQL_CALC_FOUND_ROWS + FOUND_ROWS()

http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_found-rows

一个 SELECT 语句可能包含一个 LIMIT 子句来限制数量 服务器返回给客户端的行数。在某些情况下,它是 想知道语句将返回多少行 没有 LIMIT,但没有再次运行该语句。获得 此行数,在 SELECT 中包含 SQL_CALC_FOUND_ROWS 选项 语句,然后调用 FOUND_ROWS() :

首先,只需选择所需的行,但添加SQL_CALC_FOUND_ROWS

SELECT SQL_CALC_FOUND_ROWS
  p.host_id, p.rating_support, p.rating_tech,
  MAX(p.rating_overall) AS rating_overall,
  p.publish_rating, h.name, prices.price, prices.term_duration
FROM       plans p
INNER JOIN hosts AS h ON h.id = p.host_id
INNER JOIN prices  ON prices.plan_id = p.id
                  AND prices.price > 0
                  AND prices.price < 50
                  AND prices.term_duration = 1
WHERE p.published = 1 AND h.published = 1
GROUP BY p.host_id
ORDER BY rating_overall desc
LIMIT 0, 12;

其次,获取第一个查询中没有LIMIT语句时会返回的行数:

SELECT FOUND_ROWS();

更新SQL_CALC_FOUND_ROWS + FOUND_ROWS() 似乎不太可靠,总是因为未知原因返回零(不仅仅是我:FOUND_ROWS() keeps returning 0):

http://sqlfiddle.com/#!2/7304d/8

【讨论】:

  • 或者...由于原始查询是SELECT z, a, b, c FROM y GROUP BY z,因此可以将其更改为SELECT COUNT(distinct z) FROM y,根本不需要GROUP BY。
  • 谢谢,真的!如果您从中创建答案,我将删除我自己的答案。
  • 我的是另一种选择,而你的是一种有效的答案。我不想让你删除它,所以我不会发布我的答案。但我很乐意您将其 添加 到您自己的 :) [使用您的版本的一个原因是用于测试 - 您对查询所做的更改越少越好.我的建议可能会更快一点,更简洁一些,但是您的建议可以确保原始查询已经过测试,而不是类似的查询;和类似的手段引入人为错误。]
  • 感谢大家清楚地解释发生了什么。我从您的帖子中学到了很多东西,并将使用 COUNT(DISTINCT p.host_id) 来获取正确的行数。我已经阅读了 SQL_CALC_FOUND_ROWS 并确定它不够可靠,无法使用。尽管如此,我还是选择了这个答案,因为它是最完整的,为解决方案提供了最多的选项。感谢大家的帮助!
【解决方案2】:

第二个查询的结果将返回相同数量的行,但第一行不会返回总数。

结果将给出每行每组的计数:

3
5
1
6
etc.
etc.

要将结果放入一行,请在 PHP 中对完整结果集使用 COUNT(DISTINCT p.host_id)array_sum()

【讨论】:

  • 肯定是 - 不是 - just 删除 GROUP。在您的示例中,这将返回 3+5+1+6+etc+etc。如果 OP 放弃了 GROUP BY,则 OP 必须 也使用 COUNT(distinct p.host_id)。请参阅上面的我的 cmets。
  • 感谢@Jack 的帖子。这是我一直在寻找的答案,但包含在 biziclop 的答案中。不过,我会给你一个赞成票。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多