簡體   English   中英

優化MySQL查詢以避免“使用臨時”和“使用文件排序”

[英]Optimizing a MySQL query to avoid “Using temporary” and “Using filesort”

我知道那里有一千個類似的問題,但是我已經花了兩個星期的時間來為該查詢找到一個真正的解決方案。

該查詢來自銷售點程序。 此查詢與用戶選擇他需要的報告(期間,總計等)以及是否要分組結果的表單有關。

這很不方便。 該查詢是在VB.NET中通過代碼生成的,並且會根據用戶的選擇(不同的總計,期間,組等)而有所不同,因此有了該問題的解決方案,我應該可以繼續“創建”以代碼形式對所有其他查詢進行編碼。

在這種情況下,此查詢是按族分組的總計查詢。

大部分時間(> 99%)都浪費在SENDING DATA中(顯示查詢#的配置文件)

下表如下:

CREATE TABLE `product` (
  `idProduct` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `idFamily` tinyint(3) unsigned DEFAULT NULL,
  `Codigo` char(10) NOT NULL,
  `Nombre` char(70) DEFAULT NULL COMMENT 'Nombre corto',
-- five more integer columns
  PRIMARY KEY (`idProduct`),
  KEY `fk_p_idFamily` (`idFamily`),
  CONSTRAINT `fk_p_idFamily` FOREIGN KEY (`idFamily`) REFERENCES `family`  (`idFamily`),
) ENGINE=InnoDB AUTO_INCREMENT=19420 DEFAULT CHARSET=latin1 PACK_KEYS=0;

CREATE TABLE `family` (
  `idFamily` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
  `Nombre` char(30) NOT NULL,
  `Descripcion` char(255) DEFAULT NULL,
  `Borrado` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`idFamily`)
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=latin1 PACK_KEYS=0

CREATE TABLE `document` (
`idDocument` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
`Nombre` char(25) NOT NULL,
`Descripcion` char(100) DEFAULT NULL,
`Borrado` tinyint(1) NOT NULL DEFAULT '0',
`NoComputa` tinyint(1) NOT NULL DEFAULT '0',
  `Rectifica` tinyint(1) NOT NULL DEFAULT '0',
  `CalculoSumatorioPVP` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`idDocument`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1 PACK_KEYS=0

CREATE TABLE `soldproduct` (
  `idProduct` smallint(5) unsigned NOT NULL,
  `idSale` int(10) unsigned NOT NULL,
  `PrecioCompra` decimal(7,2) NOT NULL ,
  `PrecioVenta` decimal(7,2) NOT NULL ,
  `DtoProd` decimal(7,4) DEFAULT NULL ,
  `BrutoUd` decimal(7,2) NOT NULL ,
  `PVPUd` decimal(7,2) NOT NULL ,
  `Cantidad` decimal(9,3) DEFAULT NULL ,
  PRIMARY KEY (`idProduct`,`idSale`),
  KEY `fk_pv_idSale` (`idSale`),
  CONSTRAINT `fk_pv_idProduct` FOREIGN KEY (`idProduct`) REFERENCES `product` (`idProduct`),
  CONSTRAINT `fk_pv_idSale` FOREIGN KEY (`idSale`) REFERENCES `sales` (`idSale`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 PACK_KEYS=0

CREATE TABLE `sales` (
  `idSale` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `idDocument` tinyint(3) unsigned NOT NULL,
  `idEstadoVenta` tinyint(3) unsigned NOT NULL,
  `idCliente` smallint(5) unsigned NOT NULL,
  `idFormaPago` tinyint(3) unsigned NOT NULL,
  `idEmpleado` tinyint(3) unsigned NOT NULL ,
  `idTienda` tinyint(3) unsigned DEFAULT NULL,
  `idTipoVenta` tinyint(3) unsigned NOT NULL,
  `FechaVenta` datetime DEFAULT NULL COMMENT 'Fecha de Venta',
  `PrecioCompraTotal` decimal(10,2) DEFAULT NULL,
  `IVA` decimal(7,4) DEFAULT NULL,
  -- ten more decimal columns
  -- five more datetime columns
  -- ten more char columns
  `Borrado` tinyint(1) NOT NULL DEFAULT '0' ,
  `Historia` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`idVenta`),
  KEY `fk_v_idTienda` (`idTienda`),
  KEY `fk_v_idCliente` (`idCliente`),
  KEY `fk_v_idEmpleado` (`idEmpleado`),
  KEY `fk_v_idTipoVenta` (`idTipoVenta`),
  KEY `fk_v_idFormaPago` (`idFormaPago`),
  KEY `fk_v_idDocument` (`idDocument`),
  KEY `fk_v_idEstadoVenta` (`idEstadoVenta`),
  KEY `idx_v_FechaVenta` (`FechaVenta`),
  CONSTRAINT `fk_v_idCliente` FOREIGN KEY (`idCliente`) REFERENCES `cliente` (`idCliente`),
  CONSTRAINT `fk_v_idDocument` FOREIGN KEY (`idDocument`) REFERENCES `document` (`idDocument`),
  CONSTRAINT `fk_v_idEmpleado` FOREIGN KEY (`idEmpleado`) REFERENCES `empleado` (`idEmpleado`),
  CONSTRAINT `fk_v_idEstadoVenta` FOREIGN KEY (`idEstadoVenta`) REFERENCES `estadoventa` (`idEstadoVenta`),
  CONSTRAINT `fk_v_idFormaPago` FOREIGN KEY (`idFormaPago`) REFERENCES `formapago` (`idFormaPago`),
  CONSTRAINT `fk_v_idTienda` FOREIGN KEY (`idTienda`) REFERENCES `tienda` (`idTienda`),
  CONSTRAINT `fk_v_idTipoVenta` FOREIGN KEY (`idTipoVenta`) REFERENCES `tipoventa` (`idTipoVenta`)
) ENGINE=InnoDB AUTO_INCREMENT=101770 DEFAULT CHARSET=latin1 PACK_KEYS=0

查詢是這樣的:

SELECT  f.Nombre  ,SUM(sp.PrecioVenta*sp.Cantidad)  
FROM soldproduct sp, sales s, document doc, family f, product p 
WHERE s.idDocument = doc.idDocument AND doc.NoComputa = FALSE
AND p.idProduct = sp.idProduct AND sp.idSale = s.idSale 
AND p.idFamily = f.idFamily AND p.Borrado = FALSE 
AND s.Borrado = FALSE AND s.Historia = FALSE AND s.idTienda = 1 
AND s.FechaVenta BETWEEN '2013-01-01' AND '2014-01-01' GROUP BY f.idFamily;

我也嘗試過此操作(如果有責任,我也刪除了文檔表)

SELECT ProductFamily.Nombre, SUM(sp.PrecioVenta*sp.Cantidad) 
FROM 
(SELECT idSale FROM sales WHERE Borrado = FALSE AND Historia = FALSE AND idTienda = 1 
AND FechaVenta BETWEEN '2013-01-01' AND '2014-01-01') SalesidSale
JOIN
soldproduct sp
ON sp.idSale = SalesidSale.idSale
JOIN
(SELECT p.idProduct, p.idFamily, f.Nombre FROM product p, family f WHERE 
p.idFamily = f.idFamily AND p.Borrado = FALSE) ProductFamily
ON ProductFamily.idProduct = sp.idProduct
GROUP BY ProductFamily.idFamily;

它花費的時間非常長,並且它向EXPLAIN命令的輸出是(第一個查詢):

id  select_type table   type    possible_keys                                               key                 key_len ref                     rows    Extra
1   SIMPLE      v       range   PRIMARY,fk_v_idTienda,fk_v_idDocument,idx_v_FechaVenta      idx_v_FechaVenta    6       NULL                    7387    "Using index condition; Using where; Using MRR; Using temporary; Using filesort"
1   SIMPLE      doc     ALL     PRIMARY                                                     NULL                NULL    NULL                    4       "Using where; Using join buffer (Block Nested Loop)"
1   SIMPLE      pv      ref     PRIMARY,fk_pv_idSale                                        fk_pv_idSale        4       gemalia.s.idSale        4       NULL
1   SIMPLE      p       eq_ref  PRIMARY,fk_p_idFamily                                       PRIMARY             2       gemalia.sp.idProduct    1       "Using where"
1   SIMPLE      f       eq_ref  PRIMARY                                                     PRIMARY             1       gemalia.p.idFamily      1       NULL

我希望有人能幫助我,我嘗試創建索引,子查詢等。但是我不能低於40秒,這太長了,我確定自己做錯了。

每張表的行數大約為:銷售量:100,000產品:20,000家庭:35售出產品:1,100,000文檔:4

非常感謝。

試試這個查詢,在其中我們試圖利用短路。

編輯

 SELECT  
   f.Nombre, 
   SUM(sp.PrecioVenta*sp.Cantidad)  
 FROM 
   soldproduct sp
 INNER JOIN 
   sales s
 ON 
   (s.idTienda = 1 AND AND 
   s.Borrado = FALSE AND 
   s.Historia = FALSE AND 
   sp.idSale = s.idSale)
 INNER JOIN
   document doc
 ON
   (doc.NoComputa = FALSE AND
   s.idDocument = doc.idDocument) 
 INNER JOIN
   family f
 ON
   (p.idFamily = f.idFamily) 
 INNER JOIN
   product p 
 ON
   (p.Borrado = FALSE AND         
   p.idProduct = sp.idProduct)
 WHERE 
   s.FechaVenta BETWEEN '2013-01-01' AND '2014-01-01' 
 GROUP BY 
   f.idFamily;

暫無
暫無

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

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