2010-10-28 63 views
2

我有一個子查詢問題造成了糟糕的表現......我在想這個子查詢可以使用連接來重寫,但我很難將它包裹起來。表現不佳的Mysql子查詢 - 我可以把它變成一個Join嗎?

查詢的要點是這樣的: 對於EmailAddress和Product的給定組合,我需要得到一個不是最新的ID列表......這些命令將被標記爲「廢棄」 「在表中這將只留下了EmailAddress的和產品的AA給出的組合,最新的秩序......(這是否有意義?)

表定義

CREATE TABLE `sandbox`.`OrderHistoryTable` (
`id` INT(11) NOT NULL AUTO_INCREMENT , 
`EmailAddress` VARCHAR(100) NOT NULL , 
`Product` VARCHAR(100) NOT NULL , 
`OrderDate` DATE NOT NULL , 
`rowlastupdated` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP , 
PRIMARY KEY ( `id`) , 
KEY `EmailAddress` ( `EmailAddress`) , 
KEY `Product` ( `Product`) , 
KEY `OrderDate` ( `OrderDate`) 
) ENGINE = MYISAM DEFAULT CHARSET = latin1; 

查詢

SELECT id 
FROM 
OrderHistoryTable AS EMP1 
WHERE 
OrderDate not in 
    (
    Select max(OrderDate) 
    FROM OrderHistoryTable AS EMP2 
    WHERE 
     EMP1.EmailAddress = EMP2.EmailAddress 
    AND EMP1.Product IN ('ProductA','ProductB','ProductC','ProductD') 
    AND EMP2.Product IN ('ProductA','ProductB','ProductC','ProductD') 
    ) 

重複 'in' 語句中的說明

13 [email protected] ProductA 2010-10-01 
15 [email protected] ProductB 2010-20-02 
46 [email protected] ProductD 2010-20-03 
57 [email protected] ProductC 2010-20-04 
158 [email protected] ProductE 2010-20-05 
206 [email protected] ProductB 2010-20-06 
501 [email protected] ProductZ 2010-20-07 

我查詢的結果應該是 | 13 | | 15 | | 46 | | 57 |

這是因爲,在列出的訂單中,這4個已被相同類別產品的新訂單「取代」。此'類別'包含產品A,B,C & D.

訂單ID 158和501在其各自類別中根據查詢顯示沒有其他訂單。

基於以下關接受的答案的最終查詢: 我結束了使用下面的查詢,沒有子查詢,得到了約3倍的性能(從90秒30秒關閉)。我現在也有一個單獨的「組」表在那裏我可以枚舉,而不是在查詢本身拼寫出來小組成員...

SELECT DISTINCT id, EmailAddress FROM (
    SELECT a.id, a.EmailAddress, a.OrderDate 
    FROM OrderHistoryTable a 
    INNER JOIN OrderHistoryTable b ON a.EmailAddress = b.EmailAddress 
    INNER JOIN groups g1 ON a.Product = g1.Product 
    INNER JOIN groups g2 ON b.Product = g2.Product 
    WHERE 
     g1.family = 'ProductGroupX' 
    AND g2.family = 'ProductGroupX' 
    GROUP BY a.id, a.OrderDate, b.OrderDate 
    HAVING a.OrderDate < MAX(b.OrderDate) 
) dtX 
+0

仍然閱讀我的答案。我發表了一些評論。順便說一句,發佈解釋結果。大約有多少行? – Unreason 2010-11-01 15:27:23

+0

目前有〜900,000行 – 2010-11-01 17:54:59

回答

2

Rant: OMG Ponies的答案給出了你所要求的 - 用連接重寫。但我不會太興奮,你的表現殺手是內部加入電子郵件地址,我認爲,這不是特別有選擇性的,然後你的數據庫需要篩選所有這些行尋找訂單日期的MAX。

這實際上對於MySQL來說意味着要做一個文件(你可以發佈EXPLAIN SELECT ....?)。現在

,如果MySQL曾獲得的索引,其中將包括emailaddressproductorderdate它可能,尤其是在MyISAM的太大在確定MAX(訂購日期)(和沒有更有效的,具有在每個列的索引是不同於在所有列上都有複合索引)。如果我試圖優化這個查詢,我會對此進行打賭。

除此之外咆哮這裏是我的版本的not the latest from a category(我不希望它是美好的,但它是不同的,你應該測試性能,也許會更快,因爲缺少子查詢)

我嘗試(未測試)

SELECT DISTINCT 
    notlatest.id, 
    notlatest.emailaddress, 
    notlatest.product, 
    notlatest.orderdate 
