mySQL查询分组记录的前N个条目

时间:2010-12-11 18:05:06

标签: mysql greatest-n-per-group

我是MySql和一般数据库的新手。我有一个查询,我通过在线资源和跟踪和错误的片段一起构建。它真的很慢(27秒),我认为它可以优化。也许有人可以帮我解决这个问题。

这是mySQL的数据结构 - 数据库。版本5.1.51-0

|- purchaseID -|- customerID -|- emotionID -|- customerCountryCode -|- customerContinentCode-|
|     1        |     2345     |     0       |        US             |            NA          |
|     2        |     2345     |     3       |        US             |            NA          |
|     3        |     4456     |     0       |        UK             |            EU          |
|     3        |     4456     |     5       |        UK             |            EU          |
|     4        |     4456     |     2       |        UK             |            EU          |
|     5        |     4456     |     2       |        UK             |            EU          |
|     6        |     1234     |     0       |        US             |            NA          |
|     7        |     6678     |     0       |        US             |            NA          |
|     8        |     9900     |     0       |        US             |            NA          |
|     9        |     3334     |     0       |        US             |            NA          |    
|     10       |     3334     |     4       |        US             |            NA          |

数据库用于保存所有已完成的购买。每次购买customerID,他来自的国家和大陆都会被保存。顾客还可以从一组6种情绪中评价他的购买情况。 (快乐,失望,......)他选择的情绪保存为emotionID

所以现在我需要一个查询来获取具有百分比信息的特定emotionID的前六名客户。假设我找了emotionID = 0这就是我想得到的:

|- customerID -|- emotionPercent -|
|     1234     |        100       |     
|     6678     |        100       |     
|     9900     |        100       | 
|     2345     |        50        |     
|     3334     |        50        | 
|     4456     |        25        |    

我正在使用此查询:

SELECT customers.customerID, Count( customers.emotionID ) / C.totalPeople * 100.0 AS emotionPercent 
FROM `customers` 
INNER JOIN 

    (SELECT customers.customerID, Count( customers.emotionID ) AS totalPeople
    FROM `customers` 
    GROUP BY customerID) C 

ON customers.customerID = C.customerID 
WHERE customers.emotionID = 0 
GROUP BY customers.customerID 
ORDER BY emotionPercent DESC 
LIMIT 0,6

我已经搜索了答案,但额外的百分比计算让我失望。我找到了一些解决方案,需要填充某种临时表,但我无法使用它。

问题是: 目前,数据库中有140,000个条目,此查询大约需要27秒。这可能是对的吗?使用SQL-Server会显着提高速度吗?

我没有得到的是:要求世界上最幸福的国家快速闪电(0.4秒),但结构上与第一个查询类似(27秒):

SELECT customers.customerCountryCode, Count( customers.emotionID ) / C.totalPeople * 100.0 AS emotionPercent 
FROM `customers` 
INNER JOIN 

    (SELECT customers.customerCountryCode, Count( customers.emotionID ) AS totalPeople
    FROM `customers` 
    GROUP BY customerCountryCode) C 

ON customers.customerCountryCode = C.customerCountryCode 
WHERE customers.emotionID = 0 
GROUP BY customers.customerCountryCode 
ORDER BY emotionPercent DESC 
LIMIT 0,6

当我将此示例中的GROUP BY的{​​{1}}更改为INNER Query时,查询也会永久保留。因此customerID的分组导致了问题。但为什么呢?

customerID定义为customerCountryCodevarchar(2)customerID。这是否会导致查询性能的巨大差异?是否有更合适的变量类型? int(11)最多可包含8个数字。

很多问题!感谢阅读和任何帮助!

3 个答案:

答案 0 :(得分:0)

首先,如果您认为数据库中的条目会膨胀,或者您的条目很高且服务器速度很慢,恕我直言,您可能需要预处理数据并将其存储到另一个数据库中总结结果,这样,您就不必一遍又一遍地请求相同的过程。另外,尝试为您的应用使用缓存插件。用于php的memcache或j2ee上的ehcache将是安全的投注。

答案 1 :(得分:0)

您的问题可能是您正在使用子查询。由于子查询不使用也不设置索引,因此它们使用最慢的连接方法(即全表扫描)。我没有足够的经验来提供仅限SQL的解决方案,因此我建议您将查询分解为两个单独的调用。

  1. 获取每位客户的平均情绪并选择前6位,保存为哈希或对象。
  2. 通过WHERE custumerID IN (id1, id2, id3, etc)
  3. 获取这6位客户

    虽然这可能不是最漂亮的解决方案,但您可以避免使用无索引子查询(以及非常慢的全表扫描)。

答案 2 :(得分:0)

感谢您的帮助!

来自mySQL论坛的人建议添加一些索引:

ALTER TABLE customers
  ADD KEY idx_country_emid (customerCountryCode, emotionID),
  ADD KEY idx_emid_custid (emotionID, customerID);

查询时间从27秒减少到0.1秒。 ;)

此外,对于内部查询,您可以编写

(SELECT customers.customerCountryCode, Count( * ) AS totalPeople
    FROM `customers` 
    GROUP BY customerCountryCode) C