2012-02-09 3 views
1

나는 만 이상의 레코드가 내 데이터베이스에있는 사전을 가지고이 간단MySQL의 쿼리 성능 최적화

select * from Word where languageId = 'en' order by rand() limit 1 

무작위로 하나 개의 단어를 선택하는 선택합니다.

문제는이 요청이 3-4 초 동안 지속되는데, 이는 여러 번 반복해야하기 때문에 매우 길다는 것입니다.

동일한 작업을 훨씬 빠르게 수행 할 수있는 방법이 있습니까?

편집 - 테이블 스키마

wordId - integer, auto increment 
languageId - varchar (FK), values like cs, en, de, ... 
word - varchar, word itself 

데이터 구조의 예를

wordId languageId word 
-------------------------- 
1  cs   abatyše 
... 
100000 cs   zip 
100001 en   aardvark 
... 
etc 

SQL

CREATE TABLE Language (
    languageId VARCHAR(20) NOT NULL , 
    name VARCHAR(255) NULL , 
PRIMARY KEY(languageId)); 

CREATE TABLE Word (
    wordId INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, 
    languageId VARCHAR(20) NOT NULL , 
    word VARCHAR(255) NULL , 
PRIMARY KEY(wordId) , 
INDEX Word_FK_Language(languageId), 
    FOREIGN KEY(languageId) 
    REFERENCES Language(languageId) 
     ON DELETE NO ACTION 
     ON UPDATE NO ACTION); 
+0

레코드 ID 열이 있습니까? – Cheery

+0

@Cheery 예, 있습니다 – user219882

+0

할 일에 대해 자세히 알려주십시오. 단일 쿼리를 수행 할 필요가 없거나 솔루션을 최적화 할 수 있습니다. – Cheery

답변

3

당신이 만약 하 다음이 쿼리

SELECT * FROM `table` 
    WHERE id >= 
     (SELECT FLOOR(MAX(id) * RAND()) FROM `table` WHERE languageId = 'en') 
    AND languageId = 'en' 
    ORDER BY id LIMIT 1; 

을 시도하고 여기에 다른 예 http://akinas.com/pages/en/blog/mysql_random_row/

볼 IDS는 열을했습니다와 요소 사이의 간격은 (너무 많은 요소를 제거하지, 그렇지 않으면 일부 요소가 더 자주 선택됩니다) 거대한 아니다

ps : 방금 languageId에 대한 요구 사항 없이도 제대로 작동한다는 것을 깨달았습니다. 그렇지 않으면 동일한 언어 ID의 갭이 커질 수 있습니다.

업데이트 됨 시도해보십시오. 몇 번이나 시도해보십시오. 나는

SELECT d.* FROM 
    (SELECT @rn:=0) r, 
    (SELECT FLOOR(count(*)*RAND()) as rnd FROM `Word` WHERE languageId = 'en') t, 
    (SELECT @rn:[email protected]+1 as rn, `Word`.* FROM `Word` WHERE languageId = 'en') d 
WHERE d.rn >= t.rnd LIMIT 1 

은 기본적으로 여전히 연속 ID의 어떤 종류를 만들지 만, 그들에 의해 정렬하지 않고 .. 배 빠른 .. 쿼리의 실행 시간에 대해 그것을 확인.

최신 업데이트이 하나가 더 빨리 될 수는 테이블이 적절히 인덱스가 있는지 확인 먼저

SELECT d.* FROM 
    (SELECT @rn:[email protected]+1 as rn, w.*, t.rnd rnd FROM 
    (SELECT @rn:=0) r, 
    (SELECT FLOOR(count(*)*RAND()) rnd FROM `Word` WHERE languageId = 'en') t, 
    `Word` w 
    WHERE w.languageId = 'en' AND @rn<t.rnd 
) d 
WHERE d.rn=d.rnd 
+0

이 쿼리는 내 – user219882

+0

과 비슷한 시간이 걸렸습니다. 비슷한 솔루션을 생각하고있었습니다. 나는 기본 키가 인접 해 있어야한다고 생각하지만 그렇지 않으면 무작위 값과 일치하지 않을 가능성이있다. 또한 확실하지 않지만 일부 rdbms 각 레코드에 대한 하위 쿼리를 평가할 것이라고 생각합니다. – Tim

+0

@Tomas 아마도 각 레코드의 하위 쿼리를 평가하고 있습니다. – Tim

2

(생성 된 난수에 따라 다름). 기본 키가 있습니까? languageId은 색인입니까? 그것이 맞는지 확인하십시오.

두 번째로 단어에 관심이 있으십니까? languageId 또는 다른 필드가 없습니다. 그렇다면 다음이 필요합니다.

SELECT word_field FROM Word... 

와일드 카드 SELECT는 모든 것을 반환하지만 사용하지 않을 데이터는 검색하지 않아도됩니다.

셋째, 여러 번 반복하는 경우 루프에서 동일한 쿼리를 실행하고 있습니까?한 쿼리에서 더 많은 단어를 반환하기 위해 LIMIT 문을 변경 :

-- for 10 words 
... LIMIT 10 

당신은 데이터베이스를 다시 쿼리하지 않고 나중에 사용하기 위해이 결과를 저장할 수 있습니다.

마지막으로 쿼리를 실행할 수 있지만 쿼리를 실행하면 앞에 EXPLAIN이 표시되어 MySQL을 실행할 때 수행하는 작업에 대한 개요를 얻을 수 있습니다.

EXPLAIN SELECT word_field FROM Word... 

사용하면 정확하게 쿼리가 느리게 실행되는 위치를 식별 할 수 있습니다.

+0

좋은 캐치, 나는 질문을 읽을 때 반복에 관한 부분을 놓쳤다. OP는 반드시 정렬 된 목록을 재사용해야합니다 (또한 중복 방지). – Tim

+0

단어 필드는 시작할 수있는 좋은 장소입니다. 불행히도 나는 하나의 단어만을 반복해서 선택할 수있다. 그렇지 않으면 내 소스 코드에서 캐시를 만들고, 한 번에 더 읽고, 캐시에서 읽고이 프로세스를 반복해야합니다. 나는 또한 내 질문을 업데이 트 – user219882

+0

@ 토마스 예, 당신은 캐시 된 결과에서 읽을 필요가있다. 그러나 거의 모든 데이터베이스 드라이버가 자동으로이 작업을 수행합니다. 조회를 실행할 때 사용자가 해제 할 때까지 결과 세트를 보류해야합니다. – Tim

0

단어의 첫 번째 문자로 테이블을 분할하고 임의로 문자를 선택한 다음 기존 정렬을 사용하여 해당 파티션에서 임의의 단어를 선택할 수 있습니다. 현대 서버에서는 ~ 50,000 개의 행을 정렬하는 것이 합리적으로 빠릅니다. 대부분의 데이터베이스 정렬은 ng (n)이므로 레코드의 1/26은 50 배 이상 빠르게 정렬해야합니다. 파티션 선택은 성능 측면에서 무시할 수 있어야합니다. 한편, 같은 목록을 재사용하는 것에 대한 fuzzyDunlop의 의견은 의심의 여지없이 50 년 정도의 실행 후에도 승리 할 것입니다. 편집 : Windows Calc에서 로그를 망쳤으므로 다음과 같이 진행할 것입니다.