选择一条记录,只要它前面的记录值太长而失败

时间:2014-06-04 11:14:08

标签: mysql sql phpmyadmin

我已经问了两个关于这个问题的问题,并且对这两个问题都有很好的答案。

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

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

  3. (问题2目前与主题无关,因为我不需要按月过滤)

    现在我已经实现了向我建议的代码,只要我的表继续增长sql查询 花了更长的时间。在一开始它需要8秒才能在1000行上执行。 现在在一个超过25,000行的表上,它就失败了。

    您可以在此处观看我的查询 - http://sqlfiddle.com/#!2/5c480/1/0

    SELECT  a.ID, DATE_FORMAT(a.Time,'%d/%m/%y') AS T, a.SerialNumber, p.Model,
        b.Remain_Toner_Black BeforeCountBlack,
        a.Remain_Toner_Black AfterCountBlack,
        b.Remain_Toner_Cyan BeforeCountCyan,
        a.Remain_Toner_Cyan AfterCountCyan,
    b.Remain_Toner_Magenta BeforeCountMagenta,
        a.Remain_Toner_Magenta AfterCountMagenta,
    b.Remain_Toner_Yellow BeforeCountYellow,
        a.Remain_Toner_Yellow AfterCountYellow
    FROM    
        (
            SELECT  a.ID, 
                    a.Time, 
                    a.SerialNumber, 
                    a.Remain_Toner_Black,
                    a.Remain_Toner_Cyan,
                    a.Remain_Toner_Magenta,
                    a.Remain_Toner_Yellow,
                    (
                        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,
                    a.Remain_Toner_Cyan,
                    a.Remain_Toner_Magenta,
                    a.Remain_Toner_Yellow,
                    (
                        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
    INNER JOIN Printers p ON a.SerialNumber = p.SerialNumber
    INNER JOIN Customers c ON p.IP = c.IP AND c.Company = 5
    WHERE   (b.Remain_Toner_Black < a.Remain_Toner_Black AND b.Remain_Toner_Black >= 0) OR (b.Remain_Toner_Cyan < a.Remain_Toner_Cyan AND b.Remain_Toner_Cyan >= 0) OR (b.Remain_Toner_Magenta < a.Remain_Toner_Magenta AND b.Remain_Toner_Magenta >= 0) OR (b.Remain_Toner_Yellow < a.Remain_Toner_Yellow AND b.Remain_Toner_Yellow >= 0)
    

    我需要使用以下3个表格才能选择属于具有ID的特定公司的打印机。

    报告:

    ID         SerialNumber         Remain_Toner_Black   
    29881      Z30PBAHBB00034E      58   
    30001      Z30PBAHBB00034E      98
    30200      Z30PBAHBB00034E      70
    30205      BVCfdgdfgdf329F      50
    30207      BVCfdgdfgdf329F      40
    30210      Z30PBAHBB00034E      50
    30301      Z30PBAHBB00034E      100
    

    打印机:

    IP                 SerialNumber         Customer 
    80.179.228.81      Z30PBAHBB00034E      52
    

    客户:

    ID         IP                      Company        
    52         80.179.228.81           5
    

    我的查询完美无缺,并返回:

    ID         SerialNumber            BEFORECOUNTBLACK         AFTERCOUNTBLACK        
    30001      Z30PBAHBB00034E         58                       98
    30301      Z30PBAHBB00034E         50                       100
    

    但是,当我在Reports表格中的25,000行的桌子上运行它时,它现在失败了。

1 个答案:

答案 0 :(得分:3)

这是问题1的解决方案,它运行得更快,因为您有许多全表扫描和从属子查询。在这里,您最多只能进行一次表扫描(也许是一个临时表,具体取决于您的数据有多大以及您有多少内存)。我想你可以在这里轻松调整它。问题2(我还没读过)可能也回答了,因为现在只需添加where date_column = whatever

select * from (
    select
    t.*,
    if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,
    @prev_sn := SerialNumber,
    @prev_toner := Remain_Toner_Black
    from
    Table1 t
    , (select @prev_toner:=0, @prev_sn:=SerialNumber from Table1 order by SerialNumber limit 1) var_init
    order by SerialNumber, id
) sq  
where select_it = 1

编辑:

说明:

使用此行

    , (select @prev_toner:=0, @prev_sn:=SerialNumber from Table1 order by SerialNumber 

我们只是动态初始化变量@prev_toner@prev_sn。它与在查询中没有此行但在查询前面写

相同
SET @prev_toner = 0;
SET @prev_sn = (select serialnumber from your_table order by serialnumber limit 1);
SELECT ...

那么,为什么查询要为@prev_sn赋值以及为什么按serialnumber排序?订单非常重要。没有订单,没有保证订单返回行。此外,我们将使用变量访问前面的行值,因此将相同的序列号组合在一起并且#34;重要。

select子句中的列是一个接一个地进行评估,因此首先选择此行非常重要

if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,

在选择这两行之前

@prev_sn := SerialNumber,
@prev_toner := Remain_Toner_Black

为什么?最后两行仅将当前行的值分配给变量。因此,这一行

if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,

变量仍保留前一行的值。我们在这里做的只不过是说&#34;如果列Remain_Toner_Black中的前一行值小于当前行中的那一行,前一行的序列号与实际相同行序列号,返回1,否则返回0.&#34;

然后我们可以简单地在外部查询中说出&#34;选择每一行,上面返回1&#34;。

根据您的查询,您不需要所有这些子查询。它们非常昂贵且不必要。实际上它太疯狂了。在查询的这一部分

    SELECT  a.ID, 
            a.Time, 
            a.SerialNumber, 
            a.Remain_Toner_Black,
            a.Remain_Toner_Cyan,
            a.Remain_Toner_Magenta,
            a.Remain_Toner_Yellow,
            (
                SELECT  COUNT(*)
                FROM    Reports c
                WHERE   c.SerialNumber = a.SerialNumber AND
                        c.ID <= a.ID) AS RowNumber
    FROM    Reports a

您选择整个表格每行,您可以计算该组中的行数。这是一个从属子查询。所有只是为了得到某种行号。然后你第二次这样做,这样你就可以加入这两个临时表来获取前一行。真的,难怪表演太可怕了。

那么,如何调整我的查询解决方案?而不是我用来获取Remain_Toner_Black的前一行的一个变量,使用四个颜色为黑色,青色,品红色和黄色。只需像您一样加入Printers and Customers表。别忘了订单,你已经完成了。