【问题标题】:Optimize MySQL Structure and Query优化 MySQL 结构和查询
【发布时间】:2020-03-28 13:30:36
【问题描述】:

问题的变化很大,因为我在 VPN 上而不是 MySQL 服务器的本地网络上,所以问题仍然存在,这个查询太慢了。我的完整查询现在已包含在内,我正在为连接表添加所有布局。

此查询花费的时间太长,它确实返回了一个大型数据集,但限制数据集似乎并没有帮助。我已经尝试过使用和不使用基于日期范围的 WHERE。

仍然需要 8 秒或更长时间才能返回。使用 LIMIT 也没有什么不同。

我担心每个表中的记录可能太多,在这种情况下,我唯一的选择是创建一组“工作”表,其中包含最近几周的发货量,然后将它们移到一组如果查询都会很慢的“历史”表。除非有人能找到一种方法来加快速度。 谢谢。

SELECT 
  ShipmentHeader.RecNbr,
  ShipmentHeader.Void,
  ShipmentHeader.SONum,
  ShipmentHeader.DateofReq,
  ShipmentHeader.ShipToName,
  ShipmentHeader.ShipToCity,
  ShipmentHeader.ShipToState,
  ShipmentHeader.ShipToZip,
  ShipmentHeader.ShipToCountry,
  Carrier.RecNbr AS CarrierRecNbr,
  CarrierService.RecNbr AS CarrierServiceRecNbr,
  ShipmentLabelsPrintLogView.Users 
FROM
  ShipmentHeader 
  INNER JOIN ShipmentLabels 
    ON ShipmentHeader.RecNbr = ShipmentLabels.HeaderRecNbr 
  LEFT JOIN VendorService 
    ON ShipmentLabels.VendorServiceRecNbr = VendorService.RecNbr 
  LEFT JOIN CarrierService 
    ON VendorService.CarrierServiceRecNbr = CarrierService.RecNbr 
  LEFT JOIN Carrier 
    ON CarrierService.CarrierRecNbr = Carrier.RecNbr 
  LEFT JOIN ShipmentLabelsPrintLogView 
    ON ShipmentLabels.RecNbr = ShipmentLabelsPrintLogView.ShipmentLabelsRecNbr 
ORDER BY  ShipmentHeader.RecNbr DESC

以下是表结构:

