优化MySQL中的SQL查询

时间:2018-03-14 20:56:35

标签: mysql sql performance

我想知道为什么这个查询需要很慢(大约10到20秒),使用的三个表有500,000条记录,这是查询:

SELECT  *, 'rg_egresos' AS nombre_tabla
    FROM  rg_detallexml DE
    INNER JOIN  rg_egresos EG
    INNER JOIN  rg_emisor EM  ON DE.idContador = EG.id
      AND  DE.idDetalleXml = EG.idDetalleXml
      AND  DE.idContador = EM.idContador
      AND  DE.idDetalleXml = EM.idDetalleXml
    WHERE  DE.idContador = '14894'
      AND  DATE_FORMAT(dateFechaHora, '%Y-%m-%d') BETWEEN '2017-10-01'
                                                      AND '2017-10-31'
      AND  strTipodeComprobante = 'egreso'
      AND  version_xml = '3.2'
      AND  estado_factura = 0
      AND  modificado = 0;

这就是我使用EXPLAIN

时所显示的内容
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: EG
         type: index_merge
possible_keys: idx_idDetallexml,idx_estado_factura,idx_modificado,idx_idContador
          key: idx_idContador,idx_estado_factura,idx_modificado
      key_len: 4,4,4
          ref: NULL
         rows: 2111
        Extra: Using intersect(idx_idContador,idx_estado_factura,idx_modificado); Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: DE
         type: eq_ref
possible_keys: PRIMARY,idx_strTipodeComprobante,idx_idContador,idx_version_xml
          key: PRIMARY
      key_len: 4
          ref: db_pwf.EG.idDetalleXml
         rows: 1
        Extra: Using where
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: EM
         type: ref
possible_keys: idx_idContador,idx_idDetallexml
          key: idx_idDetallexml
      key_len: 4
          ref: db_pwf.DE.idDetalleXml
         rows: 1
        Extra: Using where

你能看到改进查询的方法吗?我有其他查询使用更大的表,它们更快,所有必填字段都有索引,谢谢。

表rg_detallexml:

+---------------------------------+--------------+------+-----+---------+----------------+
| Field                           | Type         | Null | Key | Default | Extra          |
+---------------------------------+--------------+------+-----+---------+----------------+
| idDetalleXml                    | int(10)      | NO   | PRI | NULL    | auto_increment |
| UUID                            | varchar(50)  | NO   | MUL | NULL    |                |
| dateFechaSubida                 | varchar(7)   | YES  |     | NULL    |                |
| idContador                      | int(10)      | NO   | MUL | NULL    |                |
| dateFechaHora                   | datetime     | YES  | MUL | NULL    |                |
| dateFechaHoraCertificacion      | datetime     | YES  |     | NULL    |                |
| dateFechaPago                   | datetime     | YES  |     | NULL    |                |
| intFolio                        | int(10)      | YES  |     | NULL    |                |
| strSerie                        | varchar(2)   | YES  |     | A       |                |
| doubleDescuento                 | double       | YES  |     | NULL    |                |
| doubleTotal                     | double       | YES  |     | NULL    |                |
| doubleSubtotal                  | double       | YES  |     | NULL    |                |
| duobleTotalImpuestosTrasladados | double       | YES  |     | NULL    |                |
| doubleTotalImpuestosRetenidos   | double       | YES  |     | NULL    |                |
| doubleTotalRetencionesLocales   | double       | YES  |     | NULL    |                |
| doubleTotalTrasladosLocales     | double       | YES  |     | NULL    |                |
| strTipodeComprobante            | varchar(15)  | YES  | MUL | NULL    |                |
| strMetodoDePago                 | varchar(150) | YES  |     | NULL    |                |
| strFormaDePago                  | varchar(150) | YES  |     | NULL    |                |
| strMoneda                       | varchar(10)  | YES  |     | NULL    |                |
| tipoCambio                      | double       | NO   |     | NULL    |                |
| strLugarExpedicion              | varchar(150) | YES  |     | NULL    |                |
| DIOT                            | int(1)       | YES  |     | 0       |                |
| version_xml                     | varchar(10)  | NO   | MUL | NULL    |                |
+---------------------------------+--------------+------+-----+---------+----------------+

表rg_egresos:

