Einführung
MEND & MOSS ist bereit, Lieferantenaktualisierungen zu übernehmen. Am Wareneingang sind bereits eine Lieferung zwischengespeichert und die grundlegende Prüfung aus dem vorherigen Lab vorhanden. Dies ist eine unabhängige Umgebung: Dateien aus einer vorherigen VM werden nicht benötigt.
Einige auf den ersten Blick verwendbare Zeilen widersprechen sich bei derselben Version. Andere treffen verspätet ein. Du erweiterst die Prüfung, um diese Widersprüche abzulehnen, und speicherst anschließend ein Aktualisierungsskript, das nur neuere Revisionen übernimmt und dabei die lokalen Lagerplatznotizen des Geschäfts bewahrt. Grundlegendes SQL aus dem ersten angeleiteten Lab wird vorausgesetzt; Konfliktprüfungen, UPSERT und Transaktionen werden hier erklärt.
Verwende durchgehend Terminal. Du darfst inspect.sql ändern, apply.sql erstellen und raw_delivery neu laden. Bewahre die ursprünglichen Bestandsidentitäten und Lagerplatznotizen, die Lieferanten und beide CSV-Dateien. Die vom Lieferanten vorgegebenen Werte für Titel/Preis/Revision dürfen nach den folgenden Regeln auf neuere Versionen aktualisiert werden.
Widersprüchliche Versionen ablehnen
In diesem Schritt unterscheidest du eine identisch wiederholte Zeile von einem Versionskonflikt. Beginne in Terminal:
cd /home/labex/project/supplier-desk
Lies die tatsächliche Lieferung dieser Einheit und das bereitgestellte Prüfskript:
cat deliveries/current.csv
cat inspect.sql
Die zehn Lieferzeilen sind bereits zwischengespeichert. Die Zeilen 3 und 7 wiederholen dieselbe Identität, denselben Titel, Preis und dieselbe Revision des Reparaturbands; die Wiederholung einer Tatsache macht daraus kein neues Produkt. Die Zeilen 8 und 9 beschreiben beide N / PIN-2, Revision 2, widersprechen sich aber beim Preis. Keine davon ist eine freigegebene Antwort. Zeile 10 gibt für N / WASH-2, Revision 1, einen anderen Preis an als die bereits vorhandene Revision 1 im Bestand. Eine Versionsnummer kann nicht zwei unterschiedliche Inhalte bezeichnen.
Die Regeln des ersten Labs gelten weiterhin. Es gibt zwei zusätzliche Ablehnungsgründe: delivery conflict, wenn ansonsten gültige Zeilen denselben Lieferanten/dieselbe SKU/Revision, aber unterschiedliche Titel oder ganzzahlige Preise haben, und stock conflict, wenn diese Version vom aktuellen Bestand abweicht. Ein Lieferkonflikt hat Vorrang, falls beides zutrifft. Identische Wiederholungen werden akzeptiert. Ältere Versionen dürfen bei der Prüfung akzeptiert werden, dürfen aber niemals neueren Bestand überschreiben.
Sieh dir den ursprünglichen Bestand an, bevor du ihn veränderst:
sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'
Führe die grundlegende Prüfung aus, damit ihre Views vorhanden sind:
sqlite3 -bail stock.db < inspect.sql
Erstelle vor dem Bearbeiten eine Sicherungskopie des bereitgestellten Skripts zur Wiederherstellung. cp kopiert den ersten Pfad zum zweiten, ohne das Original zu verändern:
cp inspect.sql inspect-start.sql
Das nächste SQL verwendet die Aliasse t, other, s und e für eine Lieferzeile, eine andere Lieferzeile, eine Bestandszeile und eine Fehlerzeile. EXISTS (SELECT 1 ...) fragt, ob irgendeine passende Zeile existiert; die 1 ist nur ein Platzhalter, da der Inhalt der Zeile nicht zurückgegeben wird. AND verlangt, dass alle Schlüsselfelder übereinstimmen. <> bedeutet ungleich. NOT EXISTS behält Zeilen ohne Fehler. UNION ALL kombiniert grundlegende Fehler und Konflikte, ohne die einzelnen Zeilenidentitäten zu verlieren.
Hänge die Erweiterung an inspect.sql an. >> hängt an, statt zu ersetzen; behalte daher die bereitgestellten grundlegenden Definitionen darüber bei. Füge diesen Block einmal ein. Eine Wiederholung ist hier unschädlich, da jede Erweiterung nur die beiden abschließenden Views neu erstellt. Vermeide trotzdem unnötigen doppelten Text:
cat >> inspect.sql <<'SQL'
DROP VIEW IF EXISTS delivery_ready;
DROP VIEW IF EXISTS delivery_errors;
CREATE VIEW delivery_errors AS
SELECT delivery_line,reason FROM delivery_check WHERE reason IS NOT NULL
UNION ALL
SELECT t.delivery_line,
CASE WHEN EXISTS (
SELECT 1 FROM typed_delivery AS other
WHERE other.supplier=t.supplier AND other.sku=t.sku
AND other.revision=t.revision
AND (other.title<>t.title OR other.price_cents<>t.price_cents)
) THEN 'delivery conflict' ELSE 'stock conflict' END
FROM typed_delivery AS t
WHERE EXISTS (
SELECT 1 FROM typed_delivery AS other
WHERE other.supplier=t.supplier AND other.sku=t.sku
AND other.revision=t.revision
AND (other.title<>t.title OR other.price_cents<>t.price_cents)
) OR EXISTS (
SELECT 1 FROM stock AS s
WHERE s.supplier=t.supplier AND s.sku=t.sku AND s.revision=t.revision
AND (s.title<>t.title OR s.price_cents<>t.price_cents)
);
CREATE VIEW delivery_ready AS
SELECT supplier,sku,title,price_cents,revision FROM typed_delivery AS t
WHERE NOT EXISTS (
SELECT 1 FROM delivery_errors AS e WHERE e.delivery_line=t.delivery_line
);
SQL
Führe das gesamte gespeicherte Skript erneut aus:
sqlite3 -bail stock.db < inspect.sql
sqlite3 -header -column stock.db 'SELECT * FROM delivery_errors ORDER BY delivery_line;'
Erwartet werden sechs Fehler: Zeile 4 invalid price, 5 unknown supplier, 6 missing SKU, 8 und 9 delivery conflict und 10 stock conflict. Reparaturband erscheint in der Übersicht der akzeptierten Zeilen zweimal, da beide Quellzeilen akzeptabel sind; ein Paar identischer Zeilen darf keinen Preis verdoppeln oder zwei Bestandsartikel erzeugen.
sqlite3 -header -column stock.db 'SELECT * FROM delivery_ready ORDER BY supplier,sku;'
Es gibt vier akzeptierte Lieferzeilen, die drei Produktidentitäten darstellen. Der Bestand ist weiterhin unverändert. Siehst du nur die drei grundlegenden Fehler, prüfe, ob der angehängte Block vorhanden ist, und führe das vollständige Skript erneut aus. Stelle bei einem Syntaxfehler im angehängten Block mit cp inspect-start.sql inspect.sql die bereitgestellte Grundlage wieder her. Wiederhole anschließend den vollständigen Anfügeblock und führe das Skript erneut aus. Diese Wiederherstellung lässt den Bestand und beide CSV-Dateien unverändert.
Neuere Werte übernehmen und die Lieferung sicher wiederholen
In diesem Schritt übernimmst du die akzeptablen Zeilen, ohne lokale Lagerplatznotizen zu verändern. Anschließend weist du nach, dass die Wiederholung derselben Lieferung oder der Eingang einer älteren Lieferung deine Arbeit nicht rückgängig macht. Beginne im selben Verzeichnis in Terminal.
Die Tabelle stock hat einen Primärschlüssel aus (supplier,sku): Diese beiden Felder identifizieren eine Zeile. Ein UPSERT fügt eine fehlende Identität ein oder behandelt einen Konflikt mit diesem bereits vorhandenen Schlüssel. excluded bezeichnet die vorgeschlagene eingehende Zeile. Ihre Revision muss größer als die aktuelle Bestandsrevision sein, bevor eine Aktualisierung erfolgt. Die bereitgestellte Spalte shelf_note gehört dem Geschäft: Lass sie sowohl aus den eingehenden Werten als auch aus den Aktualisierungszuweisungen heraus. Eine neue Bestandsidentität erhält den vorbereiteten Standardwert einer leeren Notiz.
BEGIN startet eine Transaktion, und COMMIT speichert sie als eine Einheit. Ein Fehler hält diesen -bail-Befehl an; beim Schließen seiner Verbindung wird eine unvollendete Transaktion zurückgerollt. Dieses Beispiel aktualisiert eine kleine, vorbereitete Datenbank; es ist kein System für gleichzeitige Synchronisierung oder Backups.
Das SELECT liest die tatsächliche View der akzeptierten Zeilen. Dessen WHERE true behält alle diese Zeilen bei und verhindert zugleich, dass SQLite das folgende ON CONFLICT mit einer Join-Bedingung verwechselt. Speichere das vollständige Skript:
cat > apply.sql <<'SQL'
BEGIN;
INSERT INTO stock(supplier,sku,title,price_cents,revision)
SELECT supplier,sku,title,price_cents,revision
FROM delivery_ready WHERE true
ON CONFLICT(supplier,sku) DO UPDATE SET
title=excluded.title,
price_cents=excluded.price_cents,
revision=excluded.revision
WHERE excluded.revision>stock.revision;
COMMIT;
SQL
Aktiviere Fremdschlüssel für die Verbindung, mit der du es anwendest. -cmd führt zuerst die Verbindungseinstellung in Anführungszeichen aus; sie wird nicht global gespeichert. Anschließend liefert < apply.sql das SQL:
sqlite3 -bail -cmd 'PRAGMA foreign_keys=ON;' stock.db < apply.sql
sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'
Erwartet werden vier Bestandszeilen:
supplier sku title price_cents revision shelf_note
-------- ------ ----------- ----------- -------- ----------
N BOLT-1 Steel bolt 135 3 Bin A
N TAPE-4 Repair tape 450 1
N WASH-2 Washer 40 1 Bin C
W BOLT-1 Brass bolt 190 2 Drawer 4
Die Unterlegscheibe bleibt bei 40, da ihre widersprüchliche Version abgelehnt wurde. Es wurde kein Bestandsartikel für den Stift, die ungültige Kappe, den unbekannten Lieferanten oder die leere SKU hinzugefügt. Beide ursprünglichen Schraubennotizen bleiben erhalten. Das Reparaturband hat eine Zeile, obwohl es zweimal in der Lieferung vorkam.
Wiederhole dasselbe Skript:
sqlite3 -bail -cmd 'PRAGMA foreign_keys=ON;' stock.db < apply.sql
sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'
Das Ergebnis bleibt gleich. Die Revisionsbedingung macht eine identische Wiederholung des Imports sicher; sie addiert weder den Preis noch ersetzt sie die lokale Notiz.
Lies nun die ältere Lieferung, bevor du sie verwendest:
cat deliveries/older.csv
Dies sind ältere Schraubenversionen. Lade nur die Zwischentabelle mithilfe der interaktiven Shell neu:
sqlite3 stock.db
DELETE FROM raw_delivery;
.import --csv --skip 1 deliveries/older.csv raw_delivery
.quit
Die Views lesen automatisch die aktuellen Zeilen der Tabelle. Führe die unveränderte Aktualisierung aus und prüfe anschließend den Bestand:
sqlite3 -bail -cmd 'PRAGMA foreign_keys=ON;' stock.db < apply.sql
sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'
Alle vier Zeilen haben weiterhin dieselben Werte. Die Prüfung akzeptiert ältere gültige Zahlen, aber deren Übernahme kann den Bestand nicht auf eine ältere Version zurücksetzen. Dies unterscheidet sich von einer ungültigen oder widersprüchlichen Lieferung.
Hinterlasse den Arbeitsplatz so, dass die heutige Lieferung erneut geprüft werden kann. Das Leeren der Zwischentabelle leert nicht den Bestand:
sqlite3 stock.db
DELETE FROM raw_delivery;
.import --csv --skip 1 deliveries/current.csv raw_delivery
.quit
sqlite3 -header -column stock.db 'SELECT * FROM delivery_errors ORDER BY delivery_line;'
Die sechs abgelehnten Zeilen bleiben mit ihren Gründen sichtbar. Bewahre beide Skripte auf: inspect.sql beschreibt, wie eine weitere kompatible Lieferung geprüft wird; apply.sql beschreibt, wie deren sichere Zeilen den Bestand auf neuere Versionen aktualisieren. Einen falschen Pfad kannst du mit dem absoluten cd korrigieren; ein Skript mit Tippfehlern kannst du aus dem vollständigen Block ersetzen. Erstelle stock.db nicht neu und lösche keine Datensätze, um einen Schreibfehler zu beheben.
Zusammenfassung
Du hast anhand echter SQLite-Zeilen die Identitäten der Lieferanten, die Lieferdaten und den lokalen Bestand miteinander verknüpft. Du hast die tatsächlich akzeptierten und abgelehnten Datensätze ausgewertet, die ursprünglichen Quellen bewahrt und wiederverwendbares SQL für die nächste Lieferung gespeichert.



