Jede Bestellung über mehrere Seiten bewahren

MySQLBeginner
Jetzt üben

Einführung

Deine Kollegin oder dein Kollege hat die beiden Abfragen der ersten Seite des Geschäfts bereits optimiert. Die Abholwarteschlange zeigt die ältesten zuerst, der Kundenverlauf die neuesten zuerst. Ein Aufrufer muss nun über die ersten sieben Bestellungen hinaus fortfahren. Mehrere tatsächliche Bestellungen teilen sich einen Zeitstempel, daher kann ein Cursor nur mit Zeitstempel Datensätze verlieren oder wiederholen.

Diese unabhängige Einheit stellt ihren eigenen MySQL-Datenstand, funktionierende queue.sql und history.sql für die erste Seite, angewendete Indizes und indexes.sql bereit. Sie benötigt keine Dateien aus der vorherigen VM. Du bearbeitest die beiden gespeicherten Abfragen so, dass jede ursprüngliche Bestellung vollständige auf- und absteigende Seitendurchläufe übersteht, während der effiziente indexierte Zugriff erhalten bleibt.

Voraussetzungen sind die MySQL-Parameter, halboffenen Grenzen, Zweispaltensortierung und Diagnosepläne der ersten Einheit. Cursor, die NULL sein können, ihre vollständigen Vergleiche und Prüfungen tiefer Seiten werden hier vermittelt. Verwende Terminal und /home/labex/project/order-desk. Ändere nur queue.sql und history.sql; bewahre bereitgestellte Indizes/Indexquelle, alle Bestellfakten, Spalten, den Primärschlüssel und ausführbare Werkzeuge.

Jede Seite enthält weiterhin bis zu sieben tatsächliche Bestellungen und verwendet höchstens 64 native diagnostische Zeilenanforderungen. Die vorbereitete Verbindung lässt Index Condition Pushdown für diesen kontrollierten Vergleich ausgeschaltet; behalte diese Einstellung bei und vermeide Optimizer-Hinweise. Anfragen verwenden einen festen Datenstand. Es wird weder eine Momentaufnahme bei gleichzeitigen Aktualisierungen noch eine Anzahl physischer E/A oder eine Latenzgarantie im Produktivbetrieb beansprucht. Laufzeitbeschriftungen und Ausgaben bleiben Englisch.

Beide Schlüssel in die nächste Abholseite übernehmen

In diesem Schritt setzt du die Warteschlange mit den ältesten Bestellungen zuerst fort, ohne die verbleibenden Bestellungen mit demselben Zeitstempel zu verlieren.

Beginne in Terminal an der normalen Shell. Die bereitgestellten db und desk sind dieselben Schnittstellen wie in der ersten Einheit. Ist ein Pfad oder Dateiname falsch, verwende ls, prüfe den tatsächlichen Namen und versuche es erneut, ohne ursprüngliche Zeilen zu ändern. cat gibt hier den gespeicherten Abfragetext aus. Die folgende page-Anfrage liest dessen tatsächliche Ausgabe; keine Referenzabfrage repariert fehlende Zeilen.

cd /home/labex/project/order-desk
cat queue.sql
./desk page queue

Die gespeicherte Abfrage hat die nützlichen Geschäfts-/Status-/Zeitbedingungen und die Sortierung (placed_at,order_id), aber keine Cursorbedingung. Ihre erste Seite gibt korrekt 900001 bis 900007 zurück. Der Wert cursor_for_next_request des Arbeitsplatzprogramms enthält Zeit und ID der tatsächlich letzten Zeile. Er kennzeichnet, wo eine nächste Anfrage beginnen kann; er verspricht keine weitere nicht leere Seite.

--after nimmt zwei Argumente entgegen: den letzten tatsächlichen Zeitstempel und die letzte tatsächliche Bestell-ID. Anführungszeichen halten das Leerzeichen des Zeitstempels in einem Shell-Argument. Fordere die nächste Seite mit dem gerade beobachteten Cursor an:

./desk page queue --after "2025-05-10 09:15:00.000000" 900007

Die unveränderte Ausgangsabfrage gibt die ersten sieben IDs erneut zurück, weil sie diesen Cursor ignoriert. Alle Befehle wurden erfolgreich ausgeführt, aber dies ist die falsche nächste Seite. Ändere nicht die Quellzeitstempel und entferne keine Duplikate aus einer angezeigten Liste, um das Problem zu verbergen.

Das Arbeitsplatzprogramm liefert zwei weitere native Sitzungsvariablen: @after_at und @after_id. Für die erste Seite sind beide NULL, also ohne vorherigen Cursor. SQL prüft dieses Fehlen mit IS NULL, nicht mit = NULL. Andernfalls muss eine nächste Zeile bei „älteste zuerst“ einen späteren Zeitstempel haben oder denselben Zeitstempel und eine größere ID. Nur placed_at > @after_at zu verwenden, würde die IDs 900008 und 900009 bei der wiederholten Uhrzeit verlieren.

