2013-04-04 126 views
0

我知道這裏有上千個類似的問題,但我已經花了兩週的時間來爲這個查詢找到真正的解決方案。優化MySQL查詢以避免「使用臨時」和「使用filesort」

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

這很不方便。查詢是在VB.NET中生成的,我的意思是,通過代碼生成,它會根據用戶的選擇(不同總計,期間,組等)而變化,所以通過解決這個問題,我應該能夠繼續「創建「通過編碼表單的所有其他查詢。

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

大多數時候(> 99%)在發送數據(查詢#顯示輪廓)是浪費

的表如下所示:

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; 

所花費的時間是非常大的,並且它的輸出解釋命令(第一查詢):

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

非常感謝。

+0

試着用'WHERE'在第一個查詢..前嘗試把這些條件'p.Borrado條款= FALSE AND s.Borrado =虛假和s.Historia打=聯合條件之前首先= FALSE AND s.idTienda = 1'。 – Meherzad 2013-04-04 09:32:58

+0

嗨Meherzad,你說第一個查詢或第二個查詢? – h2ohgh2o 2013-04-04 09:37:25

+0

我也試着做了沒有聚合函數和GROUP BY CLAUSE的查詢。查詢時間是,但問題是服務器不得不發送數據(這是真正的問題),然後,我將不得不通過代碼「總和」它們(問題少)。我收到了62458行。查詢結果:持續時間(4.366秒)取(174.852秒)。我在「show profile output」中看到了一些奇怪的東西:輸出是一串「'等待查詢緩存鎖定'」(0秒)和「發送數據」(2或3秒),最後還有最後的「發送數據」爲40秒。可能是關於服務器配置的東西? – h2ohgh2o 2013-04-04 10:49:04

回答

1

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

編輯

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; 
+0

謝謝,**但它不起作用**。看起來,在MySQL 5.6.10(我正在處理的版本)中,順序無關緊要。 EXPLAIN輸出是一樣的。 「發送數據」時間正在浪費>總時間的99%。 – h2ohgh2o 2013-04-04 10:37:10

+0

檢查編輯現在它應該工作... – Meherzad 2013-04-04 11:00:57

+0

我檢查了它,它並沒有改善。在更改my.ini文件後,我現在在10秒鐘內。 – h2ohgh2o 2013-04-04 11:44:49

相關問題