Introduction
MEND & MOSS répare des objets du quotidien. Deux fournisseurs utilisent le même SKU BOLT-1, mais vendent des boulons différents. Le magasin conserve une note locale d’emplacement en rayon, en plus du libellé, du prix et de la révision fournis par chaque fournisseur. Une révision est un numéro d’édition positif : plus il est élevé, plus l’édition est récente.
Dans ce TP guidé, vous chargerez un CSV fournisseur de six lignes dans une table intermédiaire sans modifier le stock, puis créerez des vues SQL réutilisables pour séparer les enregistrements utilisables des enregistrements invalides. Une vue est une requête enregistrée, pas une deuxième copie de ses lignes. Il suffit de savoir naviguer dans un terminal ; les commandes SQL et SQLite seront présentées avant leur utilisation. Tous les enregistrements du magasin sont fictifs.
Utilisez l’onglet Terminal tout au long du TP. SQLite et la base de départ sont prêts. Il n’y a pas de site web à construire. Conservez le stock d’origine et les deux fichiers de deliveries/ sans les modifier.
Lire le stock et charger une livraison en zone intermédiaire
Dans cette étape, vous relierez un enregistrement de stock ordinaire à son fournisseur, puis importerez toute la livraison actuelle dans une table intermédiaire distincte. Commencez dans Terminal. cd change de répertoire ; ce chemin absolu permet aussi de revenir au bon endroit après une simple erreur de répertoire :
cd /home/labex/project/supplier-desk
ls affiche la liste des fichiers. Vous devez voir stock.db et deliveries :
ls
La base de données est un véritable fichier. sqlite3 stock.db l’ouvre dans le shell interactif de SQLite. Restez à l’invite sqlite> pour le SQL et les commandes commençant par un point ci-dessous ; ne les saisissez pas à l’invite du shell Linux :
sqlite3 stock.db
Une table stocke des lignes dans des colonnes nommées. SELECT choisit les colonnes à lire, FROM désigne une table et ORDER BY impose un ordre d’affichage prévisible. Les instructions SQL se terminent par ;. Les deux commandes commençant par un point règlent l’affichage : .headers on montre les noms des colonnes et .mode column aligne les valeurs. Ces commandes ne prennent pas de point-virgule :
.headers on
.mode column
SELECT supplier,sku,title,price_cents,revision,shelf_note
FROM stock ORDER BY supplier,sku;
Le stock contient trois lignes. L’article N / BOLT-1 de North est Steel bolt, à 120 centimes, révision 2, dans Bin A. L’article W / BOLT-1 de West est Brass bolt, à 180 centimes, révision 1, dans Drawer 4. Un centime est la plus petite unité monétaire de ce magasin fictif ; 120 signifie 1.20. Les centimes entiers évitent les arrondis décimaux dans cet exercice. N / WASH-2 doit aussi rester disponible.
La paire supplier,sku identifie un enregistrement de stock. Le SKU seul ne permet pas de distinguer les deux boulons. SELECT * demande toutes les colonnes :
SELECT * FROM suppliers ORDER BY code;
Vous devez obtenir N / North Parts et W / West Workshop. Un code fournisseur présent dans une livraison doit figurer dans cette table.
SQLite vérifie les relations de clé étrangère séparément pour chaque connexion. Activez-les dans ce shell. Ce réglage ne modifie pas les données du stock :
PRAGMA foreign_keys=ON;
Quittez SQLite avec .quit ; vous revenez au shell Linux :
.quit
cat affiche un fichier texte. Lisez le véritable CSV :
cat deliveries/current.csv
CSV signifie « valeurs séparées par des virgules ». La première ligne nomme cinq colonnes. Le fichier contient des prix et des révisions sous forme de texte, dont 12kits, un fournisseur inconnu Z et une ligne dont le SKU est vide. Ce sont des anomalies d’entrée intentionnelles, pas une invitation à deviner une correction. Les fichiers sources restent inchangés. Le CSV fourni comporte exactement cinq champs par ligne de données ; les guillemets standard du format CSV sont pris en charge.
Rouvrez la base de données :
sqlite3 stock.db
La table préparée raw_delivery possède cinq colonnes TEXT dans cet ordre. TEXT conserve exactement ce qui est arrivé, y compris les nombres invalides ; importer directement dans stock mélangerait l’inspection et les mises à jour métier. .import --csv sélectionne l’analyse du format CSV et --skip 1 ignore sa ligne d’en-tête :
.import --csv --skip 1 deliveries/current.csv raw_delivery
rowid est l’identifiant de ligne SQLite de cette table intermédiaire. Il sert de numéro de ligne de livraison, pas d’identité de produit. Affichez toutes les lignes importées :
.headers on
.mode column
SELECT rowid AS delivery_line,* FROM raw_delivery ORDER BY rowid;
Vous devez voir les lignes 1–6 dans l’ordre du fichier. Les lignes 1–3 correspondent aux deux boulons et au nouveau ruban de réparation. La ligne 4 contient 12kits, la ligne 5 le fournisseur Z et la ligne 6 un SKU vide. AS donne au rowid affiché le nom plus explicite delivery_line.
Un import ajoute des lignes. Si vous avez importé deux fois par accident, videz uniquement cette table intermédiaire avec DELETE FROM raw_delivery;, puis relancez l’import une seule fois. C’est la procédure de récupération ordinaire de cette unité ; ne supprimez jamais stock et ne réinitialisez pas toute la VM :
DELETE FROM raw_delivery;
.import --csv --skip 1 deliveries/current.csv raw_delivery
Terminez dans le shell Linux :
.quit
La livraison chargée en zone intermédiaire est votre premier résultat. Le stock conserve ses trois lignes et ses notes d’emplacement d’origine.
Séparer les lignes utilisables des valeurs invalides
Dans cette étape, vous enregistrerez un script d’inspection et utiliserez ses véritables vues pour examiner la livraison. Commencez dans Terminal, dans /home/labex/project/supplier-desk.
Le magasin accepte un fournisseur connu, un SKU non vide et un libellé contenant autre chose que des espaces. Les prix doivent comporter 1–6 chiffres ASCII et représenter un nombre positif. Les révisions doivent comporter 1–3 chiffres ASCII et représenter un nombre positif. Les zéros initiaux sont autorisés. Ces limites restreintes constituent le contrat d’entrée ; elles ne prétendent pas valider des formats numériques quelconques.
CREATE VIEW name AS SELECT ... enregistre une requête sous un nom. DROP VIEW IF EXISTS supprime uniquement cette requête enregistrée si elle existe ; la table source n’est pas supprimée. WHERE conserve les lignes dont la condition est vraie. BETWEEN 1 AND 6 inclut les deux bornes ; NOT BETWEEN détecte les valeurs situées en dehors.
Une expression CASE sélectionne le premier motif correspondant. WHEN définit une condition et THEN son résultat ; sans correspondance, le résultat est NULL, ce qui signifie ici qu’il n’y a pas d’erreur. IS NULL teste cette absence. OR combine plusieurs motifs possibles de rejet d’une valeur. NOT IN (SELECT code FROM suppliers) vérifie l’appartenance à la liste des fournisseurs. trim retire les espaces autour du texte et length compte les caractères.
Pour les vérifications numériques, GLOB '*[^0-9]*' détecte tout caractère autre qu’un chiffre ASCII : * correspond à une séquence quelconque et [^0-9] à un caractère non numérique. La vérification distincte de la longueur rejette les chaînes vides. CAST(... AS INTEGER) convertit du texte en entier, mais n’est utilisé qu’après les vérifications des chiffres et de la longueur. Une conversion seule n’est pas sûre : un texte comme 12kits ne doit pas être accepté comme 12.
Vous créerez quatre vues. delivery_check conserve les valeurs d’origine et un motif. typed_delivery convertit uniquement les lignes valides. delivery_errors répertorie les numéros des lignes rejetées et leurs motifs. delivery_ready expose les valeurs utilisables pour une mise à jour ultérieure. Le script supprime uniquement ces vues nommées afin de pouvoir être exécuté à nouveau ; il ne supprime jamais le stock ni les lignes sources.
La prochaine commande Linux enregistre ce SQL dans inspect.sql. > remplace le fichier de destination. <<'SQL' commence un here-document littéral : collez le bloc entier, y compris le SQL final seul sur sa ligne. N’exécutez pas cette commande cat dans SQLite.
cat > inspect.sql <<'SQL'
DROP VIEW IF EXISTS delivery_ready;
DROP VIEW IF EXISTS delivery_errors;
DROP VIEW IF EXISTS typed_delivery;
DROP VIEW IF EXISTS delivery_check;
CREATE VIEW delivery_check AS
SELECT rowid AS delivery_line, *,
CASE
WHEN supplier NOT IN (SELECT code FROM suppliers) OR supplier IS NULL
THEN 'unknown supplier'
WHEN sku IS NULL OR sku = '' THEN 'missing SKU'
WHEN title IS NULL OR trim(title) = '' THEN 'missing title'
WHEN price_cents IS NULL OR length(price_cents) NOT BETWEEN 1 AND 6
OR price_cents GLOB '*[^0-9]*' OR CAST(price_cents AS INTEGER) < 1
THEN 'invalid price'
WHEN revision IS NULL OR length(revision) NOT BETWEEN 1 AND 3
OR revision GLOB '*[^0-9]*' OR CAST(revision AS INTEGER) < 1
THEN 'invalid revision'
END AS reason
FROM raw_delivery;
CREATE VIEW typed_delivery AS
SELECT delivery_line, supplier, sku, title,
CAST(price_cents AS INTEGER) AS price_cents,
CAST(revision AS INTEGER) AS revision
FROM delivery_check WHERE reason IS NULL;
CREATE VIEW delivery_errors AS
SELECT delivery_line, reason FROM delivery_check WHERE reason IS NOT NULL;
CREATE VIEW delivery_ready AS
SELECT supplier,sku,title,price_cents,revision FROM typed_delivery;
SQL
Si le terminal continue d’attendre, le marqueur de fin manque : saisissez SQL seul. Si vous avez mal saisi une ligne, répétez le bloc complet pour remplacer ce script. Aucun éditeur ni aucune réinitialisation ne sont nécessaires.
Exécutez un script avec la redirection d’entrée Linux <. -bail demande à SQLite de s’arrêter à la première erreur plutôt que de poursuivre avec les instructions suivantes. Aucune sortie n’est attendue en cas de réussite :
sqlite3 -bail stock.db < inspect.sql
En cas de simple erreur de nom de fichier ou de syntaxe, utilisez ls, revenez avec le cd absolu de l’étape 1, corrigez inspect.sql avec le bloc complet et relancez-le. Le stock n’a pas été modifié.
SQLite accepte aussi une instruction SQL entre guillemets après le chemin de la base. -header -column sélectionne un affichage lisible pour cette seule commande. Les guillemets conservent les espaces et les points-virgules dans un seul argument du shell :
sqlite3 -header -column stock.db 'SELECT * FROM delivery_errors ORDER BY delivery_line;'
Résultat attendu :
delivery_line reason
------------- ----------------
4 invalid price
5 unknown supplier
6 missing SKU
Le motif appartient à une ligne de livraison : un article rejeté reste donc traçable jusqu’à sa source. Aucune valeur source n’a été corrigée ni effacée.
sqlite3 -header -column stock.db 'SELECT * FROM delivery_ready ORDER BY supplier,sku;'
Vous devez obtenir trois lignes acceptables : le boulon de North à 135 centimes/révision 3, le ruban de réparation à 450/révision 1 et le boulon de West à 190/révision 2. Les identités des deux boulons restent distinctes. typeof indique le véritable type d’une valeur SQLite :
sqlite3 -header -column stock.db 'SELECT sku,typeof(price_cents),typeof(revision) FROM delivery_ready ORDER BY supplier,sku;'
Les deux colonnes de type doivent indiquer integer. Enfin, lisez le véritable stock :
sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'
Il conserve ses valeurs et ses notes d’emplacement d’origine. Vous avez préparé une inspection de réception réutilisable ; vous n’avez pas appliqué les changements du fournisseur. Le prochain TP guidé indépendant commence avec une nouvelle base et une livraison plus importante déjà chargée en zone intermédiaire.
Résumé
Vous avez utilisé de véritables lignes SQLite pour relier les identités des fournisseurs, les valeurs livrées et le stock local. Vous avez interprété les enregistrements réellement acceptés et rejetés, conservé les sources d’origine et enregistré du SQL réutilisable pour la prochaine livraison.