Bewahre die vorhandenen Gruppen-, Status- und halboffenen Grenzen. Setze die Cursorauswahlmöglichkeiten in Klammern und verbinde sie mit AND mit diesen Filtern; OR darf eine Zeile nicht von den anderen geschäftlichen Bedingungen befreien. Die Reihenfolge bleibt nach beiden Schlüsseln aufsteigend. cat > queue.sql ersetzt die vollständige Abfrage unten; die Here-Dokument-Markierung SQL in Anführungszeichen verhindert Shell-Expansion und schließt auf ihrer eigenen Zeile.

cat > queue.sql <<'SQL'
SELECT order_id,shop_id AS group_id,customer_id AS actor_id,
       state,placed_at AS event_at,total_cents AS amount_cents
FROM orders
WHERE shop_id=@group_id AND state=@state
  AND placed_at>=@from_at AND placed_at<@until_at
  AND (@after_at IS NULL
       OR placed_at>@after_at
       OR (placed_at=@after_at AND order_id>@after_id))
ORDER BY placed_at ASC,order_id ASC
LIMIT 7;
SQL
./desk page queue
./desk page queue --after "2025-05-10 09:15:00.000000" 900007
./desk plan queue --after "2025-05-10 09:15:00.000000" 900007

Die erste Seite bleibt unverändert. Die nächste Seite enthält nun 900008, 900009 und 900021 mit ihren ursprünglichen Zeiten und Centbeträgen. Die exakte nächste Mitternacht liegt weiterhin außerhalb des angeforderten Tages; die letzte Mikrosekunde bleibt darin. Der Plan sollte den indexierten Bereich des Cursors erreichen, statt jede frühere Bestellung zu lesen und zu verwerfen. Prüfe die tatsächlichen Zeilen und die Diagnoseanzahl gemeinsam.

Die Aktion walk führt wiederholt dieselbe gespeicherte Abfrage aus und übergibt den Cursor der tatsächlich letzten Zeile, bis eine echte leere Seite erscheint. Sie bewahrt die zurückgegebene Reihenfolge und Felder exakt; sie sortiert nicht, entfernt keine Duplikate und füllt Ergebnisse nicht auf. Wiederholt eine Abfrage ihren letzten Cursor, meldet das Werkzeug einen Fehler, statt einen teilweisen Durchlauf als vollständig darzustellen.

./desk walk queue
./desk page queue --after "2025-05-10 23:59:59.999999" 900021

Der Durchlauf liefert alle zehn Bestellungen genau einmal: sieben, dann drei, dann eine leere Seite. Auch der ausdrücklich angegebene Cursor der letzten Zeile gibt eine leere Zeilenliste zurück. Das ist ein gültiger Endpunkt, anders als ein Order desk error. Ist eine Datei oder SQL falsch, prüfe sie mit ls oder cat, korrigiere die gespeicherte Abfrage und führe die tatsächliche Anfrage erneut aus. Ändere keine bereitgestellten Indizes oder ursprünglichen Zeilen. Führe nach dem Bestätigen der vollständigen Warteschlange die Schrittprüfung aus.

Den Verlauf absteigend mit begrenztem Aufwand fortsetzen

In diesem Schritt vervollständigst du die entgegengesetzte Cursorrichtung und vergleichst eine tiefe Seite mit einem offsetbasierten Lesezugriff auf dieselben Daten.

Bleibe in Terminal und /home/labex/project/order-desk. Bewahre die korrigierte Abholabfrage und die vorbereiteten Indizes/Indexquelle. Der Kundenverlauf verwendet DESC für Zeit und ID; eine nächste Zeile hat daher eine frühere Zeit oder dieselbe Zeit und eine kleinere ID. Sein Kundenfilter und halboffener Bereich bleiben wirksam. Der NULL-Fall der ersten Seite bleibt gleich.

Ersetze die vollständige Verlaufsabfrage. Ergänze keine Beschränkung auf den Status ready: Der Kundenverlauf enthält alle ursprünglichen Statuswerte und Geschäfte für diesen Kunden.

cat > history.sql <<'SQL'
SELECT order_id,shop_id AS group_id,customer_id AS actor_id,
       state,placed_at AS event_at,total_cents AS amount_cents
FROM orders
WHERE customer_id=@actor_id
  AND placed_at>=@from_at AND placed_at<@until_at
  AND (@after_at IS NULL
       OR placed_at<@after_at
       OR (placed_at=@after_at AND order_id<@after_id))
ORDER BY placed_at DESC,order_id DESC
LIMIT 7;
SQL
./desk page history
./desk page history --after "2025-05-10 09:15:00.000000" 900004
./desk walk history

Die erste Seite beginnt mit 900021, dann 900009 bis hinunter zu 900004. Ihre nächste Seite enthält 900003, 900002 und 900001. Der vollständige Durchlauf bewahrt alle zehn Bestellungen und erreicht dann eine tatsächliche leere Seite. Die Null-Cent-Bestellung bleibt auf der ersten Seite; gleiche Zeit oder gleicher Preis führt Identitäten niemals zusammen.