CREATE TABLE `ShipmentHeader` (
  `RecNbr` int(11) NOT NULL AUTO_INCREMENT,
  `Void` varchar(1) NOT NULL DEFAULT 'N',
  `SONum` varchar(7) NOT NULL,
  `User` varchar(6) NOT NULL,
  `DateofReq` datetime NOT NULL,
  `ShipToName` varchar(255) DEFAULT NULL,
  `ShipToAddress1` varchar(255) DEFAULT NULL,
  `ShipToAddress2` varchar(255) DEFAULT NULL,
  `ShipToAddress3` varchar(255) DEFAULT NULL,
  `ShipToCity` varchar(255) DEFAULT NULL,
  `ShipToState` varchar(255) DEFAULT NULL,
  `ShipToZip` varchar(255) DEFAULT NULL,
  `ShipToCountry` varchar(255) DEFAULT NULL,
  `PackageHeaderRecNbr` int(11) DEFAULT NULL,
  `NegotiatedShipmentCharge` decimal(10,2) DEFAULT NULL,
  `PublishedShipmentCharge` decimal(10,2) DEFAULT NULL,
  `CustomerShipmentCharge` decimal(10,2) DEFAULT NULL,
  `VoidTimeStamp` datetime DEFAULT NULL,
  `VoidUser` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`RecNbr`),
  KEY `SONum` (`SONum`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=539140 DEFAULT CHARSET=utf8



CREATE TABLE `ShipmentLabels` (
  `RecNbr` int(11) NOT NULL AUTO_INCREMENT,
  `Whse` varchar(3) DEFAULT NULL,
  `HeaderRecNbr` int(11) NOT NULL,
  `PackageRecNbr` int(11) NOT NULL,
  `Carrier` varchar(60) NOT NULL DEFAULT '',
  `Service` varchar(60) DEFAULT NULL,
  `Charges` decimal(10,2) NOT NULL,
  `RatedCost` decimal(10,2) NOT NULL,
  `PublishedRate` decimal(10,2) NOT NULL,
  `CustomerCharge` decimal(10,2) NOT NULL,
  `Weight` int(11) NOT NULL,
  `LabelFormat` varchar(30) NOT NULL DEFAULT '',
  `ShipmentID` varchar(60) NOT NULL,
  `TrackingNumber` varchar(60) NOT NULL DEFAULT '',
  `Label` mediumtext NOT NULL COMMENT 'Base64 Encoded',
  `DateofLabel` datetime NOT NULL,
  `LabelSource` varchar(50) DEFAULT NULL,
  `VendorServiceRecNbr` int(11) DEFAULT NULL,
  PRIMARY KEY (`RecNbr`),
  KEY `HeaderRecNbr` (`HeaderRecNbr`) USING BTREE,
  KEY `PackageRecNbr` (`PackageRecNbr`) USING BTREE,
  CONSTRAINT `ShipmentLabels_ibfk_1` FOREIGN KEY (`HeaderRecNbr`) REFERENCES `ShipmentHeader` (`RecNbr`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=542542 DEFAULT CHARSET=utf8

CREATE TABLE `VendorService` (
  `RecNbr` int(11) NOT NULL AUTO_INCREMENT,
  `CarrierServiceRecNbr` int(11) DEFAULT NULL,
  `VendorRecNbr` int(11) DEFAULT NULL,
  `VendorServiceCode` varchar(255) DEFAULT NULL,
  `VendorServiceDesc` varchar(255) DEFAULT NULL,
  `PackageType` varchar(60) DEFAULT NULL,
  `LabelServiceCode` varchar(255) DEFAULT NULL,
  `LabelPackageType` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`RecNbr`),
  UNIQUE KEY `Unique` (`CarrierServiceRecNbr`,`VendorRecNbr`,`PackageType`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=196 DEFAULT CHARSET=utf8mb4;

CREATE TABLE `CarrierService` (
  `RecNbr` int(11) NOT NULL AUTO_INCREMENT,
  `CarrierRecNbr` int(11) DEFAULT NULL,
  `CarrierServiceCode` varchar(50) DEFAULT NULL,
  `CarrierServiceDesc` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`RecNbr`),
  UNIQUE KEY `unique` (`CarrierRecNbr`,`CarrierServiceCode`)
) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=utf8mb4;

CREATE TABLE `Carrier` (
  `RecNbr` int(11) NOT NULL AUTO_INCREMENT,
  `CarrierName` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`RecNbr`),
  UNIQUE KEY `unique` (`CarrierName`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

我正在加入我创建的这个视图......这似乎是问题所在......但它所基于的表不是我需要的数据格式,没有这个视图。

CREATE ALGORITHM=UNDEFINED DEFINER=`atr`@`%` SQL SECURITY DEFINER VIEW `ShipmentLabelsPrintLogView` 
AS 
select `A`.`ShipmentLabelsRecNbr` AS `ShipmentLabelsRecNbr`,
  (
    select group_concat(distinct `ShipmentLabelsPrintLog`.`User` 
      order by `ShipmentLabelsPrintLog`.`User` ASC separator ', ') 
    from `ShipmentLabelsPrintLog` 
    where (`ShipmentLabelsPrintLog`.`ShipmentLabelsRecNbr` = `A`.`ShipmentLabelsRecNbr`)
  ) AS `Users` 
from `ShipmentLabelsPrintLog` `A` 
group by `A`.`ShipmentLabelsRecNbr`

视图所基于的表在这里:

CREATE TABLE `ShipmentLabelsPrintLog` (
  `RecNbr` int(11) NOT NULL AUTO_INCREMENT,
  `ShipmentLabelsRecNbr` int(11) NOT NULL,
  `User` varchar(10) NOT NULL,
  `Workstation` varchar(20) NOT NULL,
  `DateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`RecNbr`)
) ENGINE=InnoDB AUTO_INCREMENT=5908 DEFAULT CHARSET=utf8mb4;

这里是解释:

id  select_type         table                   type    possible_keys  key           key_len  ref                                          rows     Extra                            
1   PRIMARY             ShipmentHeader          index                  PRIMARY       4                                                     1979351                                   
1   PRIMARY             ShipmentLabels          ref     HeaderRecNbr   HeaderRecNbr  4        Shipping.ShipmentHeader.RecNbr               1                                         
1   PRIMARY             VendorService           eq_ref  PRIMARY        PRIMARY       4        Shipping.ShipmentLabels.VendorServiceRecNbr  1        Using where                      
1   PRIMARY             CarrierService          eq_ref  PRIMARY        PRIMARY       4        Shipping.VendorService.CarrierServiceRecNbr  1        Using where                      
1   PRIMARY             Carrier                 eq_ref  PRIMARY        PRIMARY       4        Shipping.CarrierService.CarrierRecNbr        1        Using where; Using index         
1   PRIMARY             <derived2>              ref     key0           key0          5        Shipping.ShipmentLabels.RecNbr               10       Using where                      
2   DERIVED             A                       ALL                                                                                        6576     Using temporary; Using filesort  
3   DEPENDENT SUBQUERY  ShipmentLabelsPrintLog  ALL                                                                                        6576     Using where                      

有没有办法更快地获得相同的结果?

谢谢!

【问题讨论】:

  • ShipmentLabels 中的行大小似乎没有问题; MySQL 只访问HeaderRecNbr 索引,它甚至没有查看基础表中的数据页。 (我们知道,因为解释输出显示“使用索引”,这是 ShipmentLabels 中唯一被引用的列。解释输出还显示 MySQL 正在避免可能昂贵的“使用文件排序”操作,满足 ORDER BY 子句,访问行集群键顺序。在我看来,大部分成本都在ShipmentHeader 表的完整扫描中。
  • 您是真的提取所有这些 500K+ 记录,还是在您的查询中是否有额外的 where 子句过滤记录? 500K 记录是一个巨大的数据集,大概是通过网络传输的千兆字节数据。我怀疑 this 实际上比正确执行查询需要更多时间。
  • 由于您当时只显示 50 条记录,最好使用 LIMIT 和 OFFSET 子句一次仅获取 50 条记录,并且可能在您的用户界面中添加更多过滤器。例如:guru99.com/limit.html。由于您的数据集每天都在增长,因此响应时间只会随着时间的推移而增加。不要获取整个数据库,而是获取小块,然后您可以制定一个索引来优化两个表之间的连接。
  • 还想补充一点,如果我包含类似 WHERE ShipmentHeader.DateofReq BETWEEN CURDATE() - INTERVAL 30 DAY AND CURDATE() 它仍然需要相同的时间,即使它只返回大约 22k 条记录.
  • 即使您减小了结果集的大小,MySQL 仍然必须执行全表扫描才能获取结果,除非它可以使用索引。即使你在这个字段上有一个索引,MySQL 也不能使用它,因为它必须为每条记录评估 curdate。例如:riptutorial.com/mysql/example/23472/…。 Explain 命令将告诉您在处理查询时是否以及何时使用索引。

标签: mysql database query-optimization


【解决方案1】:

ShipmentLabelsPrintLog 需要INDEX(ShipmentLabelsRecNbr, User)

除非你真的需要,否则不要使用LEFT。在您的情况下,它似乎阻止了优化器启动依赖子查询。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-06
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多