如何优化此查询作为in数组似乎显着减慢了事情

时间:2013-03-17 18:31:54

标签: mysql optimization

我希望找到优化查询的最佳方法,如下所示:

SELECT
  a.ID,
  a.ECPCodeID,
  a.RegDate,
  a.BusName,
  a.City,
  a.AccountNum,
  b.ID         as RepCodeID,
  b.RepCode
FROM ECPs_Registration a,
  Reps_Codes b
WHERE (SUBSTR(a.PostalCode,1,5)IN(SELECT
                    SUBSTR(Zip,1,5)
                  FROM Reps_Zip
                  WHERE RepCodeID = b.ID)
       AND a.AccountNum NOT IN(SELECT
                 ShipTo
                   FROM Reps_ShipTo))
     OR a.AccountNum IN(SELECT
              ShipTo
            FROM Reps_ShipTo
            WHERE RepCodeID = b.ID)
ORDER BY b.RepCode,a.BusName,a.City

我知道有更多因素涉及索引等等,我现在只是询问它的查询部分。主要是因为我必须通过Reps_ShipTo和Reps_Zip表来获取大量记录。我想改变像:

a.AccountNum NOT IN (SELECT ShipTo FROM Reps_ShipTo)
INTO
(SELECT count(*) FROM Reps_ShipTo WHERE a.AccountNum = ShipTo) = 0

不确定这是否正确或是否有更好的方法。任何帮助,将不胜感激。感谢。

编辑:

架构:

CREATE TABLE IF NOT EXISTS `ECPs_Codes` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `ECPCode` char(4) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `ECPCode` (`ECPCode`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 ;

CREATE TABLE IF NOT EXISTS `ECPs_Registration` (
  `RegDate` datetime NOT NULL,
  `ID` int(10) NOT NULL AUTO_INCREMENT,
  `ECPCodeID` int(11) NOT NULL,
  `FirstName` varchar(200) NOT NULL,
  `LastName` varchar(200) NOT NULL,
  `BusName` varchar(200) NOT NULL,
  `Address` varchar(200) NOT NULL,
  `Address2` varchar(200) NOT NULL,
  `City` varchar(100) NOT NULL,
  `Province` char(2) NOT NULL,
  `Country` varchar(100) NOT NULL,
  `PostalCode` varchar(10) NOT NULL,
  `Email` varchar(200) NOT NULL,
  `AccountNum` int(8) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `ECPCodeID` (`ECPCodeID`),
  KEY `PostalCode` (`PostalCode`),
  KEY `AccountNum` (`AccountNum`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS `Reps_Codes` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `Name` varchar(50) NOT NULL,
  `RepCode` varchar(16) NOT NULL,
  `AllAccess` tinyint(4) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `RepCode` (`RepCode`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS `Reps_ShipTo` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `RepCodeID` int(11) NOT NULL,
  `ShipTo` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `RepID` (`RepCodeID`),
  KEY `ShipTo` (`ShipTo`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE IF NOT EXISTS `Reps_Zip` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `RepCodeID` int(11) NOT NULL,
  `Zip` varchar(10) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `RepCodeID` (`RepCodeID`),
  KEY `Zip` (`Zip`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

2 个答案:

答案 0 :(得分:1)

有两件事严重影响了您的查询效果。

  1. 您通过组合多个条件来加入两个表,每个条件都需要子查询
  2. 您正在使用SUBSTR(Zip,1,5)= SUBSTR(邮政编码,1,5)在两个表上进行连接
  3. 您的查询背后的逻辑似乎是:

      

    对于每个ECPs_Registration找到Rep_Codes中的匹配记录   使用以下规则:

         
        
    • 如果Reps_ShipTo中有匹配的记录,则对于该注册,请使用该表进行查找(主要匹配)
    •   
    • 如果Reps_ShipTo中没有匹配的记录,请通过邮政编码匹配(辅助)通过Reps_Zip寻找匹配的RepCode
    •   

    现在,如果以上内容完全描述了您的情况,您可能应该从重新设计数据库开始。

    Reps_ShipTo表在ECPs_RegistrationRep_Codes之间创建0:N关系。这样的关系不需要额外的表 - 它们可以简单地存储为可以为空的外键 - 在你的情况下RepCodeId中的ECPs_Registration可以做到这一点,并且会从数据库中删除整个Reps_ShipTo表

    您还应该创建(是的,多余的)额外列,这些列仅存储ECPs_RegistrationReps_Zip中邮政编码的前5个字母。这将允许简单的相等匹配而不是SUBSTR函数。或者,您可能决定仅为每条记录执行此匹配一次,并将结果存储在上面RepCodeId完全取消双联接。

    以下查询假定您由于某种原因不希望或无法更改数据库:

    SELECT 
      a.ID,  a.ECPCodeID,  a.RegDate,  a.BusName,  a.City,  a.AccountNum,
      CASE (b1.ID IS NOT NULL, b1.ID, b2.ID) as RepCodeID,
      CASE (b1.ID IS NOT NULL, b1.RepCode, b2.RepCode) as MyRepCode
    FROM ECPs_Registration a
        LEFT JOIN Reps_ShipTo  ON (Reps_ShipTo.Shipto=a.AccountNum)
        LEFT JOIN Rep_Codes b1 ON (b1.ID=Reps_ShipTo.RepCodeId)
        LEFT JOIN Reps_Zip     ON (SUBSTR(Zip,1,5)=SUBSTR(a.postalcode,1,5))
        LEFT JOIN Rep_Codes b2 ON (b2.ID=Reps_Zip.RepCodeID)
    ORDER BY MyRepCode,a.BusName,a.City
    

    如果没有您的数据库架构和示例数据,我无法测试上述查询是否真正有效并且与原始查询具有相同的结果。

答案 1 :(得分:0)

SELECT
  a.ID,
  a.ECPCodeID,
  a.RegDate,
  a.BusName,
  a.City,
  a.AccountNum,
  b.ID         as RepCodeID,
  b.RepCode
FROM ECPs_Registration a,   Reps_Codes b
INNER JOIN Reps_Zip as r on SUBSTR(a.PostalCode,1,5) = SUBSTR(r.Zip,1,5)
LEFT JOIN Reps_ShipTo as rs on a.AccountNum = rs.ShipTo
LEFT JOIN ShipTo as s on a.AccountNum = s.ShipTo
WHERE (s.id is null or rs.id is null) 
ORDER BY b.RepCode,a.BusName,a.City