FROM 
    OrderHistoryTable AS notlatest 
    LEFT JOIN OrderHistoryTable AS EMP latest ON 
     notlatest.emailaddress = latest.emailaddress AND 
     notlatest.orderdate < latest.orderdate AND 
WHERE 
    notlatest.product IN ('ProductA','ProductB','ProductC','ProductD') AND 
    latest.product IN ('ProductA','ProductB','ProductC','ProductD') AND 
    latest.id IS NOT NULL 

評論:
- 如果它不會顯示
類別只有一條記錄- 再次指標應加快上述非常

其實這是(可能是)如何標準化數據將提高性能一個很好的例子 - 你的產品意味着產品類別,但產品類別不存儲和IN從長遠來看,測試將無法維持。

此外,通過創建產品類別,您可以直接在索引之上。

如果產品是按類別建立索引的,那麼對類別的聯接性能應該更好,然後測試按值(而不是類別)索引的產品。 (實際上,然後MyISAM的emailaddress,category,orderdate上的複合索引應該已經包含每個類別的最大值,最小值和計數,並且應該是便宜的)。

+0

實際上第二,雖然它不需要是左加入,但它可以INNER JOIN沒有IS NOT NULL條件在latest.id(這應該是更好的MySQL)。 – Unreason 2010-11-01 15:56:55

+0

剛剛嘗試過您的建議,並獲得了比我以前的最佳解決方案提高了2倍的性能...原本90秒現在降至15秒。不過,我還在電子郵件/產品/訂單日期中添加了一個覆蓋索引,這有點幫助,但非常熱門。 – 2010-11-01 17:45:05

+0

另外,我同意100%關於正常化...但現在不在卡片中。也許爲我的未來項目。 – 2010-11-01 17:52:18

5

用途:

SELECT a.id 
    FROM ORDERHISTORYTABLE AS a 
LEFT JOIN (SELECT e.EmailAddress, 
        e.product, 
        MAX(OrderDate) AS max_date 
      FROM OrderHistoryTable AS e 
      WHERE e.Product IN ('ProductA','ProductB','ProductC','ProductD') 
     GROUP BY e.EmailAddress) b ON b.emailaddress = a.emailaddress 
            AND b.max_date = a.orderdate 
            AND b.product = a.product 
    WHERE x.emailaddress IS NULL 
     AND a.Product IN ('ProductA','ProductB','ProductC','ProductD') 
+0

這看起來不錯,但我得到「#1054 - 未知列'emp1.Product'在'where子句'」 – 2010-10-28 17:51:00

+0

@ OMG Ponies:所以我原來的查詢需要大約90秒(但我必須用不同的產品集再次運行它多次)......我只是試着修改後的查詢,並在3分鐘的時間內殺死了進程,因爲它被標記爲** DEAD ** ...有任何想法嗎? – 2010-10-28 18:29:22

+0

@BrianAdkins:我重新在派生表中添加了用於LEFT JOIN的過濾 - 這應該儘可能減少處理量,但我希望能夠整合它。您有單獨的索引 - 使用電子郵件地址,產品和訂單列的覆蓋索引如何? – 2010-10-28 18:51:22

1

我的MySQL是一個位生鏽(我習慣了MSSQL),但這是我最好的猜測。它可能需要在GROUP BYHAVING子句中進行一些調整。另外,我從重複的IN語句中假定您希望產品在兩個表中匹配。如果情況並非如此,我會調整查詢。

SELECT a.id 
FROM OrderHistoryTable a 
INNER JOIN OrderHistoryTable b 
    ON a.Product = b.Product AND 
     a.Employee = b.Employee 
WHERE a.Product IN ('ProductA','ProductB','ProductC','ProductD') 
GROUP BY a.id, a.OrderDate, b.OrderDate, 
HAVING b.OrderDate < MAX(a.OrderDate) 

編輯:刪除無關AND

+0

我能夠得到這個工作一點點調整 – 2010-11-01 14:29:30

0
SELECT * 
FROM (
     SELECT product, MAX(OrderDate) AS md 
     FROM OrderHistoryTable 
     WHERE product IN ('ProductA','ProductB','ProductC','ProductD') 
     GROUP BY 
       product 
     ) ohti 
JOIN orderhistorytable oht 
ON  oht.product = ohti.product 
     AND oht.orderdate <> ohti.md 

此創建於OrderHistoryTable (product, orderdate)的索引快速地工作。

另請注意,它將返回產品中MAX(orderdate)的重複項(如果有)。

相關問題