通过大量连接优化缓慢的MySQL查询

时间:2015-09-16 12:46:45

标签: mysql sql database performance optimization

我不确定如何提高此查询的性能。这需要100多秒。我已经添加了索引并尝试了子查询,但似乎没有什么可以提高性能。

查询

SELECT
GiftVoucher.VoucherNumber,
GiftVoucher.DateIssued,
GiftVoucher.DateRedeemed,
R.old_name as RedeemedBy,
I.old_name as IssuedBy,
RH.Name as RedeemedForHotel,
V.old_name as VoidedBy,
GiftVoucher.VoidedReplacment,
GiftVoucher.VoidedDescription
FROM GiftVoucher
LEFT JOIN StaffToWp R ON GiftVoucher.RedeemedBy=R.old_id
LEFT JOIN StaffToWp I ON GiftVoucher.IssuedBy=I.old_id
LEFT JOIN StaffToWp V ON GiftVoucher.VoidedBy=V.old_id
LEFT JOIN Hotel RH ON GiftVoucher.RedeemedForHotelID=RH.HotelID
WHERE DateIssued > "2011-12-31 23:59:59"
LIMIT 0, 20000

GiftVoucher结构

GiftVoucher

Column  Type    Null    Default Comments
GiftVoucherID   int(11) No       
ParentGiftVoucherID int(11) Yes     NULL     
Value   decimal(19,4)   No       
VoucherNumber   varchar(150)    Yes     NULL     
SendToRecipientAddress  int(11) No       
DateIssued  datetime    No       
DateRedeemed    datetime    Yes     NULL     
GiftVoucherPurchaseID   int(11) No       
RedeemedBy  int(11) Yes     NULL     
IssuedBy    int(11) Yes     NULL     
Active  int(11) No       
RedeemedForHotelID  int(11) Yes     NULL     
RedeemedTo  int(11) Yes     NULL     
Redeemed    int(1)  No  0    
RedeemedAmount  decimal(19,4)   Yes     NULL     
Voided  int(1)  No  0    
VoidedDate  datetime    Yes     NULL     
VoidedBy    int(11) Yes     NULL     
VoidedReplacment    int(11) Yes     NULL     
VoidedDescription   mediumtext  Yes     NULL     
SystemVersion   int(11) No       
Indexes

Keyname Type    Unique  Packed  Column  Cardinality Collation   Null    Comment
PRIMARY BTREE   Yes No  GiftVoucherID   23191   A   No  
VoidedBy    BTREE   No  No  VoidedBy    2   A   Yes 
RedeemedBy  BTREE   No  No  RedeemedBy  244 A   Yes 
IssuedBy    BTREE   No  No  IssuedBy    212 A   Yes 
DateIssued  BTREE   No  No  DateIssued  23191   A   No  
RedeemedForHotelID  BTREE   No  No  RedeemedForHotelID  10  A   Yes 

StaffToWP结构

StaffToWp

Column  Type    Null    Default Comments
id  int(11) No       
old_id  int(11) No       
old_name    varchar(255)    No       
new_id  int(11) No       
new_name    varchar(255)    No       
Indexes

Keyname Type    Unique  Packed  Column  Cardinality Collation   Null    Comment
PRIMARY BTREE   Yes No  id  121 A   No  
old_id  BTREE   No  No  old_id  121 A   No  


Space usage:
Data    4,524   B
Index   7,168   B
Total   11,692  B
    Row Statistics:
Format  dynamic
Rows    121
Row length ø    37
Row size ø  97 B
Next autoindex  122
Creation    Sep 16, 2015 at 12:01 PM
Last update Sep 16, 2015 at 12:01 PM
Last check  Sep 16, 2015 at 12:01 PM

酒店结构

Hotel

Column  Type    Null    Default Comments
HotelID int(11) No       
Name    varchar(250)    No       
Telephone   varchar(50) No       
AccommodationUrl    varchar(250)    No  ''   
ColourClass varchar(50) Yes     NULL     
Indexes

