在多个表上查询太大又慢,如何优化呢?

时间:2019-01-24 23:54:29

标签: mysql

我有一个带有订单的商店系统,该系统具有订单及其物料的状态。我有这六个表:

订单(pedido)。

订购商品(pedido_item)。

订单状态(status_pedido)。

订单商品状态(status)。

订单状态日志(pedido_status_pedido)。

订单商品状态日志(pedido_item_status)。

因此,当我必须从订单更新状态时,我在pedido_status_pedido上插入新行,并更新表status_pedido_id上的pedido。物品也一样。

一个订单状态与许多项目状态相关联,例如,订单状态“待处理”与项目状态“正在等待文件”,“有错误的文件”和“已批准文件”相关。

例如,当前订单状态基于其当前商品状态,例如最“延迟”的状态,而“成品错误”项目后面是“错误文件”。为此,在订单和商品状态上都有列列。

因此,如果我的订单上有3个项目,且状态为“有错误的文件”,“生产中”,“生产完成”,则订单状态为“待处理”,因为这是该订单的对应订单状态项状态“文件错误”,该状态更远。

问题是当我必须更新特定的订单状态时。我提出了一个非常复杂的查询,我需要SET SQL_BIG_SELECTS=1使其运行。显然,查询是如此之慢,以至于使我的整个网站都很慢(每10分钟调用一次,以获取大量订单)

这是我的查询,解释如下:

