2017-04-22 7 views
0

잘린 데이터 집합에서 원하는 것을 수행하는 쿼리가 있지만 전체 데이터 집합 (수백만 행)에서 실행하면 영원히 실행됩니다.이 mysql 쿼리 (업데이트, 다중 조인)를 최적화하는 방법이 있습니까?

두 테이블 - microsat_table 및 coverage_table이 있습니다.

microsat_table :

+----+----------+-----------+---------+-------------------------------------------------+ 
| id | Seq_Name | SSR_Start | SSR_End | Sequence          | 
+----+----------+-----------+---------+-------------------------------------------------+ 
| 2 | chr2L |  11050 | 11067 | TTTAATTTAATTTAATTT        | 
| 3 | chr2L |  44173 | 44187 | TATGTATGTATGTAT         | 
| 5 | chr2L |  54431 | 54477 | ATAATAATATAATATAATATAATATAATATATAATAATATAATAATA | 
| 6 | chr2L |  57571 | 57594 | ATATATATATATATATATATATAT      | 
| 7 | chr2L |  72439 | 72453 | CATACATACATACAT         | 
| 8 | chr2L |  74028 | 74042 | ATACATACATACATA         | 
| 9 | chr2L |  85573 | 85587 | ATTTTATTTTATTTT         | 
| 10 | chr2L |  92429 | 92443 | ACATACATACATACA         | 
| 11 | chr2L | 138132 | 138166 | TATATAGATATATAAATATATATATATATATATAT    | 
| 13 | chr2L | 162245 | 162259 | ATACATACATACATA         | 
+----+----------+-----------+---------+-------------------------------------------------+ 

coverage_table : 모든 행에 걸쳐

| Seq_Name | Start | Stop | Coverage | 
+----------+-------+-------+----------+ 
| chr2L | 5716 | 5771 |  1 | 
| chr2L | 8730 | 8824 |  1 | 
| chr2L | 9894 | 9948 |  1 | 
| chr2L | 19391 | 19491 |  1 | 
| chr2L | 19575 | 19675 |  1 | 
| chr2L | 19773 | 19776 |  1 | 
| chr2L | 19776 | 19872 |  2 | 
| chr2L | 21920 | 21959 |  1 | 
| chr2L | 21959 | 22020 |  2 | 
| chr2L | 22020 | 22059 |  1 | 
+----------+-------+-------+----------+ 

I합니다 (coverage_table에서) 평균 범위를 계산 microsat_table에 열을 추가 할 위치를 시작 및 중지 값 커버리지 테이블에있는 값은 microsat_table의 SSR_Start 및 SSR_End 값에 속한다.

예 결과 :

+-----+----------+-----------+---------+--------------------------------+---------+ 
| id | Seq_Name | SSR_Start | SSR_End | Sequence      | avg  | 
+-----+----------+-----------+---------+--------------------------------+---------+ 
| 53 | chr2L | 402489 | 402503 | AAAACAAAACAAAAC    | 3.0000 | 
| 64 | chr2L | 447214 | 447233 | CAGCAGCAGCAGCAGCAGCA   | 8.0000 | 
| 66 | chr2L | 457839 | 457868 | CAGCAGCAGCAACAGCAGCAGCAGGCAGCA | 2.0000 | 
| 105 | chr2L | 579589 | 579603 | TCGAATCGAATCGAA    | 11.0000 | 
| 123 | chr2L | 628484 | 628501 | TAATGTTAATGTTAATGT    | 6.0000 | 
+-----+----------+-----------+---------+--------------------------------+---------+ 

내 쿼리는 다음과 같습니다

UPDATE microsat_table 
JOIN 
    (SELECT m.id, SUM(p.Coverage)/count(p.Start) 
     AS avg FROM microsat_table m 
     LEFT OUTER JOIN coverage_table p 
     ON m.Seq_Name LIKE p.Seq_Name 
     WHERE m.Seq_Name LIKE p.Seq_Name GROUP BY m.id) AS qt 
ON microsat_table.id = qt.id 
SET microsat_table.avg = qt.avg; 

하면 절단 테이블의 결과를 설명 : (HASH 및 BTREE 인덱스를 시도 포함) 나 인덱스를 추가

+----+-------------+----------------------+------------+-------+---------------------------------------------------+-------------+---------+--------------------------------+--------+----------+----------------------------------------------------+ 
| id | select_type | table    | partitions | type | possible_keys          | key   | key_len | ref       | rows | filtered | Extra            | 
+----+-------------+----------------------+------------+-------+---------------------------------------------------+-------------+---------+--------------------------------+--------+----------+----------------------------------------------------+ 
| 1 | UPDATE  | microsat_table_short | NULL  | ALL | PRIMARY           | NULL  | NULL | NULL       | 40356 | 100.00 | NULL            | 
| 1 | PRIMARY  | <derived2>   | NULL  | ref | <auto_key0>          | <auto_key0> | 4  | testdb.microsat_table_short.id | 1236 | 100.00 | NULL            | 
| 2 | DERIVED  | m     | NULL  | index | PRIMARY,Sequence,Seq_Name,Motif,SSR_Start,SSR_End | Seq_Name | 53  | NULL       | 40356 | 100.00 | Using index; Using temporary; Using filesort  | 
| 2 | DERIVED  | p     | NULL  | ALL | NULL            | NULL  | NULL | NULL       | 100163 |  1.23 | Using where; Using join buffer (Block Nested Loop) | 
+----+-------------+----------------------+------------+-------+---------------------------------------------------+-------------+---------+--------------------------------+--------+----------+----------------------------------------------------+ 

을 상당히 빨라졌지만, 더 큰 데이터 세트에서 1.5 일 동안 작동 시켰고 여전히 지느러미가 없었습니다 너.