Keyname Type    Unique  Packed  Column  Cardinality Collation   Null    Comment
PRIMARY BTREE   Yes No  HotelID 7   A   No  
HotelID BTREE   No  No  HotelID 7   A   No

EXPLAIN结果

id  select_type table   type    possible_keys   key key_len ref rows    Extra   
1   SIMPLE  GiftVoucher ALL DateIssued  NULL    NULL    NULL    22180   Using where
1   SIMPLE  R   ref old_id  old_id  4   milsomho_voucher.GiftVoucher.RedeemedBy 1   
1   SIMPLE  I   ref old_id  old_id  4   milsomho_voucher.GiftVoucher.IssuedBy   1   
1   SIMPLE  V   ref old_id  old_id  4   milsomho_voucher.GiftVoucher.VoidedBy   1   
1   SIMPLE  RH  eq_ref  PRIMARY,HotelID PRIMARY 4   milsomho_voucher.GiftVoucher.RedeemedForHotelID 1   

3 个答案:

答案 0 :(得分:0)

即使您的查询似乎进一步优化,您可以按照下面提到的两种方法进行检查,如果可以减少时间 -

第一种方法:

在GiftVoucher表中创建组合索引而不是单个索引,您必须检查不同的组合,例如在redeemedby,issuedby,voidedby,RedeemedForHotelID,dateissues上的单个组合索引。如果它不起作用,那么尝试使用有限的字段组合,并使用哪种组合可以获得更好的结果。

第二种方法:

当您需要前20,000行然后应用日期范围(下限和上限)时,您可以通过该日期范围确定至少获得20,000行并将其作为子查询,然后使用此子查询应用下一个左连接。这样,mysql必须匹配第一个左表(子查询数据)的有限记录和其他右表。

答案 1 :(得分:0)

实际包括表格结构和解释计划的荣誉!

  

DateIssued> " 2011-12-31 23:59:59"

据推测,此过滤器会显着减少查询返回的行数。但是你会发现DBMS没有在DateIssued上使用索引:

id  select_type table   type    possible_keys   key key_len ref rows    Extra   
1   SIMPLE  GiftVoucher ALL DateIssued  NULL    NULL    NULL    22180   Using where

可能的原因是类型不匹配,迫使MySQL在源表中的每一行上进行类型转换。

尝试:

DateIssued > 20111231235959

您还可以考虑使用其他方法更具体地了解要提取的数据(更多过滤)和对​​数据进行非规范化,以减少LEFT加入的表的数量。

基数数字看起来相当低 - 这是一个测试数据集吗?它们过时了吗?

答案 2 :(得分:0)

该表有大约22K行?但你要求20K?听起来像LIMIT是没用的;为什么会这样?

那个日期是4年前......它是否包括GiftVoucher的大部分内容?如果是这样,DateIssued上的索引可能不会被使用。这是因为扫描表可能更有效,而不是在索引和数据之间反弹。

综合指数?没有人会帮忙。 DateIssuedWHEREGROUP BY中只引用了一列ORDER BY

old_id是否在其他表中编入索引?它似乎是(" ref"),但它似乎不是PRIMARY KEY

您的LIMIT没有ORDER BY。所以你不关心你得到的20K行?

您尚未提供SHOW CREATE TABLE;我假设引擎是InnoDB?

DateIssued > "2011-12-31 23:59:59"可用于比较DATETIME;无需使用不同的语法。

缩小表格大小会有所帮助......你有很多INTs(带符号,4字节),你可以使用SMALLINT UNSIGNED(2字节,范围0)。 0.65535)。或MEDIUMINT UNSIGNED

有一件事可能会有所帮助...... A"覆盖索引":

INDEX(old_id, old_name)
<{1>} StaffToWp。这样可以更有效地查找old_id以获取old_name,这似乎是LEFT JOINs中3的目的。