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
- veja-o funcionando ao vivo em um sqlfiddle
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.