+---------------------------+--------------+------+-----+---------+----------------+
| Field                     | Type         | Null | Key | Default | Extra          |
+---------------------------+--------------+------+-----+---------+----------------+
| id_egreso                 | int(11)      | NO   | PRI | NULL    | auto_increment |
| id                        | int(11)      | NO   | MUL | NULL    |                |
| idDetalleXml              | int(10)      | NO   | MUL | NULL    |                |
| idCatalogo                | int(19)      | NO   | MUL | NULL    |                |
| tipoCuenta                | int(11)      | NO   | MUL | NULL    |                |
| intRubro                  | int(1)       | NO   |     | NULL    |                |
| RFC                       | varchar(20)  | NO   | MUL | NULL    |                |
| compra_gastos_0_porciento | float        | NO   | MUL | NULL    |                |
| deducible                 | int(1)       | NO   |     | NULL    |                |
| compra_gastos_exentos     | float        | NO   |     | NULL    |                |
| no_deducibles             | float        | NO   |     | NULL    |                |
| estado_factura            | int(11)      | NO   | MUL | NULL    |                |
| fecha                     | date         | NO   | MUL | NULL    |                |
| total_xml                 | double       | NO   |     | NULL    |                |
| subtotal_xml              | double       | NO   |     | NULL    |                |
| iva_xml                   | double       | NO   |     | NULL    |                |
| total_impuestos           | double       | NO   |     | NULL    |                |
| abonado                   | double       | NO   |     | NULL    |                |
| subtotal                  | double       | NO   |     | NULL    |                |
| iva                       | double       | NO   |     | NULL    |                |
| pendiente                 | double       | NO   |     | NULL    |                |
| subtotal_sin_iva          | double       | NO   |     | NULL    |                |
| acreditable               | int(1)       | NO   | MUL | 0       |                |
| fecha_operacion           | datetime     | NO   | MUL | NULL    |                |
| modificado                | int(1)       | NO   | MUL | NULL    |                |
| UUID                      | varchar(50)  | NO   | MUL | NULL    |                |
| IEPS                      | double       | NO   |     | NULL    |                |
| retencion_iva             | double       | NO   |     | NULL    |                |
| retencion_isr             | double       | NO   |     | NULL    |                |
| imp_local                 | double       | NO   |     | 0       |                |
| enviado_a                 | int(11)      | NO   | MUL | NULL    |                |
| enviado_al_iva            | int(1)       | NO   |     | NULL    |                |
| EsNomina                  | int(1)       | NO   | MUL | 0       |                |
| dateFechaPago             | date         | NO   | MUL | NULL    |                |
| nota_credito              | int(1)       | NO   | MUL | NULL    |                |
| extranjero                | int(1)       | NO   | MUL | NULL    |                |
| pago_banco                | int(1)       | NO   | MUL | NULL    |                |
| idBanco_Pago              | int(20)      | NO   | MUL | NULL    |                |
| movimientoPago            | int(10)      | NO   |     | NULL    |                |
| saldo_banco               | varchar(50)  | NO   |     | NULL    |                |
| tipo_pago                 | int(1)       | NO   |     | 0       |                |
| responsable               | varchar(100) | NO   |     | NULL    |                |
+---------------------------+--------------+------+-----+---------+----------------+

表rg_emisor:

+-----------------+--------------+------+-----+---------+----------------+
| Field           | Type         | Null | Key | Default | Extra          |
+-----------------+--------------+------+-----+---------+----------------+
| idEmisor        | int(10)      | NO   | PRI | NULL    | auto_increment |
| idDetalleXml    | int(10)      | NO   | MUL | NULL    |                |
| idContador      | int(10)      | NO   | MUL | NULL    |                |
| strRFC          | varchar(13)  | NO   |     | NULL    |                |
| strNombreEmisor | varchar(200) | YES  |     | NULL    |                |
| strRegimen      | varchar(250) | YES  |     | NULL    |                |
| strPais         | varchar(40)  | YES  |     | MX      |                |
| strEstado       | varchar(50)  | YES  |     | NULL    |                |
| intCP           | int(5)       | YES  |     | NULL    |                |
| strMunicipio    | varchar(250) | YES  |     | NULL    |                |
| strLocalidad    | varchar(250) | YES  |     | NULL    |                |
| strColonia      | varchar(250) | YES  |     | NULL    |                |
| intNumExt       | int(10)      | YES  |     | NULL    |                |
| intNumInt       | int(10)      | YES  |     | NULL    |                |
| strCalle        | varchar(250) | YES  |     | NULL    |                |
| regimenFiscal   | varchar(20)  | YES  |     | NULL    |                |
+-----------------+--------------+------+-----+---------+----------------+

