Выберите запись, только если она имеет более низкое значение, отфильтрованное по месяцам

57
7

У меня очень сложный ответ на мой вопрос:


Выберите запись, только если она имеет более низкое значение


около 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."
к запросу наиболее эффективным образом?

спросил(а) 2013-10-03T15:01:00+04:00 6 лет, 11 месяцев назад
1
Решение
58

В этом случае следующий способ - это, возможно, более быстрый способ сделать основной выбор, чем вы уже используете: -


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

ответил(а) 2013-10-03T15:37:00+04:00 6 лет, 11 месяцев назад
40

Я не уверен, что этот ответ решит вашу проблему или нет, но я думаю, что если вы добавите это условие месяца (a.Time) = ". $i." в обоих подзапросах, то есть в подзапросе "a" и подпросе "b", это может помочь и повысить производительность вашего запроса.

ответил(а) 2013-10-03T15:14:00+04:00 6 лет, 11 месяцев назад
41

Поместите индекс в 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`

ответил(а) 2013-10-03T15:11:00+04:00 6 лет, 11 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема