sql >> Base de Datos >  >> Database Tools >> phpMyAdmin

Seleccione un registro solo si el anterior tiene un valor más bajo tarda demasiado y falla

Aquí hay una solución para su pregunta 1 que se ejecutará mucho más rápido, ya que tiene muchos escaneos de tablas completos y subconsultas dependientes. Aquí, como máximo, tendrá solo un escaneo de tabla (y tal vez una tabla temporal, dependiendo del tamaño de sus datos y de la cantidad de memoria que tenga). Creo que puede ajustarlo fácilmente a su pregunta aquí. La pregunta 2 (realmente no la he leído) probablemente también se responda, ya que ahora es fácil simplemente agregar 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:

Explicación:

Con esta línea

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

simplemente inicializamos las variables @prev_toner y @prev_sn sobre la marcha. Es lo mismo que no tener esta línea en la consulta pero escribir delante de la consulta

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

Entonces, ¿por qué hacer la consulta para asignar un valor a @prev_sn y por qué ordenar por número de serie? El orden por es muy importante. Sin un pedido por no hay un orden garantizado en el que se devuelvan las filas. También accederemos al valor de las filas anteriores con variables, por lo que es importante que los mismos números de serie estén "agrupados".

Las columnas en la cláusula de selección se evalúan una tras otra, por lo que es importante que primero seleccione esta línea

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

antes de seleccionar estas dos líneas

@prev_sn := SerialNumber,
@prev_toner := Remain_Toner_Black

¿Porqué es eso? Las dos últimas líneas asignan solo los valores de las filas actuales a las variables. Por lo tanto en esta línea

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

las variables aún mantienen los valores de las filas anteriores. Y lo que hacemos aquí no es más que decir "si el valor de las filas anteriores en la columna Remain_Toner_Black es más pequeño que el de la fila actual y el número de serie de las filas anteriores es el mismo que el número de serie de las filas reales, devuelve 1, de lo contrario, devuelve 0".

Luego, simplemente podemos decir en la consulta externa "seleccione cada fila, donde la anterior devolvió 1".

Dada su consulta, no necesita todas estas subconsultas. Son muy caros e innecesarios. En realidad es bastante loco. En esta parte de la 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

seleccionas la tabla completa y para cada fila usted cuenta las filas dentro de ese grupo. Esa es una subconsulta dependiente. Todo solo para tener algún tipo de número de fila. Luego hace esto por segunda vez, solo para poder unir esas dos tablas temporales para obtener la fila anterior. Realmente, no es de extrañar que el rendimiento sea horrible.

Entonces, ¿cómo ajustar mi solución a su consulta? En lugar de la única variable que usé para obtener la fila anterior para Remain_Toner_Black, use cuatro para los colores negro, cian, magenta y amarillo. Y simplemente únase a la mesa de Impresoras y Clientes como ya lo hizo. No olvides ordenar antes de y listo.