簡體   English   中英

在多個表上查詢太大又慢,如何優化呢?

[英]Query too big and slow on multiple tables, how to optimize that?

我有一個帶有訂單的商店系統,該系統具有訂單及其物品的狀態。 我有以下六個表:

訂購( pedido )。

訂單商品( pedido_item )。

訂單狀態( status_pedido )。

訂單商品狀態( status )。

訂單狀態日志( pedido_status_pedido )。

訂單商品狀態日志( pedido_item_status )。

因此,當我必須從訂單中更新狀態時,我在pedido_status_pedido上插入新行,並在表pedido上更新status_pedido_id 物品也一樣。

一個訂單狀態具有許多關聯的項目狀態,例如,訂單狀態“待處理”與項目狀態“正在等待文件”,“有錯誤的文件”和“已批准文件”相關。

當前訂單狀態基於其當前物料狀態,例如,最“延遲”的狀態,而物料“有錯誤的文件”位於“成品生產”物料的后面。 為此,在訂單和商品狀態上都有列列。

因此,如果我的訂單上有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
    )

然后,我使用結果在另一個查詢中插入訂單狀態日志。 我快多了。 我解決的整個過程的邏輯還存在其他問題。

好吧,我的猜測是運行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您的查詢,並使用EXPLAIN檢查它們是否存在瓶頸。

否則,我將運行此查詢(當然,我已經在表中填充了一些虛假數據),並且執行了0.016秒。 真討厭 這就是為什么另一個猜測是在后端代碼和/或數據量/服務器功能方面

因此,最后:

  1. 使用EXPLAIN檢查查詢
  2. 分解您的復雜查詢,檢查內部SELECT可能的瓶頸
  3. 更改邏輯以避免瓶頸-將復雜的查詢分解為簡單的查詢。 例如,您可以使用觸發器和設置變量來存儲MAX( si.sta_ordem )而無需為每個查詢重新計算它。 如果您一直在運行服務-您可以將臨時表用於緩存。
  4. 如果您的MySQL查詢正常,請檢查您的后端代碼(PHP)。 也許您在這里陷入僵局。
  5. 檢查您的數據量。
  6. 如果可能,請重新引導服務器並檢查其在負載下的運行情況。 性能計數器可用於所有操作系統

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM