2

这是我的查询:

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A, STOCKENTRY B 
WHERE A.ITEMID = B.ITEMID 
  AND RECORDID = (SELECT MAX(RECORDID) FROM STOCKENTRY 
                  WHERE ITEMID = A.ITEMID) 
  AND A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;

表 B (StockEntry) 可能包含一条或多条记录,而表 A (ItemMaster) 肯定只有该 ItemID 的一行。

如果我删除 WHERE 子句中的子查询,它会显示一行或多行。我觉得通过 WHERE 子句中的子查询选择 max(RecordID) 会减慢查询速度。我在 RecordID、InvoiceDate、ItemID 上确实有索引,但MySQL日志仍然显示此查询执行不佳。由于某种原因,我无法更改列顺序。

有没有更好的方法来优化这个查询?

4

5 回答 5

6

它可能很慢,因为它为外部查询的每一行运行一个相关的子查询。有两种解决方案往往会更有效地运行。

一种是使用派生表,它使用子查询,但它只执行一次子查询来准备派生表。

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A
JOIN STOCKENTRY B ON A.ITEMID = B.ITEMID
JOIN (SELECT ITEMID, MAX(RECORDID) AS MAXRECORDID 
      FROM STOCKENTRY GROUP BY ITEMID) M
  ON (M.ITEMID, M.MAXRECORDID) = (B.ITEMID, B.RECORDID)
WHERE A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;

另一种解决方案是使用排除连接来查找 B 中的行,这样不存在具有相同 itemid 和更大 recordid 的其他行。使用正确的索引(例如,(ITEMID,RECORDID)上的复合索引,这应该会执行得很好。

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A
JOIN STOCKENTRY B ON A.ITEMID = B.ITEMID 
LEFT OUTER JOIN STOCKENTRY B2
  ON B.ITEMID = B2.ITEMID AND B.RECORDID < B2.RECORDID
WHERE B2.ITEMID IS NULL 
  AND A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;

这类问题在 Stack Overflow 上经常出现。我在问题中添加了best-n-per-group标签,以便您查看其他案例。


回复@RPK 的评论:

我自己不使用 MySQL QB,并且该应用程序已经更改了很多次,我无法建议如何使用它。但是在 mysql 监视器(命令行)中,我使用EXPLAINPROFILING的组合来给我统计信息。

但是,您发表了关于不修改(或创建?)索引的评论。这会阻碍你的优化尝试。

于 2011-08-05T15:22:09.860 回答
1

我的建议是创建一个视图

CREATE VIEW `STOCKENTRY_V` AS 
SELECT ITEMID,MAX(RECORDID) AS RECORDID
FROM STOCKENTRY
GROUP BY ITEMID;

您可以对 2 个表 + 视图进行简单连接。我对它的执行速度很感兴趣。

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A, STOCKENTRY B, STOCKENTRY_V C
WHERE A.ITEMID = B.ITEMID AND AND B.ITEMID = C.ITEMID
  AND B.RECORDID = C.RECORDID
  AND A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;
于 2011-08-05T15:37:40.930 回答
1

如果查询被频繁使用并且性能仍然是一个问题,您可以为项目创建一个包含最后一条记录 id 的表,并使用 ITEMMASTER 表上的触发器使其保持最新。

于 2011-08-05T15:56:02.147 回答
1

尝试使用TOP 1 ... ORDER BY .. DESC,像这样:

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A, STOCKENTRY B 
WHERE A.ITEMID = B.ITEMID 
  AND RECORDID = (SELECT top 1 RECORDID FROM STOCKENTRY 
                  WHERE ITEMID = A.ITEMID
                  order by RECORDID desc) 
  AND A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;
于 2011-08-05T15:14:42.147 回答
1

TOP是特定于数据库的;您可能想要使用 MySQL 替代方案ORDER BY ... DESC LIMIT 1

这篇 SO 帖子LIMIT很好地概述了跨数据库实现概念的不同方法。

于 2011-08-05T15:22:25.973 回答