简体   繁体   中英

Select a record just if the one before it has a lower value filtered by month

I've got a very complicated answer about my question here:

Select a record just if the one before it has a lower value

about 3 weeks ago.

now I'm troubled with altering this query.

so this is the final version of this query right now:

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

and it takes about 0.0002 sec to accomplish.

what I want is to edit the last line of this query so it would be:

WHERE month(a.Time) = ".$i." AND b.Remain_Toner_Black < a.Remain_Toner_Black AND b.Remain_Toner_Black >= 0

but then, the query takes about 6.9047 sec to accomplish.

How can I add this: month(a.Time) = ".$i." to the query in the most time efficient way?

Looking into this, the following way is possibly a quicker way of doing the basic select than you are already using:-

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

The problem with checking the month here is that the following item could be in a different month, and this is relying on a count.

You could try:-

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

but this won't use an index (but the number of rows I would hope is low so I would hope not an issue).

You could possibly do the select to get the sequence numbers, then only check the items for that month, before joining to the next month:-

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

(note, neither of the last 2 selects are tested)

EDIT

Adding a check for year / month to the 2 subselects. However as the date is being formatted to do this check I am not sure the index will be useful:-

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

Using a date in the subselects (which means working out the last day of the month) might be more efficient:-

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

Please place index on A.time and use this

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`

I am not sure that whether this answer will solve your problem or not, but i think if you append this condition month(a.Time) = ".$i." in both sub queries ie sub query 'a' and sub query 'b', it might help and improve the performance of your query.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM