我有一个导致性能不佳的子查询问题......我认为可以使用连接重写子查询,但是我很难绕过它。
查询的要点是这样的: 对于EmailAddress和Product的给定组合,我需要获得一个不是最新的ID列表....这些订单将在表格中标记为“过时”,这将只留下aa的最新订单给出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
答案 0 :(得分: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')
答案 1 :(得分:2)
<强>咆哮:强> OMG小马的答案给出了你所要求的 - 用连接重写。但我不会太兴奋,你的性能杀手是电子邮件地址的内部联接,我认为,这不是特别选择性的,然后你的数据库需要筛选所有那些寻找订单日期MAX的行。 / p>
这对于MySQL来说实际上意味着要做一个文件排序(你可以发布EXPLAIN SELECT ....?)。
现在,如果mysql可以访问包含emailaddress
,product
和orderdate
的索引,特别是在MyISAM上,可以更有效地确定MAX(orderdate)(和不,在每个列上都有一个索引与在所有列上都有一个复合索引不同。如果我试图优化该查询,我会打赌。
除了这个咆哮之外,这是我not the latest from a category
的版本(我不认为它会更好,但它不同,你应该测试性能;由于缺少子查询,它可能会更快)< / p>
我的尝试(未经测试)
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
上的综合索引应该已经包含每个类别的最大值,最小值和数量,而且应该很便宜。
答案 2 :(得分:1)
我的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
。
答案 3 :(得分: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)
的重复项(如果有)。