Ein tiefes OFFSET fordert MySQL auf, vorangehende Zeilen nach dem Sortieren zu verwerfen. Es kann sieben korrekte Zeilen zurückgeben und dabei deutlich mehr lesen. Ein Cursor kann stattdessen nahe dem letzten tatsächlichen Schlüssel beginnen. Vergleiche beides am Verlauf von Januar bis April des Kunden 77, dessen exklusives Ende der 1. Mai ist.

Im nativen Client unten überspringt LIMIT 1 OFFSET 199 zunächst 199 geordnete Zeilen und gibt die 200. Zeile zurück, die letzte vor einer OFFSET-200-Seite. EXPLAIN ANALYZE führt die Offsetabfrage tatsächlich aus und meldet ihren Iteratoraufwand. Das abschließende SELECT zeigt die tatsächlichen Zeilen dieser Seite. Here-Dokumente und beide Sortierrichtungen sind dieselben wie bereits geübt.

./db <<'SQL'
SELECT order_id,placed_at
FROM orders
WHERE customer_id=77
  AND placed_at>='2025-01-01' AND placed_at<'2025-05-01'
ORDER BY placed_at DESC,order_id DESC
LIMIT 1 OFFSET 199;
EXPLAIN ANALYZE FORMAT=TREE
SELECT order_id,shop_id AS group_id,customer_id AS actor_id,
       state,placed_at AS event_at,total_cents AS amount_cents
FROM orders
WHERE customer_id=77
  AND placed_at>='2025-01-01' AND placed_at<'2025-05-01'
ORDER BY placed_at DESC,order_id DESC
LIMIT 7 OFFSET 200;
SELECT order_id,shop_id AS group_id,customer_id AS actor_id,
       state,placed_at AS event_at,total_cents AS amount_cents
FROM orders
WHERE customer_id=77
  AND placed_at>='2025-01-01' AND placed_at<'2025-05-01'
ORDER BY placed_at DESC,order_id DESC
LIMIT 7 OFFSET 200;
SQL

Die 200. Zeile ist Bestellung 94263 um 2025-02-25 02:30:00.000000. Lies die tatsächlichen Zeilen des unteren Iterators: Die Offsetanfrage muss die ersten 200 passieren, bevor sie sieben liefert. Verwende nun diese tatsächliche Zeile als Cursor der gespeicherten Abfrage mit demselben Kunden und denselben Grenzen. --from und --until gelten nur für diesen Aufruf.

./desk page history --from 2025-01-01 --until 2025-05-01 --after "2025-02-25 02:30:00.000000" 94263
./desk plan history --from 2025-01-01 --until 2025-05-01 --after "2025-02-25 02:30:00.000000" 94263

Dieselben sieben IDs erscheinen, beginnend mit 3542 und 87067. Die Cursorabfrage sollte die indexierten Bereiche mit einer kleinen tatsächlichen Anforderungsanzahl erreichen, statt 200 frühere Zeilen zu verwerfen. Vergleiche den Aufwand derselben Seite, nicht zweier unbeteiligter Anfragen; verstrichene Millisekunden belegen weiterhin nicht das Abnahmeergebnis.

Prüfe zum Schluss einen anderen Kunden, einen gültigen leeren Bereich und den unveränderten Abholdurchlauf:

./desk page history --actor 78
./desk page history --from 2027-01-01 --until 2027-01-02
./desk walk queue

Bewahre beide wiederverwendbaren SQL-Dateien. Die bereitgestellten Indizes und indexes.sql bleiben intakt. Ein SQL-/Datei-/Verbindungsfehler einer Abfrage bleibt ein Fehler; das Leseprogramm erfindet keine fehlenden Bestellungen. Dies sind stabile Durchläufe über den vorbereiteten festen Datenstand, keine Transaktionsmomentaufnahme bei gleichzeitigen Änderungen. Die Abschlussprüfung prüft exakte ursprüngliche Felder/Reihenfolge, kleine vollständige Durchläufe, geänderte Identitäten/Bereiche/Cursor und diagnostischen Aufwand zusammen mit dem Erhalt der Quelle und Werkzeuge.

Zusammenfassung

Du hast zwei indexierte Bestellansichten mit vollständigen Zeitstempel-und-ID-Cursorn fortgesetzt, jede Bestellung mit gleicher Zeit und jeden Null-Cent-Wert bewahrt und tatsächliche leere Endpunkte geprüft. Du hast dieselbe tiefe Seite über OFFSET und indexierte Cursoranfragen verglichen, ohne Millisekunden als Beweis zu behandeln. Beide Richtungen funktionieren auf dem festen Datenstand, während ursprüngliche Daten, Werkzeuge und vorbereitete Indizes intakt bleiben.