Conserver chaque commande sur toutes les pages

MySQLBeginner
Pratiquer maintenant

Introduction

Votre collègue a déjà optimisé les deux recherches de première page du magasin. La file de retrait commence par les commandes les plus anciennes et l’historique client par les plus récentes. Un appelant doit maintenant poursuivre au-delà des sept premières commandes. Plusieurs véritables commandes partagent un horodatage ; un curseur fondé sur le seul horodatage peut donc perdre des enregistrements ou les répéter.

Cette unité indépendante fournit son propre instantané MySQL, des queue.sql et history.sql fonctionnels pour la première page, des index appliqués et indexes.sql. Aucun fichier de la VM précédente n’est nécessaire. Vous modifierez les deux requêtes enregistrées afin que chaque commande d’origine soit conservée dans des parcours complets de pages croissants et décroissants, tout en maintenant un accès indexé efficace.

Les prérequis sont les paramètres MySQL, les bornes semi-ouvertes, le tri à deux colonnes et les plans de diagnostic de la première unité. Les curseurs pouvant être NULL, leurs comparaisons complètes et les vérifications de pages profondes sont enseignés ici. Utilisez Terminal et /home/labex/project/order-desk. Modifiez uniquement queue.sql et history.sql ; préservez les index fournis/leur source, tous les faits des commandes, les colonnes, la clé primaire et les outils exécutables.

Chaque page contient toujours au plus sept véritables commandes et utilise au plus 64 demandes natives de lignes dans le diagnostic. La connexion préparée garde index condition pushdown désactivé pour cette comparaison contrôlée ; conservez ce réglage et évitez les indications à l’optimiseur. Les demandes utilisent un instantané de données fixe. Aucune garantie d’instantané pendant des mises à jour concurrentes, de nombre d’E/S physiques ou de latence en production n’est avancée. Les libellés et sorties de l’environnement d’exécution restent en anglais.

Transmettre les deux clés à la prochaine page de retrait

Dans cette étape, vous ferez avancer la file du plus ancien au plus récent sans perdre les commandes restantes au même horodatage.

Commencez dans Terminal, à l’invite du shell ordinaire. Les db et desk fournis sont les mêmes interfaces que celles pratiquées dans la première unité. Si un chemin ou un nom de fichier est incorrect, utilisez ls, vérifiez le véritable nom et réessayez sans changer les lignes d’origine. Ici, cat affiche le texte de la requête enregistrée. La demande page suivante lit sa véritable sortie ; aucune requête de référence ne répare les lignes manquantes.

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

La requête enregistrée possède les conditions utiles magasin/état/temps et le tri (placed_at,order_id), mais aucune condition de curseur. Sa première page renvoie correctement 900001 à 900007. Le cursor_for_next_request du poste contient l’heure et l’identifiant de la véritable dernière ligne. Il indique où une demande suivante peut commencer ; il ne promet pas une autre page non vide.

--after prend deux arguments : le dernier horodatage réel et le dernier identifiant réel de commande. Les guillemets gardent l’espace de l’horodatage dans un seul argument du shell. Demandez la page suivante avec le curseur que vous venez d’observer :

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

La requête de départ inchangée renvoie encore les sept premiers identifiants, car elle ignore ce curseur. Toutes les commandes se sont exécutées avec succès, mais c’est la mauvaise page suivante. Ne changez pas les horodatages sources et ne dédupliquez pas une liste affichée pour masquer le problème.

Le poste fournit deux variables natives de session supplémentaires : @after_at et @after_id. Pour la première page, les deux sont NULL, ce qui signifie aucun curseur précédent. SQL teste cette absence avec IS NULL, pas = NULL. Sinon, une ligne suivante du plus ancien au plus récent doit avoir un horodatage ultérieur, ou le même horodatage et un identifiant supérieur. Utiliser uniquement placed_at > @after_at ferait perdre les identifiants 900008 et 900009 à l’heure répétée.

Gardez les bornes semi-ouvertes, le groupe et l’état existants. Placez les choix de curseur entre parenthèses, reliés à ces filtres par AND ; OR ne doit pas soustraire une ligne aux autres conditions métier. L’ordre reste croissant selon les deux clés. cat > queue.sql remplace la requête complète ci-dessous ; le marqueur de here-document SQL entre guillemets empêche l’expansion du shell et termine le bloc seul sur sa ligne.

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

La première page reste inchangée. La page suivante contient maintenant 900008, 900009 et 900021, avec leurs heures et centimes d’origine. Le minuit suivant exact reste hors du jour demandé ; la dernière microseconde reste dedans. Le plan devrait atteindre la plage indexée du curseur au lieu de lire et écarter chaque commande antérieure. Examinez ensemble les véritables lignes et le compteur de diagnostic.