더 빨리 달리는 방법에 대한 제안 사항이 있습니까?

감사합니다.

+0

저속 작업이기 때문에 실제 테이블에 대한 쿼리 계획을 추가하십시오. –

+0

결과 집합이 데이터 집합에 해당하지 않습니다. https://meta.stackoverflow.com/questions/333952/why-should-i-provide-an-mcve-for-what-seems-to-me-to-be-a-very-simple-sql-query를 참조하십시오. – Strawberry

답변

1

코드에 비교적 사소한 부분이 있습니다. 그러나 커다란 문제는 커버리지 테이블의 시작 및 종료 값이 microsat_table의 SSR_Start 및 SSR_End 값에 해당하는 모든 행에 대해 "coverage_table의 평균 커버리지"를 계산하려는 경우에는 그렇지 않다는 것입니다. 실제로 쿼리를 제한하는 것처럼 보입니다. 대신 Seq_Name에 성냥을 코드화했습니다. 어쩌면 하나의 큰 트랜잭션에서 테이블을 갱신

UPDATE microsat_table 
JOIN 
    (
    SELECT 
     m.id, 
     AVG(p.Coverage) AS avg -- MySQL has it's own average function 
    FROM 
     microsat_table m 
     INNER JOIN coverage_table p ON -- Change to INNER JOIN, your old WHERE clause had this effect anyway 
      m.Seq_Name = p.Seq_Name -- Use '=' not 'Like' when looking for an exact match 
    WHERE 
     p.Start >= m.SSR_Start -- This WHERE clause is the most important change 
     AND p.End <= m.SSR_End -- You omitted it in your version 
    GROUP BY 
     m.id) AS qt 
ON microsat_table.id = qt.id 
SET microsat_table.avg = qt.avg; 
+0

이 권고안을 다룰 때까지 (그리고 검증 할 때까지) 색인에 관한 원래의 질문에 답할 수 없습니다. –

+0

감사합니다 !! 명령 줄에서 쿼리를 40 초에서 8 초로 단축했습니다. 어떤 이유로 스크립트에서 정확히 동일한 쿼리를 실행하면 900 초가됩니다. - /. 나는 거기에서 무슨 일이 일어나는지 알아 내려고 노력하고있다. 그러나 그것은 스크립트가 아닌 쿼리가되어야하는 것처럼 보인다. –

0

정말 '좋아요'를 사용해야합니까? 이것은 성능상 가장 나쁜 것 중 하나입니다.

+0

그리고 같은 LIKE를 두 번하지 마십시오. –

+0

고마워요! "좋아"제거 도움이되는 것! –

0

단순히 너무 :

아래 코드는 그와 다른 더 작은 비트 (내가 >=<= 당신이 필요하지 않을 수 있습니다 사용) 수정을 시도 시스템에 많은가? (업데이트중인 테이블의 크기는 얼마입니까?) 블록 단위로 시도해 볼 수 있습니다. 나는 또한 여기에 간단한 하위 선택을 위해 갈 것이고, IMHO를 읽기가 더 쉬워 보인다.

또한 쿼리가 시작/중지 열을 신경 쓰지 않는 것으로 스티브 로벨 (Steve Lovell)의 말을 참고하십시오. 당신은 아마 사고가 난 너무 어려워하지 않아야 제거도 여기에 추가 한 =)

DECLARE @min_id int, 
     @max_id int, 
     @blocksize int 

SELECT @min_id = MIN(id), 
     @max_id = MAX(id), 
     @blocksize = 100000 -- adapt as needed 
    FROM microsat_table 

WHILE @min_id <= @max_id 
    BEGIN 

     UPDATE microsat_table 
      SET microsat_table.avg = ((SELECT SUM(p.Coverage)/count(p.Start) AS avg 
             FROM microsat_table m 
             LEFT OUTER JOIN coverage_table p 
                ON m.Seq_Name LIKE p.Seq_Name -- if possble use '=' here instead of LIKE 
                AND p.Start >= m.SSR_Start -- flagrantly "stolen" from Steve Lovell's answer 
                AND p.End <= m.SSR_End 
             WHERE m.id = microsat_table.id) 
     -- limit update to this block: 
     WHERE microsat_table.id BETWEEN @min_id AND (@min_id + @blocksize - 1) 

     -- prepare for next block 
     SELECT @min_id = @min_id + @blocksize 
    END 

하여 잊었 때문에 당신은 아마 microsat_tableid 필드와 Seq_name + Start을에 기본 키를 원하는 coverage_table의 열

+0

감사합니다 !! 예, 저는 그것을 우연히 잊었습니다 :-). 질문 - 전에 mysql 저장 프로 시저와 변수로 작업 한 적이 없으며 모든 코드를 함수에 넣어야합니까, 아니면 명령 줄에 그대로 입력 할 수 있습니까? 내가 그것을 입력 할 때 구문 오류가 발생하고 또한 구문 오류가 준 함수에 넣으려고했습니다. 나는 결코 그 전에 함수를 사용한 적이 없으므로 함수 선언을 잘못하고있을 수 있습니다 ... –

+0

나는 MSSQL에 익숙하다는 것을 인정할 것입니다. 나도 순진하게 보였지만 이것이 ANSI SQL이라고 생각하고 MySQL에 너무 많은 번거 로움없이 작업 할 것입니다. sqlfiddle에서 실행하려고 할 때 나는 매우 잘못되었다는 것을 보여 주었다. 나는 그것을 MySQL에서 작동시키고 다음 코드를 수정하려고 노력할 것이다 ... (이것은 다소 시간이 걸릴 것이다) – deroby