子查询运行缓慢的查询

时间:2019-11-01 00:56:08

标签: mysql sql

我有一条查询需要几分钟才能运行。它实际上是较大查询的一部分,但是这似乎是瓶颈。我有一个内部选择可能是罪魁祸首。

我正在寻找其他索引或其他重新布置以加快速度。我正在考虑也许将该子选择放入一个临时表中,除了它使用where子句中外部查询的数据外,否则将不起作用。

以下是查询:

SELECT principalid, count(*) AS CRs_used FROM
(
    SELECT crMan.principalid, crMan.repid, MIN(crMan.daterequest) as FirstContactDate
    FROM contactrequest crMan
    INNER JOIN principal p
        ON crMan.principalid = p.userid
    WHERE
        initiatedby = 2
        AND status <> 'C'
        AND NOT EXISTS
        (
             SELECT *
             FROM contactrequest crRep
             WHERE crMan.principalid = crRep.principalid
                 AND crMan.repid = crRep.repid
                 AND initiatedby = 1
                 AND status <> 'C'
                 AND crRep.daterequest < crMan.daterequest
         )
    GROUP BY userid, crMan.principalid, crMan.repid) AS ContactRequestsThatCount GROUP BY principalid;

模式:

CREATE TABLE `principal` (
  `operid` mediumint(8) unsigned NOT NULL DEFAULT 0,
  `ts` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  `userid` mediumint(8) unsigned NOT NULL DEFAULT 0,
  `targetcustomer` varchar(8000) NOT NULL DEFAULT '',
  `targetcustomer_stemmed` varchar(10000) NOT NULL DEFAULT '',
  `productline` varchar(8000) NOT NULL DEFAULT '',
  `productline_stemmed` varchar(10000) NOT NULL DEFAULT '',
  `salesopportunity` varchar(8000) NOT NULL DEFAULT '',
  `salesopportunity_stemmed` varchar(10000) NOT NULL DEFAULT '',
  `annualsales` decimal(11,0) DEFAULT NULL,
  `marketingassistance` bit(1) DEFAULT NULL,
  `trainingprovided` bit(1) DEFAULT NULL,
  `exclusiveterritories` bit(1) DEFAULT NULL,
  `repagency` bit(1) DEFAULT NULL,
  `made_in_usa` bit(1) DEFAULT NULL,
  `established_line` bit(1) DEFAULT NULL,
  PRIMARY KEY (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

CREATE TABLE `contactrequest` (
  `operid` mediumint(8) unsigned NOT NULL DEFAULT 0,
  `ts` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  `contactrequestid` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `repid` mediumint(8) unsigned NOT NULL DEFAULT 0,
  `principalid` mediumint(8) unsigned NOT NULL DEFAULT 0,
  `initiatedby` tinyint(3) unsigned NOT NULL DEFAULT 0,
  `response` char(1) NOT NULL DEFAULT '',
  `reasonid` tinyint(3) unsigned NOT NULL DEFAULT 0,
  `status` char(1) NOT NULL DEFAULT '',
  `daterequest` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `dateresponse` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `archivebypri` tinyint(1) NOT NULL DEFAULT 0,
  `archivebyrep` tinyint(1) NOT NULL DEFAULT 0,
  PRIMARY KEY (`contactrequestid`),
  KEY `ix_contactrequest_repid_request` (`repid`,`daterequest`),
  KEY `ix_contactrequest_principalid_request` (`principalid`,`daterequest`)
) ENGINE=InnoDB AUTO_INCREMENT=851354 DEFAULT CHARSET=latin1

这是EXPLAIN输出:

enter image description here

编辑:

查询的目的如下: contactrequest表包含我们网站的两个成员之间的联系记录,表示为负责人和代表。双方均可发起请求; initialby = 1表示代表已启动; initialby = 2表示委托人已启动。在每对原理和代表之间可以有多个这样的记录。

该查询计算从委托人到代表的联系数量,但是没有从代表到具有较早时间戳记的委托人的联系。此外,状态为'C'的行也会被忽略。

下面的答案中建议的索引已经部分包含在内。状态索引和initialby索引不是因为根据SQL文档,不应该使用基数较低的索引。 initialby仅在(1、2)中具有值,而在('C','N','')中具有状态。因此基数非常低。

编辑2:

查看原始查询和答案之后,这个问题没有意义,我认为SQL已更改。这样做的证据是-的建议中包含某些内容,除非原始查询中存在该内容。因此,我将把原始查询修改回我认为应该的样子。

问题在于最后一行的一部分在渲染的问题中不可见,但是当您尝试编辑时,实际上那里是正确的文本。我将尝试获取它,以便您可以在格式化的代码中看到它。

2 个答案:

答案 0 :(得分:0)

您可以从简化查询开始:

SELECT principalid, COUNT(DISTINCT userid, repid) AS CRs_used
FROM contactrequest crMan INNER JOIN
     principal p
     ON crMan.principalid = p.userid
WHERE initiatedby = 2 AND
      status <> 'C' AND
      NOT EXISTS (SELECT 1
                  FROM contactrequest crRep
                   WHERE crMan.principalid = crRep.principalid AND
                         crMan.repid = crRep.repid AND
                         initiatedby = 1 AND
                         status <> 'C' AND
                         crRep.daterequest < crMan.daterequest
                )
GROUP BY principalid;

您要在contactrequest(repid, initiatedby, status, daterequest)上建立索引。

有关该查询及其应做的工作的更多信息,您可能可以做的更多。

答案 1 :(得分:0)

您在这里不需要子查询,select case应该可以处理。如果您的not exists子句中符合条件,则将date的最大值设置为'9999-12-31'不会成为min()函数的候选对象

SELECT principalid, count(*) AS CRs_used FROM
    (
        SELECT crMan.principalid, crMan.repid
            , min(case when crMan.principalid = p.principalid 
                        and initiatedby = 1
                        and status <> 'C'                       
                    then '9999-12-31' 
                    when initiatedby = 2 and status <> 'C'
                    then crMan.daterequest
                    else crMan.daterequest end)
                 as FirstContactDate
        FROM contactrequest crMan
        INNER JOIN principal p
            ON crMan.principalid = p.userid  
        WHERE initiatedby = 2 AND status <> 'C'
        GROUP BY userid, crMan.principalid, crMan.repid) AS ContactRequestsThatCount