MySQL慢查询优化国家百分比查​​询

时间:2016-02-23 14:29:21

标签: mysql

此查询需要100秒才能运行。我已将每个用于条件或连接的列编入索引,但运行时间太长。如何以有效运行的方式编写此查询?

SELECT e.earning_country, c.country_name, COUNT(e.earning_id) AS views, ROUND(100 * COUNT(e.earning_id)/b.total, 2) AS percentage
FROM earnings AS e
CROSS JOIN (
SELECT COUNT(earning_id) AS total
FROM earnings
WHERE earning_paid = 1 AND earning_ad_id = 1 AND earning_referral_id = 0) AS b

INNER JOIN countries as c
ON c.country_id = e.earning_country_id

WHERE earning_paid = 1 AND e.earning_ad_id = 1 AND earning_referral_id = 0
GROUP BY e.earning_country
ORDER BY percentage DESC

EXPLAIN结果:

"id",   "select_type",  "table",        "type",         "possible_keys",                                                                                "key",                                                              "key_len",  "ref",                          "rows", "Extra"
1,      "PRIMARY",      "<derived2>",   "system",       NULL,                                                                                           NULL,                                                               NULL,       NULL,                           1,      "Using temporary; Using filesort"
1,      "PRIMARY",      "e",            "index_merge",  "earning_referral_id_index,earning_country_id_index,earning_paid_index,earning_ad_id_index",    "earning_referral_id_index,earning_paid_index,earning_ad_id_index", "4,1,4",    NULL,                           362698, "Using intersect(earning_referral_id_index,earning_paid_index,earning_ad_id_index); Using where"
1,      "PRIMARY",      "c",            "eq_ref",       "PRIMARY",                                                                                      "PRIMARY",                                                          4,          "site.e.earning_country_id",    1,      NULL
2,      "DERIVED",      "earnings",     "index_merge",  "earning_referral_id_index,earning_paid_index,earning_ad_id_index",                             "earning_referral_id_index,earning_paid_index,earning_ad_id_index", "4,1,4",    NULL,                           362698, "Using intersect(earning_referral_id_index,earning_paid_index,earning_ad_id_index); Using where; Using index"

2 个答案:

答案 0 :(得分:2)

这不是一个真正的答案,但请尝试运行以下查询,以了解您以简单的方式处理此数据的速度:

SELECT
  e.earning_country
  ,c.country_name
--  ,COUNT(e.earning_id) AS views
FROM earnings AS e
INNER JOIN countries as c
        ON c.country_id = e.earning_country_id
WHERE earning_paid = 1 AND e.earning_ad_id = 1 AND earning_referral_id = 0
GROUP BY e.earning_country_id
;

尝试使用views行注释进行运行,并查看性能差异,注意:我注意到您在原始查询中按earning_country而不是earning_country_id进行分组。

PS - 如果此查询运行得更快,您可以在内存中完成剩余的计算以获得总计,百分比并对其进行排序。

如果您想了解三向索引的大小,请尝试运行查询:

SELECT 
  COUNT(DISTINCT earning_paid, earning_ad_id, earning_referral_id)
FROM earnings;

索引大小应基于数据的可变性,而不是表的大小。

如果earning_id永远不会NULL(并且主键不应该是),那么您可以使用COUNT(*)代替COUNT(earning_id)来提升效果。

答案 1 :(得分:1)

MySQL每个表只使用1个索引。所以你有4列的索引用于where子句和连接只会使用其中一个索引。 MySQL会选择它认为最好的索引,但这可能远非完美。

使用你的查询我怀疑earning_paid是一个标志,所以它本身可能对索引几乎没用(平均一半的记录会有每个值)。使用earning_ad_id和earning_referral_id,您似乎正在检查0,这是我假设每个的默认值,并且每个可能再次覆盖大量行。将这3个组合在一起可能确实有一些用作索引。

earning_country可能作为聚合函数的索引很有用,但不会帮助缩小行数。

如果您的索引涵盖所有4列,则可以使用

设置一个覆盖earning_paid,earning_ad_id,earning_referral_id和earning_country(按此顺序)的索引。

修改

小解释

说你有电话簿。要查找名称,这是按姓氏排序的(实际上是一个索引)。滚动浏览直到找到所需的名称,按名称顺序这很容易。

如果你想找一个名叫史密斯的人,你可以快速跳到那里。

如果您知道他们的名字,那么您可以在史密斯列表中轻松找到它。所以可以找到约翰史密斯(无疑很多)。

但是,如果你想找一个叫史密斯的医生而且你不知道他们的名字你就可以在姓氏和头衔上找到一个索引。如果它是一个罕见的姓氏和一个共同的头衔,最好是姓氏第一和第二名,如果姓氏很常见且标题很少,那么最好将头衔和姓氏列为第二名。

在这种情况下,索引只是每个姓氏和标题的列表,并带有指向记录其余部分的指针。

如果你想要一个名为史密斯的所有医生的计数,那么你只需查看索引就可以得到这个,而无需查看记录的其余部分。