Выберите запись, только если она имеет более низкое значение, отфильтрованное по месяцам
У меня очень сложный ответ на мой вопрос:
Выберите запись, только если она имеет более низкое значение
около 3 недель назад.
теперь меня беспокоит изменение этого запроса.
так что это окончательная версия этого запроса прямо сейчас:
SELECT a.ID, DATE_FORMAT(a.Time,'%d/%m/%y') AS T, a.SerialNumber,
b.Remain_Toner_Black BeforeCount,
a.Remain_Toner_Black AfterCount
FROM
(
SELECT a.ID,
a.Time,
a.SerialNumber,
a.Remain_Toner_Black,
(
SELECT COUNT(*)
FROM Reports c
WHERE c.SerialNumber = a.SerialNumber AND
c.ID <= a.ID) AS RowNumber
FROM Reports a
) a
LEFT JOIN
(
SELECT a.ID,
a.Time,
a.SerialNumber,
a.Remain_Toner_Black,
(
SELECT COUNT(*)
FROM Reports c
WHERE c.SerialNumber = a.SerialNumber AND
c.ID <= a.ID) AS RowNumber
FROM Reports a
) b ON a.SerialNumber = b.SerialNumber AND
a.RowNumber = b.RowNumber + 1
WHERE b.Remain_Toner_Black < a.Remain_Toner_Black AND b.Remain_Toner_Black >= 0
и для достижения требуется 0,0002 сек.
Я хочу, чтобы отредактировать последнюю строку этого запроса так:
WHERE month(a.Time) = ".$i." AND b.Remain_Toner_Black < a.Remain_Toner_Black AND b.Remain_Toner_Black >= 0
но затем запрос занимает около 6.9047 сек для выполнения.
Как я могу добавить это:
месяц (a.Time) = ". $i."
к запросу наиболее эффективным образом?
В этом случае следующий способ - это, возможно, более быстрый способ сделать основной выбор, чем вы уже используете: -
SELECT AfterSub.ID,
AfterSub.SerialNumber,
BeforeSub.Remain_Toner_Black BeforeCount,
AfterSub.Remain_Toner_Black AfterCount
FROM
(
SELECT ID, SerialNumber, Remain_Toner_Black, @Counter1:=@Counter1+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter1:=0) Sub1
ORDER BY SerialNumber, ID
) AfterSub
INNER JOIN
(
SELECT ID, SerialNumber, Remain_Toner_Black, @Counter2:=@Counter2+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter2:=1) Sub2
ORDER BY SerialNumber, ID
) BeforeSub
ON BeforeSub.SerialNumber = AfterSub.SerialNumber
AND BeforeSub.SeqCnt = AfterSub.SeqCnt
WHERE AfterSub.Remain_Toner_Black > BeforeSub.Remain_Toner_Black
ORDER BY AfterSub.SerialNumber, AfterSub.ID
Проблема с проверкой месяца здесь заключается в том, что следующий элемент может быть в другом месяце, и это полагается на счет.
Вы можете попробовать: -
SELECT AfterSub.ID,
AfterSub.SerialNumber,
BeforeSub.Remain_Toner_Black BeforeCount,
AfterSub.Remain_Toner_Black AfterCount
FROM
(
SELECT ID, SerialNumber, Remain_Toner_Black, @Counter1:=@Counter1+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter1:=0) Sub1
ORDER BY SerialNumber, ID
) AfterSub
INNER JOIN
(
SELECT ID, SerialNumber, Remain_Toner_Black, @Counter2:=@Counter2+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter2:=1) Sub2
ORDER BY SerialNumber, ID
) BeforeSub
ON BeforeSub.SerialNumber = AfterSub.SerialNumber
AND BeforeSub.SeqCnt = AfterSub.SeqCnt
AND AfterSub.Remain_Toner_Black > BeforeSub.Remain_Toner_Black
WHERE month(BeforeSub.Time) = ".$i."
ORDER BY AfterSub.SerialNumber, AfterSub.ID
но это не будет использовать индекс (но количество строк, на которые я надеюсь, будет низким, поэтому я надеюсь, что не проблема).
Вы можете сделать выбор, чтобы получить порядковые номера, а затем только проверить элементы за этот месяц, прежде чем присоединяться к следующему месяцу: -
SELECT AfterSub.ID,
AfterSub.SerialNumber,
BeforeSub.Remain_Toner_Black BeforeCount,
AfterSub.Remain_Toner_Black AfterCount
FROM
(
SELECT ID, SerialNumber, Remain_Toner_Black, @Counter1:=@Counter1+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter1:=0) Sub1
ORDER BY SerialNumber, ID
) AfterSub
INNER JOIN
(
SELECT ID, SerialNumber, Remain_Toner_Black, SeqCnt
FROM
(
SELECT ID, SerialNumber, Remain_Toner_Black, `Time`, @Counter2:=@Counter2+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter2:=1) Sub2
ORDER BY SerialNumber, ID
) BeforeSub
WHERE month(BeforeSub.Time) = ".$i."
) BeforeSub
ON BeforeSub.SerialNumber = AfterSub.SerialNumber
AND BeforeSub.SeqCnt = AfterSub.SeqCnt
AND AfterSub.Remain_Toner_Black > BeforeSub.Remain_Toner_Black
ORDER BY AfterSub.SerialNumber, AfterSub.ID
(обратите внимание, что ни один из последних 2 выборок не тестировался)
ИЗМЕНИТЬ
Добавление проверки на год/месяц на 2 подзапроса. Однако, поскольку дата отформатирована для выполнения этой проверки, я не уверен, что индекс будет полезен: -
SELECT AfterSub.ID,
AfterSub.SerialNumber,
BeforeSub.Remain_Toner_Black BeforeCount,
AfterSub.Remain_Toner_Black AfterCount
FROM
(
SELECT ID, SerialNumber, Remain_Toner_Black, @Counter1:=@Counter1+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter1:=0) Sub1
WHERE DATE_FORMAT(`Time`,'%Y %m') >= '2013 01'
ORDER BY SerialNumber, ID
) AfterSub
INNER JOIN
(
SELECT ID, SerialNumber, Remain_Toner_Black, `Time`, @Counter2:=@Counter2+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter2:=1) Sub2
WHERE DATE_FORMAT(`Time`,'%Y %m') = '2013 01'
ORDER BY SerialNumber, ID
) BeforeSub
ON BeforeSub.SerialNumber = AfterSub.SerialNumber
AND BeforeSub.SeqCnt = AfterSub.SeqCnt
AND AfterSub.Remain_Toner_Black > BeforeSub.Remain_Toner_Black
ORDER BY AfterSub.SerialNumber, AfterSub.ID
Использование даты в подзапросах (что означает разработку последнего дня месяца) может быть более эффективным: -
SELECT AfterSub.ID,
AfterSub.SerialNumber,
BeforeSub.Remain_Toner_Black BeforeCount,
AfterSub.Remain_Toner_Black AfterCount
FROM
(
SELECT ID, SerialNumber, Remain_Toner_Black, @Counter1:=@Counter1+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter1:=0) Sub1
WHERE `Time` >= '2013-01-01'
ORDER BY SerialNumber, ID
) AfterSub
INNER JOIN
(
SELECT ID, SerialNumber, Remain_Toner_Black, `Time`, @Counter2:=@Counter2+1 AS SeqCnt
FROM TableName
CROSS JOIN (SELECT @Counter2:=1) Sub2
WHERE `Time` BETWEEN '2013-01-31' AND '2013-01-31'
ORDER BY SerialNumber, ID
) BeforeSub
ON BeforeSub.SerialNumber = AfterSub.SerialNumber
AND BeforeSub.SeqCnt = AfterSub.SeqCnt
AND AfterSub.Remain_Toner_Black > BeforeSub.Remain_Toner_Black
ORDER BY AfterSub.SerialNumber, AfterSub.ID
Я не уверен, что этот ответ решит вашу проблему или нет, но я думаю, что если вы добавите это условие месяца (a.Time) = ". $i." в обоих подзапросах, то есть в подзапросе "a" и подпросе "b", это может помочь и повысить производительность вашего запроса.
Поместите индекс в A.time и используйте это
SELECT a.ID, DATE_FORMAT(a.Time,'%d/%m/%y') AS T, a.SerialNumber,
b.Remain_Toner_Black BeforeCount,
a.Remain_Toner_Black AfterCount
FROM
(
SELECT a.ID,
a.Time,
a.SerialNumber,
a.Remain_Toner_Black,
month(a.time) as Month
(
SELECT COUNT(*)
FROM Reports c
WHERE c.SerialNumber = a.SerialNumber AND
c.ID <= a.ID) AS RowNumber
FROM Reports a
) a
LEFT JOIN
(
SELECT a.ID,
a.Time,
a.SerialNumber,
a.Remain_Toner_Black,
(
SELECT COUNT(*)
FROM Reports c
WHERE c.SerialNumber = a.SerialNumber AND
c.ID <= a.ID) AS RowNumber
FROM Reports a
) b ON a.SerialNumber = b.SerialNumber AND
a.RowNumber = b.RowNumber + 1
WHERE a.month = ".$i." AND b.Remain_Toner_Black < a.Remain_Toner_Black AND b.Remain_Toner_Black >= 0`