【问题标题】:MYSQL INNER JOIN is slow with indexMYSQL INNER JOIN 索引很慢
【发布时间】:2016-12-04 08:16:44
【问题描述】:

这是我的简单内部连接:

    SELECT
        SUM(ASSNZ.assenzeDidattiche) AS TotaleAssenze,
        SUM(ASSNZ.ore) AS totale_parziale,
        FLOOR(((SUM(ASSNZ.assenzeDidattiche) / SUM(ASSNZ.ore)) * 100)) AS andamento,
        MAX(ASSNZ.dataLezione) AS ultima_lezione,
        ASSNZ.idServizio,
        ASSNZ.idUtente
    FROM
        ciac_corsi_assenze AS ASSNZ
    INNER JOIN
        ciac_serviziAcquistati_ITA AS ACQ
               ON ACQ.idContatto = ASSNZ.idUtente
              AND ACQ.idServizio = ASSNZ.idServizio
              AND ACQ.stato_allievo <> 'ritirato'
    GROUP BY
        ASSNZ.idServizio,
        ASSNZ.idUtente

表“ASSNZ”有 213886 行,索引为“idUtente”、“idServizio”

表“ACQ”有 8950 行,索引为“idContatto”、“idServizio”

ASSNZ 表:

    CREATE TABLE `ciac_corsi_assenze` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `idUtente` int(11) DEFAULT NULL,
  `idServizio` int(11) DEFAULT NULL,
  `idCorso` int(11) DEFAULT NULL,
  `idCalendario` int(11) DEFAULT NULL,
  `modalita` varchar(255) DEFAULT NULL,
  `ore` int(11) DEFAULT NULL,
  `assenzeDidattiche` float DEFAULT NULL,
  `assenzeAmministrative` float DEFAULT NULL,
  `dataLezione` date DEFAULT NULL,
  `ora_inizio` varchar(8) DEFAULT NULL,
  `ora_fine` varchar(8) DEFAULT NULL,
  `dataFineStage` date DEFAULT NULL,
  `giustificata` varchar(128) DEFAULT NULL,
  `motivazione` longtext,
  `grouped` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idUtente` (`idUtente`) USING BTREE,
  KEY `idServizio` (`idServizio`) USING BTREE,
  KEY `dataLezione` (`dataLezione`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=574582 DEFAULT CHARSET=utf8;

ACQ 表:

CREATE TABLE `ciac_serviziacquistati_ita` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `idServizio` int(11) NOT NULL,
  `idContatto` int(11) NOT NULL,
  `idAzienda` int(11) NOT NULL,
  `idSede` int(11) NOT NULL,
  `tipoPersona` int(11) NOT NULL,
  `num_registro` int(11) NOT NULL,
  `codice` varchar(255) CHARACTER SET latin1 DEFAULT NULL,
  `dal` date NOT NULL,
  `al` date NOT NULL,
  `ore` int(11) NOT NULL,
  `costoOrario` decimal(10,0) NOT NULL,
  `annoFormativo` varchar(128) CHARACTER SET latin1 NOT NULL,
  `stato_attuale` int(11) NOT NULL,
  `datore_attuale` int(11) NOT NULL,
  `stato_allievo` varchar(64) CHARACTER SET latin1 NOT NULL DEFAULT 'corsista',
  `data_ritiro` date DEFAULT NULL,
  `crediti_formativi` int(11) NOT NULL,
  `note` longtext CHARACTER SET latin1 NOT NULL,
  `valore_economico` decimal(10,2) NOT NULL,
  `dataInserimento` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idServizio` (`idServizio`) USING BTREE,
  KEY `idAzienda` (`idAzienda`) USING BTREE,
  KEY `idContatto` (`idContatto`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9542 DEFAULT CHARSET=utf8;

这是我对选择的解释

现在因为查询很慢,在 1.5s / 2.0s 期间??

有什么问题吗?

更新

在 ciac_corsi_assenze 表中添加了新索引(带有 John Bollinger 的答案):

PRIMARY KEY (`id`),
  KEY `dataLezione` (`dataLezione`) USING BTREE,
  KEY `test` (`idUtente`,`idServizio`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=574582 DEFAULT CHARSET=utf8;

为 ciac_serviziAcquistati_ITA 表添加了新索引:

  PRIMARY KEY (`id`),
  KEY `idAzienda` (`idAzienda`) USING BTREE,
  KEY `test2` (`idContatto`,`idServizio`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9542 DEFAULT CHARSET=utf8;

新的解释:

但它总是很慢:(

【问题讨论】:

  • 我曾经遇到过列定义不同的问题。您要加入的列是一个表上的INT NOT NULL 和另一个表上的INT DEFAULT NULL。您可以使列定义相同吗? (当然,在测试表和/或数据库上进行,而不是在实时数据上进行,以防出现问题)
  • @charmeleon 现在列定义相同(INT NOT NULL)。但是查询总是很慢
  • ciac_corsi_assenzeciac_serviziacquistati_ita 有多大?看起来您的查询基本上从ciac_corsi_assenze 中选择每一行(因为没有WHERE 条件)。如果ciac_serviziacquistati_ita 是明显较小的表,您可能希望首先从较小的表中“驱动”查询。
  • 反映新索引的计划看起来比之前的计划有了实质性的改进。如果性能没有提高,那么问题可能出在数据库之外。
  • 我昨晚正在考虑这个问题,我认为只需在stato_allievo 上添加一个索引就可以解决您的问题(即使它不是一个复杂的索引)

标签: mysql performance join indexing inner-join


【解决方案1】:

您的表在感兴趣的各个列上都有单独的索引,但 MySQL 将最多为每个表使用一个索引来执行您的查询。这个特定的查询可能会由表ciac_corsi_assenze 加速,该表在(idUtente, idServizio) 上有一个索引(并且这样的索引将取代现有的(idUtente) 单独的索引)。这应该允许 MySQL 避免对结果行进行排序以执行分组,并且它将比任何现有索引更有助于执行连接。

ciac_serviziAcquistati_ITA(idContatto, idServizio) 甚至(idContatto, idServizio, ritirato) 上具有索引可能会进一步加快查询速度。其中任何一个都将取代 (idContatto) 上的现有索引。

【讨论】:

  • @marcozipsp,您的查询不是很复杂。如果它(现在)被最适合的索引支持,那么唯一可能的改进领域是加强运行 MySql 的硬件。更快的磁盘(并确保它是 local 磁盘)、更多的内存和更少的 CPU 争用可能会有所帮助。但在考虑这些之前,请检查您的查询计划以验证是否正在使用新索引。
【解决方案2】:

约翰走对了方向。但是复合索引中列的顺序需要改变。

对于GROUP BY,需要此订单(ASSNZ):

INDEX(idServizio, idUtente)

(应该替换KEY(idServizio),而不是KEY(idUtente)

那么ACQ需要,按这个顺序:

INDEX(idContatto, idServizio, stato_allievo)

仅替换KEY(idContatto)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-18
    • 2015-07-12
    • 1970-01-01
    • 2013-11-11
    • 2018-03-09
    • 2014-03-15
    • 1970-01-01
    相关资源
    最近更新 更多