INSERT INTO pedido_status_pedido (pedido_id, status_pedido_id) VALUES ({$this->pedido_id}, ( --Insert into order status log the id, and the status id
    SELECT sp.status_pedido_id FROM `status` s --Subquery for the order status id, get it from the relationship inside the item status
    LEFT JOIN status_pedido sp ON sp.status_pedido_id = s.status_pedido_id
    WHERE s.status_id = ( --Subquery for the further behind item status
        SELECT s.status_id FROM pedido_item_status p1
        LEFT JOIN `status` s ON s.status_id = p1.status_id
        LEFT JOIN pedido_item ON pedido_item.pedido_item_id = p1.pedido_item_id
        INNER JOIN ( --Get the LATEST status of each item and compare
            SELECT MAX( si.sta_ordem ) AS maxordem, pedido_item_id FROM pedido_item_status pi
            LEFT JOIN status si ON pi.status_id = si.status_id
            WHERE pi.excluido IS NULL AND pi.pedido_id = {$this->pedido_id}
            GROUP BY pi.pedido_item_id
        ) p2 ON ( s.sta_ordem = p2.maxordem ) AND p1.excluido IS NULL AND p1.pedido_item_id = p2.pedido_item_id
        WHERE p1.pedido_id = {$this->pedido_id}
        ORDER BY s.sta_ordem ASC 
        LIMIT 1
    )
)

这是表的定义(对不起,它有点大):

    CREATE TABLE `pedido`  (
        `pedido_id` int(11) NOT NULL AUTO_INCREMENT,
        `cliente_id` int(11) NULL DEFAULT NULL,
        `forma_envio_id` int(11) NULL DEFAULT NULL,
        `balcao_retirada_id` int(11) NULL DEFAULT NULL,
        `ped_responsavel_retirada` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `ped_codigo_rastreio` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `ped_prazo_entrega` int(11) NULL DEFAULT NULL,
        `ped_prazo_producao` int(11) NULL DEFAULT 0,
        `ped_data` datetime(0) NULL DEFAULT '0000-00-00 00:00:00',
        `ped_data_producao` date NULL DEFAULT NULL,
        `ped_data_entrega` date NULL DEFAULT NULL,
        `ped_notificado` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'N',
        `ped_transacao_pagarme` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `ped_cmd` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT '',
        `ped_data_confirmacao_pagamento` datetime(0) NULL DEFAULT NULL,
        `forma_pagamento_id` int(11) NULL DEFAULT NULL,
        `ped_vencimento_boleto` date NULL DEFAULT NULL,
        `ped_comprovante` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `ped_pago` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'N',
        `status_id` int(11) NOT NULL DEFAULT 0,
        `status_pedido_id` int(11) NOT NULL DEFAULT 0,
        `ped_valor_adicionais` decimal(10, 2) NULL DEFAULT NULL,
        `ped_valor_frete` decimal(10, 2) NULL DEFAULT NULL,
        `ped_valor_produtos` decimal(10, 2) NULL DEFAULT 0.00,
        `ped_valor_desconto` decimal(10, 2) NULL DEFAULT 0.00,
        `ped_valor_total` decimal(10, 2) NULL DEFAULT 0.00,
        `excluido` datetime(0) NULL DEFAULT NULL,
        `cadastrado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
        `atualizado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
        PRIMARY KEY (`pedido_id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 15876 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;


    CREATE TABLE `pedido_item`  (
        `pedido_item_id` int(11) NOT NULL AUTO_INCREMENT,
        `pedido_id` int(11) NULL DEFAULT NULL,
        `pei_indice` int(11) NULL DEFAULT NULL,
        `produto_id` int(11) NULL DEFAULT NULL,
        `produto_variacao_id` int(11) NULL DEFAULT NULL,
        `produto_preco_id` int(11) NULL DEFAULT NULL,
        `tipo_arquivo_id` int(11) NULL DEFAULT NULL,
        `pei_arquivo` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `pei_arquivo_nome` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `pei_nome` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `pei_quantidade` int(11) NULL DEFAULT NULL,
        `pei_valor_unitario` decimal(10, 2) NULL DEFAULT NULL,
        `pei_valor_total` decimal(10, 2) NULL DEFAULT NULL,
        `pei_valor_frete` decimal(10, 2) NULL DEFAULT NULL,
        `pei_codigo_preco` int(11) NULL DEFAULT NULL,
        `pei_codigo_interno` varchar(40) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `status_id` int(11) NOT NULL DEFAULT 0,
        `excluido` timestamp(0) NULL DEFAULT NULL,
        `cadastrado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
        `atualizado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
        `pei_producao_finalizada` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'N',
        `pei_arquivo_erro` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        PRIMARY KEY (`pedido_item_id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 17528 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;

    CREATE TABLE `pedido_item_status`  (
        `pedido_item_status_id` int(11) NOT NULL AUTO_INCREMENT,
        `pedido_id` int(11) NOT NULL,
        `pedido_item_id` int(11) NOT NULL,
        `status_id` int(11) NOT NULL,
        `pis_texto` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL,
        `pis_notificar_cliente` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'S',
        `excluido` datetime(0) NULL DEFAULT NULL,
        `cadastrado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
        `atualizado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
        PRIMARY KEY (`pedido_item_status_id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 35743 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;

    CREATE TABLE `pedido_status_pedido`  (
        `pedido_status_pedido_id` int(11) NOT NULL AUTO_INCREMENT,
        `pedido_id` int(11) NULL DEFAULT NULL,
        `status_pedido_id` int(11) NULL DEFAULT NULL,
        `psp_notificar_cliente` char(1) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT 'S',
        `psp_texto` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL,
        `excluido` timestamp(0) NULL DEFAULT NULL,
        `cadastrado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
        `atualizado` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
        PRIMARY KEY (`pedido_status_pedido_id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 38216 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;

    CREATE TABLE `status`  (
        `status_id` int(11) NOT NULL AUTO_INCREMENT,
        `sta_nome` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `sta_observacao` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `sta_ordem` int(11) NULL DEFAULT NULL,
        `status_pedido_id` int(11) NULL DEFAULT NULL,
        `excluido` datetime(0) NULL DEFAULT NULL,
        `cadastrado` datetime(0) NULL DEFAULT NULL,
        `atualizado` datetime(0) NULL DEFAULT NULL,
        `sta_cor` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `sta_icon` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `sta_alert` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        PRIMARY KEY (`status_id`) USING BTREE,
        INDEX `idx_1`(`excluido`, `status_id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;

    INSERT INTO `status` VALUES (1, 'Aguardando pagamento', NULL, 1, 1, NULL, NULL, NULL, '#CACACA', 'fa-clock-o', 'alert-warning');
    INSERT INTO `status` VALUES (2, 'Aguardando arquivo', NULL, 2, 2, NULL, NULL, NULL, '#FAC08C', 'fa-file-image-o', 'alert-warning');
    INSERT INTO `status` VALUES (3, 'Arquivo em análise', NULL, 4, 2, NULL, NULL, NULL, '#8CBCFA', 'fa-spinner', 'alert-info');
    INSERT INTO `status` VALUES (4, 'Produção finalizada', NULL, 9, 4, NULL, NULL, NULL, '#DCBCA5', 'check-square-o', 'alert-info');
    INSERT INTO `status` VALUES (5, 'Arquivo com erro', NULL, 5, 2, NULL, NULL, NULL, '#FF8C8C', 'fa-exclamation-circle', 'alert-danger');
    INSERT INTO `status` VALUES (6, 'Em produção', NULL, 7, 3, NULL, NULL, NULL, '#8CBCFA', 'fa-cogs', 'alert-info');
    INSERT INTO `status` VALUES (7, 'Em transporte', NULL, 11, 5, NULL, NULL, NULL, '#DCA5A5', 'fa-truck', 'alert-info');
    INSERT INTO `status` VALUES (8, 'Entregue', NULL, 12, 6, NULL, NULL, NULL, '#5CCE90', 'fa-check-circle-o', 'alert-success');
    INSERT INTO `status` VALUES (9, 'Cancelado', NULL, 13, 7, NULL, NULL, NULL, '#FF7979', 'fa-times-circle-o', 'alert-danger');
    INSERT INTO `status` VALUES (10, 'Pronto para retirada', NULL, 10, 4, NULL, NULL, NULL, '#FFD24D', 'fa-check-circle-o', 'alert-info');
    INSERT INTO `status` VALUES (11, 'Produto com defeito', NULL, 8, 3, NULL, NULL, NULL, '#FF8C8C', 'fa-exclamation-circle', 'alert-danger');
    INSERT INTO `status` VALUES (12, 'Arquivo aprovado', NULL, 6, 20, NULL, NULL, NULL, '#C0ED85', 'fa-check-circle-o', 'alert-info');
    INSERT INTO `status` VALUES (13, 'Em Espera', NULL, 3, 1, NULL, NULL, NULL, '#8CBCFA', 'fa-spinner', 'alert-info');

    CREATE TABLE `status_pedido`  (
        `status_pedido_id` int(11) NOT NULL AUTO_INCREMENT,
        `stp_nome` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `stp_observacao` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `stp_ordem` int(11) NULL DEFAULT NULL,
        `excluido` datetime(0) NULL DEFAULT NULL,
        `cadastrado` datetime(0) NULL DEFAULT NULL,
        `atualizado` datetime(0) NULL DEFAULT NULL,
        `stp_cor` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `stp_icon` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        `stp_alert` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
        PRIMARY KEY (`status_pedido_id`) USING BTREE,
        INDEX `idx_1`(`excluido`, `status_pedido_id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;

    INSERT INTO `status_pedido` VALUES (1, 'Aguardando pagamento', NULL, 1, NULL, NULL, NULL, NULL, NULL, NULL);
    INSERT INTO `status_pedido` VALUES (2, 'Pendente', NULL, 2, NULL, NULL, NULL, NULL, NULL, NULL);
    INSERT INTO `status_pedido` VALUES (3, 'Em produção', NULL, 4, NULL, NULL, NULL, NULL, NULL, NULL);
    INSERT INTO `status_pedido` VALUES (4, 'Pronto', NULL, 5, NULL, NULL, NULL, NULL, NULL, NULL);
    INSERT INTO `status_pedido` VALUES (5, 'Em transporte', NULL, 6, NULL, NULL, NULL, NULL, NULL, NULL);
    INSERT INTO `status_pedido` VALUES (6, 'Entregue', NULL, 7, NULL, NULL, NULL, NULL, NULL, NULL);
    INSERT INTO `status_pedido` VALUES (7, 'Cancelado', NULL, 8, NULL, NULL, NULL, NULL, NULL, NULL);
    INSERT INTO `status_pedido` VALUES (20, 'Aprovado', NULL, 3, NULL, NULL, NULL, NULL, NULL, NULL);

查询本身可以正常工作,但是我需要一种使其更快的方法。如果我将其分解为较小的查询,可能会更好一些?还是我使用了太多不必要的数据?

编辑:

在回答之后,新查询现在看起来像这样:

SELECT s.status_pedido_id AS status_pedido_atual, p.status_pedido_id AS status_pedido_anterior FROM `status` s
    LEFT JOIN pedido p ON p.pedido_id = {$_POST['pedido_id']}
    WHERE s.sta_ordem = 
    (
        SELECT MAX( si.sta_ordem ) AS max_ordem
        FROM pedido_item_status pis
        LEFT JOIN `status` si ON si.status_id = pis.status_id
        WHERE pis.pedido_id = {$_POST['pedido_id']}
        AND pis.excluido IS NULL
        GROUP BY pis.pedido_item_id
        ORDER BY max_ordem ASC
        LIMIT 1
    )

然后,我使用结果在另一个查询中插入订单状态日志。我快多了。我解决的整个过程的逻辑还存在其他问题。

1 个答案:

答案 0 :(得分:0)

好吧,我的猜测是运行EXPLAIN ...并为您的INSERT ...查询运行

+---+----------+----------------------+--+--------+-------------+-------------+--+--+---+--------+----------------------------------------------+
| 1 |  INSERT  | pedido_status_pedido |  |  ALL   |             |             |  |  |   |        |                                              |
+---+----------+----------------------+--+--------+-------------+-------------+--+--+---+--------+----------------------------------------------+
| 2 | SUBQUERY |                      |  |        |             |             |  |  |   |        | no matching row in const table               |
| 3 | SUBQUERY | p1                   |  | ALL    |             |             |  |  | 1 | 100.00 | Using where; Using temporary; Using filesort |
| 3 | SUBQUERY | s                    |  | eq_ref | PRIMARY     | PRIMARY     |  |  | 1 | 100.00 | Using where                                  |
| 3 | SUBQUERY | pedido_item          |  | eq_ref | PRIMARY     | PRIMARY     |  |  | 1 | 100.00 | Using index                                  |
| 3 | SUBQUERY | <derived4>           |  | ref    | <auto_key0> | <auto_key0> |  |  | 2 | 100.00 | Using index                                  |
| 4 | DERIVED  | pi                   |  | ALL    |             |             |  |  | 1 | 100.00 | Using where; Using temporary; Using filesort |
| 4 | DERIVED  | si                   |  | eq_ref | PRIMARY     | PRIMARY     |  |  | 1 | 100.00 |                                              |
+---+----------+----------------------+--+--------+-------------+-------------+--+--+---+--------+----------------------------------------------+

我处理了所有数据,发现(可能)您有7个子查询中的3个,没有可能的搜索关键字

我将开始分解您的查询,并用EXPLAIN检查它们是否存在瓶颈。

否则,我将运行此查询(当然,我已经在表中填充了一些虚假数据),并且执行了0.016秒。真讨厌这就是为什么后端代码和/或数据量/服务器功能的另一个猜测。

所以,最后:

  1. 使用EXPLAIN
  2. 检查查询
  3. 分解复杂的查询,检查内部SELECT可能的瓶颈
  4. 更改逻辑以避免瓶颈-将复杂的查询分解为简单的查询。例如,您可以使用触发器和设置变量来存储MAX( si.sta_ordem ),而无需为每个查询重新计算它。如果您一直在运行服务-您可以将临时表用于缓存。
  5. 如果您的MySQL查询正常,请检查您的后端代码(PHP)。也许您在这里陷入僵局。
  6. 检查数据量。
  7. 如果可能,请重新引导服务器并检查其在负载下的运行情况。性能计数器可用于所有操作系统