phpMyAdmin
 sql >> Base de Dados >  >> Database Tools >> phpMyAdmin

Selecione um registro apenas se o anterior tiver um valor menor demorar muito e falhar


Aqui está uma solução para sua pergunta 1 que será executada muito mais rápido, pois você tem muitas varreduras de tabela completas e subconsultas dependentes. Aqui você terá no máximo apenas uma varredura de tabela (e talvez uma tabela temporária, dependendo do tamanho dos seus dados e quanta memória você tem). Acho que você pode facilmente ajustá-lo à sua pergunta aqui. A pergunta 2 (eu não li realmente) provavelmente também foi respondida, pois agora é fácil adicionar 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

EDITAR:

Explicação:

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

nós apenas inicializamos as variáveis ​​@prev_toner e @prev_sn no vôo. É o mesmo que não ter esta linha na consulta, mas escrever na frente da consulta
SET @prev_toner = 0;
SET @prev_sn = (select serialnumber from your_table order by serialnumber limit 1);
SELECT ...

Então, por que fazer a consulta para atribuir um valor a @prev_sn e por que ordenar por número de série? A ordem por é muito importante. Sem uma ordem por não há ordem garantida na qual as linhas são retornadas. Também acessaremos o valor das linhas anteriores com variáveis, por isso é importante que os mesmos números de série sejam "agrupados".

As colunas na cláusula select são avaliadas uma após a outra, por isso é importante que você primeiro selecione esta linha
if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,

antes de selecionar essas duas linhas
@prev_sn := SerialNumber,
@prev_toner := Remain_Toner_Black

Por que é que? As duas últimas linhas atribuem apenas os valores das linhas atuais às variáveis. Por isso nesta linha
if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,

as variáveis ​​ainda mantêm os valores das linhas anteriores. E o que fazemos aqui nada mais é do que dizer "se o valor das linhas anteriores na coluna Remain_Toner_Black for menor que o da linha atual e o número de série das linhas anteriores é o mesmo que o número de série das linhas reais, retorne 1, senão retorne 0."

Então podemos simplesmente dizer na consulta externa "selecione todas as linhas, onde o acima retornou 1".

Dada sua consulta, você não precisa de todas essas subconsultas. São muito caros e desnecessários. Na verdade é bem louco. Nesta parte da consulta
    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

você seleciona a tabela inteira e para cada linha você conta as linhas dentro desse grupo. Essa é uma subconsulta dependente. Tudo apenas para ter algum tipo de número de linha. Então você faz isso uma segunda vez, apenas para poder juntar essas duas tabelas temporárias para obter a linha anterior. Realmente, não é à toa que o desempenho é horrível.

Então, como ajustar minha solução à sua consulta? Em vez de uma variável que usei para obter a linha anterior para Remain_Toner_Black, use quatro para as cores preto, ciano, magenta e amarelo. E é só entrar na tabela de Impressoras e Clientes como já fez. Não se esqueça do pedido e pronto.