[英]Poorly performing Mysql subquery — can I turn it into a Join?
我有一個導致性能不佳的子查詢問題......我認為子查詢可以使用連接重寫,但我很難繞過它。
查詢的要點是這樣的:對於給定的EmailAddress和Product的組合,我需要得到一個不是最新的ID列表....這些訂單將在表格中標記為“過時”只留下給定的EmailAddress和Product組合的最新訂單......(這有意義嗎?)
表定義
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 bob@aol.com ProductA 2010-10-01
15 bob@aol.com ProductB 2010-20-02
46 bob@aol.com ProductD 2010-20-03
57 bob@aol.com ProductC 2010-20-04
158 bob@aol.com ProductE 2010-20-05
206 bob@aol.com ProductB 2010-20-06
501 bob@aol.com 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
采用:
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')
Rant: OMG小馬的答案給出了你要求的東西 - 用連接重寫。 但我不會太興奮,你的性能殺手是電子郵件地址的內部聯接,我認為,這不是特別選擇性的,然后你的數據庫需要篩選所有那些尋找訂單日期最大值的行。
這對於MySQL來說實際上意味着要做一個文件排序(你可以發布EXPLAIN SELECT ....?)。
現在,如果mysql可以訪問包含emailaddress
, product
和orderdate
的索引,特別是在MyISAM上可以更有效地確定MAX(orderdate)(並且不會,每個列上的索引都不同於在所有列上都有一個復合索引。 如果我試圖優化該查詢,我會打賭。
除了這個咆哮之外,我的版本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
的綜合索引應該已經包含每個類別的最大,最小和計數,這應該是便宜的)。
我的MySQL有點生疏(我已經習慣了MSSQL),但這是我最好的猜測。 它可能需要在GROUP BY
和HAVING
子句中進行一些調整。 此外,我從您的重復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
。
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)
上創建一個索引OrderHistoryTable (product, orderdate)
以便快速工作。
另請注意,如果有的話,它將返回產品中MAX(orderdate)
重復項。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.