3 个答案:

答案 0 :(得分:3)

现在您已经显示了表格,我们发现rg_egresos.id不是表格的ID。因此,表中的一个概念可以有多个记录。让我们更仔细地查看表格和查询:

所有表格都包含一个contador ID和一个DetalleXml ID。你想在这两个领域加入他们。所以你从rg_detallexml开始并获取contador的所有记录。使用找到的idDetalleXml,您可以搜索rg_egresosrg_emisors

这有点奇怪。首先,rg_detallexml显然与一个概念相关联,但在其他表格中,rg_detallexml可以与另一个概念相关联。嗯,这可能是可能的(可能是某种关系)。但是,对于rg_egresos / contador,有五个rg_emisors条记录和四条rg_detallexml条记录,您需要选择30条记录,因为您要将rg_egresos条记录与rg_emisors组合在一起与真正无关的记录。

无论如何:你想快速找到rg_detallexml

create index idx_de on rg_detallexml(idcontador, strtipodecomprobante, version_xml,
                                     datefechahora, iddetallexml);

然后你找rg_egresos

create index idx_eg on rg_egresos(id, iddetallexml, estado_factura, modificad);

最后你要找rg_emisor

create index idx_em on rg_emisor(idcontador, iddetallexml);

由于列存在于所有表中,我们当然可以按任何顺序浏览它们。从rg_detallexml开始似乎也是最自然和最具限制性的,但这不一定是最好的。所以你可能想要为DBMS提供另一个索引:

create index idx_eg2 on rg_egresos(id, estado_factura, modificad, iddetallexml);

这将允许DBMS首先在此表中查找contador的记录,并使用添加的条件在此处查找相关的iddetallexml

答案 1 :(得分:1)

我看到的最大问题是这一部分:

library(purrr)
ParsedFile %>% map(~map_df(., ~trimws(.)))

dateFechaHora是一个日期时间字段吗?为什么要将datetime字段转换为字符串(DATE_FORMAT)?即使你在dateFechaHora字段上有索引,它也不会被使用。

我建议您改用此代码:

DATE_FORMAT(dateFechaHora, '%Y-%m-%d') BETWEEN '2017-10-01' AND '2017-10-31'

是的,第二天它就不会被包括在内。

所以你的查询可能如下所示:

and DateFechaHora >= '2017-10-01' and DateFechaHora < '2017-11-01'
                                                       ^^^^^^^^^^

答案 2 :(得分:1)

我在其他回复中看到两个部分答案。让我们把它们绑在一起。

更改

  AND  DATE_FORMAT(dateFechaHora, '%Y-%m-%d') BETWEEN '2017-10-01'
                                                  AND '2017-10-31'

  AND DE.dateFechaHora >= '2017-10-01'
  AND DE.dateFechaHora  < '2017-10-01' + INTERVAL 1 MONTH

如果DE是一个很好的起点:

DE:  INDEX(idContador, strTipodeComprobante, version_xml, dateFechaHora)
       -- date last; others in any order

如果EG是一个更好的起点:

EG:  INDEX(estado_factura, modificado, id)   -- in any order
DE:  INDEX(idContador, idDetalleXml,
           strTipodeComprobante, version_xml, dateFechaHora)

也有

EM:  INDEX(idContador, idDetalleXml)   -- in either order

“使用intersect”几乎总是一个线索,你应该有一个复合索引而不是单独的索引。 (单独的索引可能对其他查询有用。)

(即添加所有这些索引,然后让优化器决定。)

请使用SHOW CREATE TABLE,而不是描述性较低的DESCRIBE

你真的需要SELECT *吗?

查询后,我的建议:

SELECT  DE.*,
        EG.*,
        EM.*,
        'rg_egresos' AS nombre_tabla
    FROM  rg_detallexml DE
    INNER JOIN  rg_egresos EG
       ON  DE.idContador = EG.id
      AND  DE.idDetalleXml = EG.idDetalleXml
    INNER JOIN  rg_emisor EM
       ON  DE.idContador = EM.idContador
      AND  DE.idDetalleXml = EM.idDetalleXml
    WHERE  DE.idContador = '14894'
      AND  DE.dateFechaHora >= '2017-10-01'
      AND  DE.dateFechaHora  < '2017-10-01' + INTERVAL 1 MONTH
      AND  DE.strTipodeComprobante = 'egreso'
      AND  DE.version_xml = '3.2'
      AND  EG.estado_factura = 0
      AND  EG.modificado = 0;