L’action walk exécute cette même requête enregistrée de façon répétée, en transmettant le curseur de la véritable dernière ligne, jusqu’à l’apparition d’une véritable page vide. Elle conserve exactement l’ordre et les champs renvoyés ; elle ne trie, ne déduplique et ne complète pas les résultats. Si une requête répète son dernier curseur, l’outil signale une erreur au lieu de présenter un parcours partiel comme complet.

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

Le parcours livre les dix commandes exactement une fois : sept, puis trois, puis une page vide. Le curseur explicite de dernière ligne renvoie lui aussi une liste de lignes vide. C’est une fin valide, contrairement à une erreur Order desk error. Si un fichier ou le SQL est incorrect, utilisez ls ou cat pour l’examiner, corrigez la requête enregistrée et relancez la véritable demande. Ne modifiez pas les index fournis ni les lignes d’origine. Lancez la vérification de l’étape après avoir confirmé la file complète.

Poursuivre l’historique du plus récent au plus ancien avec un travail borné

Dans cette étape, vous compléterez le sens opposé du curseur et comparerez une page profonde à une lecture par décalage des mêmes données.

Restez dans Terminal et dans /home/labex/project/order-desk. Conservez la requête de retrait corrigée et les index préparés/leur source. L’historique client utilise DESC pour l’heure comme pour l’identifiant ; une ligne suivante a donc une heure antérieure, ou la même heure et un identifiant inférieur. Son filtre client et sa plage semi-ouverte restent en vigueur. Le cas NULL de première page reste identique.

Remplacez la requête d’historique complète. N’ajoutez pas de restriction à l’état prêt : l’historique client comprend tous les états et magasins d’origine pour ce client.

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

La première page commence par 900021, puis 900009 jusqu’à 900004. Sa page suivante contient 900003, 900002 et 900001. Le parcours complet conserve les dix commandes puis atteint une véritable page vide. La commande à zéro centime reste dans la première page ; une heure ou un prix égal ne fusionne jamais les identités.

Un OFFSET profond demande à MySQL d’écarter les lignes précédentes après le tri. Il peut renvoyer sept lignes correctes tout en en lisant beaucoup plus. Un curseur peut plutôt démarrer près de la dernière clé réelle. Comparez les deux sur l’historique de janvier à avril du client 77, dont la fin exclusive est le 1er mai.

Dans le client natif ci-dessous, LIMIT 1 OFFSET 199 saute 199 lignes triées et renvoie la 200e ligne, la dernière précédant une page OFFSET 200. EXPLAIN ANALYZE exécute réellement la requête par décalage et indique le travail de ses itérateurs. Le SELECT final affiche les véritables lignes de cette page. Les here-documents et les deux sens de tri sont les mêmes que ceux déjà pratiqués.

./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

La 200e ligne est la commande 94263 à 2025-02-25 02:30:00.000000. Lisez les lignes réelles de l’itérateur inférieur : la demande par décalage doit passer les 200 premières avant d’en livrer sept. Utilisez maintenant cette véritable ligne comme curseur de la requête enregistrée, avec le même client et les mêmes bornes. --from et --until s’appliquent uniquement à cet appel.

./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

Les sept mêmes identifiants apparaissent, en commençant par 3542 et 87067. La requête par curseur devrait atteindre les plages indexées avec un faible nombre réel de demandes, plutôt que d’écarter 200 lignes antérieures. Comparez le travail sur la même page, pas deux demandes sans rapport ; les millisecondes écoulées ne suffisent toujours pas à établir l’acceptation.

Enfin, vérifiez un autre client, une plage vide valide et le parcours de retrait inchangé :

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

Conservez les deux fichiers SQL réutilisables. Les index fournis et indexes.sql restent intacts. Une erreur de SQL/fichier/connexion de requête reste une erreur ; le lecteur n’invente pas de commandes manquantes. Ce sont des parcours stables sur l’instantané fixe préparé, pas un instantané transactionnel pendant des modifications concurrentes. La vérification finale contrôle les champs/l’ordre exacts d’origine, de petits parcours complets, des identités/plages/curseurs modifiés et le travail du diagnostic, ainsi que la préservation de la source et des outils.

Résumé

Vous avez poursuivi deux vues indexées de commandes à l’aide de curseurs complets horodatage-identifiant, préservé chaque commande à heure égale et chaque valeur à zéro centime, et vérifié de véritables fins de parcours vides. Vous avez comparé la même page profonde via OFFSET et des demandes par curseur indexé sans prendre les millisecondes pour preuve. Les deux sens fonctionnent sur l’instantané fixe tandis que les données, outils et index préparés d’origine restent intacts.