开发者

MySQL indexing in an "or" statement

I have 3 tables that I need to join, these join together fine using indexes. However, we are transitioning over from using one legacy field as the identifier to another one in another table. LEGACYID is that legacy field, while NEWID is the new field. Both fields are varchars. Both fields are indexed exclusively with a btree index, both tables are MyISAM.

SELECT Username  
  FROM CUST C use index(primary,NEWID)
  JOIN TBLSHP S ON S.CUSID = C.CUSID
  JOIN TBLQ Q ON Q.SHPID = S.SHPID
 WHERE C.LEGACYID = '692041' 
    OR Q.NEWID = '692041'

This query takes 5.147 seconds, that's 5 seconds longer than I expect.

When doing an EXPLAIN EXTENDED query the index type for NEWID is ALL i.e. full table scan , possible keys are (primary,NEWID) and key(null). If I remove the LEGACYID from the Or statement, explain says key (NEWID) will now be used. If I remove NEWID from the OR statement changes occur as following:

  • the type of the table joins for (S,C) change from type ref to eq_ref
  • key_len changes from 4 to 5 (on both)
  • extra changes from empty to "Using where" . With either one of the statements removed from the the OR statement the query runs at expected speeds.

Table Q has 183k records; C:115k; S:169k. One la开发者_运维问答st point. if I move the query placement:

   SELECT Username  
     FROM CUST C use index(primary,NEWID)
     JOIN TBLSHP  S ON S.CUSID = C.CUSID
LEFT JOIN TBLQ Q ON Q.SHPID = S.SHPID 
                AND Q.NEWID = '692041'
    WHERE C.LEGACYID = '692041' 

Although its not the same query, for the way the data works, it will provide the results I need, and the speed is down to under a .1 of a second again.

I did want to clarify that I don't really need a query that works solution. Thanks to Ponies below that already has provided one. What I need to know is if anyone else has run into this problem and can explain why this is happening and what I can do for this simple or statement to use both indexes.


If you know there won't be duplicates, change UNION to UNION ALL (UNION ALL is faster because it doesn't remove duplicates). Otherwise, use:

SELECT Username  
  FROM CUST C use index(primary,NEWID)
  JOIN TBLSHP S ON S.CUSID = C.CUSID
  JOIN TBLQ Q ON Q.SHPID = S.SHPID
 WHERE C.LEGACYID = '692041' 
UNION
SELECT Username  
  FROM CUST C use index(primary,NEWID)
  JOIN TBLSHP S ON S.CUSID = C.CUSID
  JOIN TBLQ Q ON Q.SHPID = S.SHPID
 WHERE Q.NEWID = '692041'

ORs are notoriously bad performers, because it splinters the execution path. The UNION alleviates that splintering, and the combines the two results sets. That said, IN is preferable to ORs because though being logically the same, the execution of IN is generally more optimized.

UNION isn't always the answer

Investigate many options, comparing the EXPLAIN PLAN output before determining a solution. I've come across a couple recently that perform better using a cursor than a single query using esoteric functionality.

Also, make sure foreign key columns (what you're using in the ON clause when JOINing) are indexed. MySQL has started (v5.5+?) to automatically do this when a foreign key constraint is made, but that's only for InnoDB tables.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