Oto rozwiązanie Twojego pytania 1, które będzie działać znacznie szybciej, ponieważ masz wiele pełnych skanów tabel i zależnych podzapytań. Tutaj będziesz mieć co najwyżej tylko jedno skanowanie tabeli (i może tabelę tymczasową, w zależności od tego, jak duże są twoje dane i ile masz pamięci). Myślę, że możesz to łatwo dostosować do swojego pytania tutaj. Pytanie 2 (tak naprawdę tego nie czytałem) prawdopodobnie również otrzyma odpowiedź, ponieważ teraz łatwo jest po prostu dodać 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
- zobacz, jak działa na żywo w sqlfiddle
EDYCJA:
Wyjaśnienie:
Z tą linią
, (select @prev_toner:=0, @prev_sn:=SerialNumber from Table1 order by SerialNumber
po prostu inicjujemy zmienne @prev_toner
i @prev_sn
w locie. To to samo, co brak tego wiersza w zapytaniu, ale pisanie przed zapytaniem
SET @prev_toner = 0;
SET @prev_sn = (select serialnumber from your_table order by serialnumber limit 1);
SELECT ...
Dlaczego więc zapytanie przypisuje wartość do @prev_sn i dlaczego porządkować według numeru seryjnego? Kolejność według jest bardzo ważna. Bez zamówienia przez nie ma gwarantowanej kolejności zwracania wierszy. Ponadto uzyskamy dostęp do wartości poprzednich wierszy ze zmiennymi, dlatego ważne jest, aby te same numery seryjne były „zgrupowane razem”.
Kolumny w klauzuli select są oceniane jedna po drugiej, dlatego ważne jest, aby najpierw wybrać tę linię
if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,
zanim wybierzesz te dwie linie
@prev_sn := SerialNumber,
@prev_toner := Remain_Toner_Black
Dlaczego? Ostatnie dwie linie przypisują zmiennym tylko wartości bieżących wierszy. Dlatego w tej linii
if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,
zmienne nadal przechowują wartości z poprzednich wierszy. A to, co tutaj robimy, to nic innego jak powiedzenie „jeśli wartość poprzedniego wiersza w kolumnie Remain_Toner_Black jest mniejsza niż w bieżącym wierszu i poprzedni numer seryjny wiersza jest taki sam, jak rzeczywisty numer seryjny wiersza, zwróć 1, w przeciwnym razie zwróć 0."
Następnie możemy po prostu powiedzieć w zewnętrznym zapytaniu „wybierz każdy wiersz, w którym powyższe zwrócono 1”.
Biorąc pod uwagę Twoje zapytanie, nie potrzebujesz wszystkich tych podzapytań. Są bardzo drogie i niepotrzebne. Właściwie to dość szalone. W tej części zapytania
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
wybierasz całą tabelę i dla każdego wiersza liczysz wiersze w tej grupie. To jest podzapytanie zależne. Wszystko po to, żeby mieć jakiś numer wiersza. Następnie robisz to po raz drugi, aby połączyć te dwie tabele tymczasowe, aby uzyskać poprzedni wiersz. Naprawdę, nic dziwnego, że wydajność jest okropna.
Jak więc dostosować moje rozwiązanie do Twojego zapytania? Zamiast jednej zmiennej, której użyłem do uzyskania poprzedniego wiersza dla Remain_Toner_Black, użyj czterech dla kolorów czarnego, cyjan, magenta i żółtego. I po prostu dołącz do stołu Drukarzy i Klientów, tak jak już to zrobiłeś. Nie zapomnij o zamówieniu do i gotowe.