Оптимизирующий запрос.Хотите выбрать последнюю запись, не используя max в подзапросе - PullRequest
2 голосов
/ 05 августа 2011

Это мой запрос:

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, он отображает одну или несколько строк.Я чувствую, что выбор max (RecordID) через подзапрос в предложении WHERE замедляет запрос.У меня есть индексы на RecordID, InvoiceDate, ItemID, но все равно MySQL журналы показывают, что этот запрос не работает должным образом.Я по какой-то причине не могу изменить последовательность столбцов .

Есть ли лучший способ оптимизировать этот запрос?

Ответы [ 5 ]

6 голосов
/ 05 августа 2011

Скорее всего, он медленный, потому что он выполняет коррелированный подзапрос для каждой строки внешнего запроса.Есть два решения, которые имеют тенденцию работать более эффективно.

Одним из них является использование производной таблицы , которая использует подзапрос, но выполняет подзапрос только один раз для подготовки производной таблицы.

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;

Этот тип проблем часто возникает при переполнении стека. Я добавил наибольшее-n-per-group тег к вопросу, чтобы вы могли видеть другие случаи.


Комментарий Re @ RPK:

Я сам не использую MySQL QB, иэто приложение менялось так много раз, что я не могу посоветовать, как его использовать, но в мониторе mysql (командной строке) я использую комбинацию EXPLAIN и PROFILING длядайте мне статистику.

Тем не менее, вы сделали комментарий о том, что не нужно изменять (или создавать?) индексы. Это помешает вашим попыткам оптимизировать.

1 голос
/ 05 августа 2011

Если запрос часто используется, а производительность по-прежнему остается проблемой, вы можете создать таблицу идентификатора последней записи для элемента и поддерживать его в актуальном состоянии с помощью триггера в таблице ITEMMASTER.

1 голос
/ 05 августа 2011

Мое предложение - создать представление

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;
1 голос
/ 05 августа 2011

TOP зависит от базы данных; Вы можете использовать альтернативу MySQL ORDER BY ... DESC LIMIT 1.

Этот пост SO содержит хороший обзор различных способов реализации концепции LIMIT в базах данных.

1 голос
/ 05 августа 2011

попробуйте использовать 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;
...