2013-10-02 2 views
0

mysql 5.1에서 작업합니다.정렬과 그룹을 결합하여 MySQL 쿼리를 최적화하십시오.

  • 항목 : ID와 이름의 '사용자'테이블 (100 개 000 항목)
  • 출력 : 첫 번째 user_ids에 대한

    나는 그것이이이에서 생성하는 쿼리를 최적화 할 각 문자, 그들의 카운트

예 :

id | name 
1 | Bob 
2 | Albert 
3 | bernard 

출력 :

letter | id | count 
    A | 2 | 1 
    B | 1 | 2 

첫 번째 문자 A는 1 명의 사용자 (Albert)이고, 문자 B는 2 명의 사용자 (bernard 및 Bob)입니다. 알파벳 순서의 첫 번째 문자는 bernard입니다.

나는 작동하는 쿼리가 있습니다. 첫 번째 사용자 및 개수와 함께 모든 문자 (및 '문자 없음')를 반환합니다.

SELECT formatted_letter, id, COUNT(1) 
FROM (
    SELECT 
    CASE WHEN name REGEXP '[A-Za-z].*' 
      THEN UPPER(SUBSTR(name, 1, 1)) 
     ELSE '@' 
    END as formatted_letter, id, name 
    FROM `users` 
    ... (some joins and conditions) 
    ORDER BY name 
) AS A 
GROUP BY formatted_letter 

이 완벽하게 작동하고 올바른 값을 반환 ...하지만이 쿼리 (25 000 사용자의 선택에 구초) 매우 시간이 많이 소요입니다 ...

당신이 어떤 다른 방법이 있나요 이 쿼리를 최적화하려면?

상황이 내가 시도 :

  • 는 각 문자에 대한 큰 조합을,이 최악의 (36초)입니다.
  • CASE/WHEN 부분을 삭제하려면 'formatted_letter'열을 추가하십시오. 나쁘지는 않지만 8 초가 걸립니다.

모든 색인은 사용자 ID, 사용자 이름 및 조인 및 조건에 대한 모든 색인에 있습니다.

답변

1

가능한 아이디어 : -

SELECT FirstLetter, MAX(name), SUM(NameCount) 
FROM 
(
    SELECT substr(name, 1, 1) AS FirstLetter, MIN(name) AS name, COUNT(*) AS NameCount 
    FROM company 
    GROUP BY FirstLetter 
    UNION 
    SELECT 'A' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'B' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'C' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'D' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'E' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'F' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'G' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'H' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'I' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'J' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'K' AS FirstLetter, "" AS name, 0 AS NameCount 
    UNION 
    SELECT 'L' AS FirstLetter, "" AS name, 0 AS NameCount 
) sub1 
GROUP BY FirstLetter 

(나는 간격을 작성하기에 노동 조합에 가능한 문자를 입력 지루있어).

이것은 작동하지만 테이블의 성능에 대해서는 확신 할 수 없습니다 (내가 가지고있는 임의의 테이블/필드에서 약 140k 레코드로 1 초가 걸립니다).

EDIT - 다시 시도하십시오.

기본적인 쿼리 (빈칸을 채우는 무시)이 내려 온다 : -

SELECT CASE WHEN name REGEXP '[A-Za-z].*' THEN UPPER(SUBSTR(name, 1, 1)) ELSE '@' END as formatted_letter, MIN(id) AS id, COUNT(*) AS NameCount 
FROM users 
GROUP BY formatted_letter 

이 그 자체로 매우 효율적이어야한다. 시도 해보고 얼마나 걸릴지 알려주세요.

이것이 빠르면 노동 조합에서 제로 카운트 레코드를 추가하는 것이 공칭 시간을 추가해야합니다.

140k 레코드가있는 임의의 테이블에서 시도한 결과 나에게 약 1 초가 걸렸으며 이름 필드도 인덱싱되지 않았습니다.

UNION을 선택에 추가하면 쿼리에 띄는 시간을 추가하지 않습니다 -

SELECT formatted_letter, MAX(name), SUM(NameCount) 
FROM 
(
    SELECT CASE WHEN company REGEXP '[A-Za-z].*' THEN UPPER(SUBSTR(company, 1, 1)) ELSE '@' END as formatted_letter, MIN(id) AS id, COUNT(*) AS NameCount 
    FROM users 
    GROUP BY formatted_letter 
    UNION 
    SELECT 'A' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'B' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'C' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'D' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'E' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'F' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'G' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'H' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'I' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'J' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'K' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'L' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'M' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'N' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'O' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'P' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'Q' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'R' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'S' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'T' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'U' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'V' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'W' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'X' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'Y' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT 'Z' AS formatted_letter, "" AS id, 0 AS NameCount 
    UNION SELECT '@' AS formatted_letter, "" AS id, 0 AS NameCount 
) Sub1 
GROUP BY formatted_letter 

을이 후 이상한 일이

+0

나는이 아이디어를 이미 시도했다는 것을 나의 포스트에서 설명한다 ... 그리고 이것은 최악이다! – pierallard

+0

이것은 끝에 오는 여분의 글자 (0 카운트 포함)에서만 병합됩니다. 이 단계에서는 적은 수의 행만 다루기 때문에 매우 효율적이어야합니다. 나는 원래의 글에서 각 글자에 대해 한 번 전체 쿼리를 수행 한 다음 그 결과를 병합하는 것으로 추측했다. (느림) – Kickstart

+0

추가 세부 사항. – Kickstart

0

'문자 없음'은 무엇을 의미하며 노출 된 경우 보낸 사람 (다른 조인/조건)도 최적화 할 수 있습니다. MINIMUM에서, 당신은 단지 이름을 가지고 있습니까? 아니면 적어도 첫 번째 위치에있는 이름입니까?

또한, 내가 말하자면 내부 출력 ORDER BY NAME 절을 죽일 것입니다. 그 이유는 어쨌든 formatted_letter에 의해 그룹을 수행하는 최종 출력에 실제적인 영향을 미치지 않기 때문입니다. 외부 쿼리에서 formatted_letter를 사용하여 순서를 추가하십시오. 단지 26 + '@'레코드를 반환하고 즉시 것입니다. 여기

+0

사용자 일어나고 당신이 컴퓨터에 너무 36초 걸리는 경우 또는 여러에 속하는 그룹; JOIN과 WHERE를 사용하여 그룹 집합의 사용자 만 선택했습니다. "no-letters"는 "@"또는 "$"와 같이/[A-Z0-9] /로 시작하지 않는 이름입니다. – pierallard

+0

@ForgetTheNorm 인 경우 GROUPS 규정 조건을 맨 앞에 놓고 자격을 얻는 이름 만 가져 오는 것이 좋습니다. 다시 말하면, 그 관계/기준을 보지 않고, 그것이 더 이상 최적화 될 수 있는지 확실하지 않습니다. – DRapp

관련 문제