Skip to content Skip to sidebar Skip to footer

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.

Solution 1:

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

Solution 2:

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 i.e. sub query 'a' and sub query 'b', it might help and improve the performance of your query.


Solution 3:

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`

Post a Comment for "Select A Record Just If The One Before It Has A Lower Value Filtered By Month"