优化(MySQL)SELECT .. GROUP BY性能

时间:2012-03-27 09:10:48

标签: mysql sql group-by

我有一个相当静态的(InnoDB)表T,其中有四列:ABCD

我首先希望确定A的给定值,B在所有记录中产生唯一C的值。我的尝试如下:

CREATE PROCEDURE P(x int) BEGIN
    SELECT   B
    FROM     T
    WHERE    A = x
    GROUP BY B
    HAVING   COUNT(DISTINCT C) = COUNT(C);
END

但是,尽管列GROUP BY上有索引,但引入B会大大降低此查询的性能。有没有更有效的方法,或者我可以以某种方式提高此查询的性能?


为了回应下面的Daan评论,该表格创建时带有以下内容:

CREATE TABLE T (
    A int(11) NOT NULL,
    B varchar(45) NOT NULL,
    C varchar(255) DEFAULT NULL,
    D int(11) NOT NULL,
    PRIMARY KEY (A,B,D),
    KEY iA (A),
    KEY iB (B),
    KEY iC (C)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

在回应下面的tombom评论时,查询解释如下:

+----+-------------+-------+------+---------------+---------+---------+-------+---------+-----------------------------+
| id | select_type | table | type | possible_keys | key     | key_len | ref   | rows    | Extra                       |
+----+-------------+-------+------+---------------+---------+---------+-------+---------+-----------------------------+
| 1  | SIMPLE      | T     | ref  | PRIMARY,iA    | PRIMARY | 4       | const | 2603472 | Using where; Using filesort |
+----+-------------+-------+------+---------------+---------+---------+-------+---------+-----------------------------+

2 个答案:

答案 0 :(得分:2)

您可以尝试各种方法:

1。)像这样

创建A,B和C的索引
CREATE INDEX iABC ON T(A,B,C);

由于问题很可能是HAVING子句(C列为varchar(255)在这种情况下不是很好):

2。)创建(临时或非临时)表,然后加入它。这可能会加快速度。以下非临时性可能更快,因为您可以在其上创建索引。

CREATE TABLE foo AS
SELECT 
B, 
COUNT(DISTINCT C) AS distinctC, 
COUNT(C) AS countC 
FROM T 
GROUP BY B;

CREATE INDEX idx_b ON foo(B);
CREATE INDEX idx_cc ON foo(distinctC, countC);

SELECT   T.B
FROM     T
INNER JOIN foo ON T.B = foo.B
WHERE    A = x
AND foo.distinctC = foo.countC
GROUP BY B
ORDER BY NULL; /*see Daan's comment*/

3.。)将C列放在一个单独的表中,其中实际内容由INT标识。

CREATE TABLE T (
    A int(11) NOT NULL,
    B varchar(45) NOT NULL,
    C int(11) DEFAULT NULL,
    PRIMARY KEY (A,B),
    KEY iB (B),
    KEY iC (C)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


CREATE TABLE C (
    id int(11) NOT NULL,
    Ccontent varchar(255) DEFAULT NULL
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

然后像往常一样做所有事情,稍后在将结果输出到表C时加入,用实际的varchar值转换id。

我更喜欢选项2.顺便说一句,你的索引iA可能没用。

答案 1 :(得分:0)

为什么不改为COUNT(DISTINCT C)=1