Eine Lieferantenlieferung prüfen

SQLiteBeginner
Jetzt üben

Einführung

MEND & MOSS repariert Haushaltsgegenstände. Zwei Lieferanten verwenden beide die SKU BOLT-1, verkaufen aber unterschiedliche Schrauben. Das Geschäft speichert neben Titel, Preis und Revision jedes Lieferanten auch eine eigene Lagerplatznotiz. Eine Revision ist eine positive Versionsnummer: Je größer sie ist, desto neuer ist die Version.

In diesem angeleiteten Lab importierst du eine Lieferanten-CSV mit sechs Datenzeilen in eine Zwischentabelle, ohne den Bestand zu verändern. Anschließend erstellst du wiederverwendbare SQL-Views, die verwendbare von ungültigen Datensätzen trennen. Eine View ist eine gespeicherte Abfrage, keine zweite Kopie ihrer Zeilen. Du benötigst nur grundlegende Kenntnisse der Navigation im Terminal; die SQL- und SQLite-Befehle werden vor ihrer Verwendung erklärt. Alle Geschäftsdaten sind synthetisch.

Verwende durchgehend den Tab Terminal. SQLite und die Ausgangsdatenbank sind vorbereitet. Es gibt keine Website zu erstellen. Lass den ursprünglichen Bestand und beide Dateien in deliveries/ unverändert.

Bestand lesen und eine Lieferung zwischenspeichern

In diesem Schritt ordnest du einen normalen Bestandsdatensatz seinem Lieferanten zu und importierst anschließend die gesamte aktuelle Lieferung in eine separate Zwischentabelle. Beginne in Terminal. cd wechselt das Verzeichnis; mit diesem absoluten Pfad kannst du auch einen gewöhnlichen Fehler durch ein falsches Arbeitsverzeichnis beheben:

cd /home/labex/project/supplier-desk

ls listet Dateien auf. Du solltest stock.db und deliveries sehen:

ls

Die Datenbank ist eine echte Datei. sqlite3 stock.db öffnet sie in der interaktiven SQLite-Shell. Bleibe für die folgenden SQL- und Punktbefehle an der Eingabeaufforderung sqlite>; gib sie nicht an der Eingabeaufforderung der Linux-Shell ein:

sqlite3 stock.db

Eine Tabelle speichert Zeilen in benannten Spalten. SELECT wählt die zu lesenden Spalten aus, FROM benennt eine Tabelle und ORDER BY legt eine vorhersehbare Anzeigereihenfolge fest. SQL-Anweisungen enden mit ;. Die beiden Punktbefehle steuern die Anzeige: .headers on zeigt Spaltennamen an, und .mode column richtet Werte spaltenweise aus. Punktbefehle haben kein Semikolon:

.headers on
.mode column
SELECT supplier,sku,title,price_cents,revision,shelf_note
FROM stock ORDER BY supplier,sku;

Es gibt drei Bestandszeilen. Norths N / BOLT-1 ist Steel bolt mit 120 Cent, Revision 2, in Bin A. Wests W / BOLT-1 ist Brass bolt mit 180 Cent, Revision 1, in Drawer 4. Ein Cent ist die kleinste Währungseinheit dieses fiktiven Geschäfts; 120 bedeutet 1,20. Ganzzahlige Centbeträge vermeiden in dieser Übung Rundungsfehler bei Dezimalzahlen. Auch N / WASH-2 muss weiterhin verfügbar bleiben.

Das Paar supplier,sku identifiziert einen Bestandsdatensatz. Die SKU allein kann die beiden Schrauben nicht unterscheiden. SELECT * fordert jede Spalte an:

SELECT * FROM suppliers ORDER BY code;

Erwartet werden N / North Parts und W / West Workshop. Ein Lieferantencode aus einer Lieferung muss in dieser Tabelle vorkommen.

SQLite prüft Fremdschlüsselbeziehungen für jede Verbindung separat. Aktiviere sie in dieser Shell. Diese Einstellung verändert keine Bestandsdaten:

PRAGMA foreign_keys=ON;

Verlasse SQLite mit .quit; du kehrst zur Linux-Shell zurück:

.quit

cat gibt eine Textdatei aus. Lies die tatsächliche CSV-Datei:

cat deliveries/current.csv

CSV steht für durch Kommas getrennte Werte. Die erste Zeile benennt fünf Spalten. Die Datei enthält Preise und Revisionen als Text, darunter 12kits, den unbekannten Lieferanten Z und eine Zeile mit leerer SKU. Dies sind absichtliche Eingabeprobleme, keine Aufforderungen, eine Korrektur zu erraten. Die Quelldateien bleiben unverändert. Die bereitgestellte CSV enthält genau fünf Felder je Datenzeile; die übliche CSV-Schreibweise mit Anführungszeichen wird unterstützt.

Öffne die Datenbank erneut:

sqlite3 stock.db

Die vorbereitete Tabelle raw_delivery hat fünf TEXT-Spalten in dieser Reihenfolge. TEXT bewahrt genau das, was angekommen ist, einschließlich ungültiger Zahlen; ein direkter Import in stock würde Prüfung und Bestandsänderungen vermischen. .import --csv wählt die CSV-Verarbeitung aus, und --skip 1 überspringt die Kopfzeile:

.import --csv --skip 1 deliveries/current.csv raw_delivery

rowid ist die SQLite-Zeilenkennung dieser Zwischentabelle. Sie eignet sich als Nummer einer Lieferzeile, nicht als Produktidentität. Zeige jede importierte Zeile an:

.headers on
.mode column
SELECT rowid AS delivery_line,* FROM raw_delivery ORDER BY rowid;

Erwartet werden die Zeilen 1–6 in Dateireihenfolge. Die Zeilen 1–3 sind die beiden Schrauben und neues Reparaturband. Zeile 4 enthält 12kits, Zeile 5 den Lieferanten Z und Zeile 6 eine leere SKU. AS gibt der angezeigten rowid den verständlicheren Namen delivery_line.

Ein Import hängt Zeilen an. Falls du versehentlich zweimal importiert hast, leere mit DELETE FROM raw_delivery; nur diese Zwischentabelle und wiederhole den Import anschließend einmal. Dies ist die normale Fehlerbehebung für diese Einheit; lösche niemals stock und setze nicht die gesamte VM zurück:

DELETE FROM raw_delivery;
.import --csv --skip 1 deliveries/current.csv raw_delivery

Beende diesen Schritt in der Linux-Shell:

.quit

Die zwischengespeicherte Lieferung ist dein erstes Ergebnis. Der Bestand enthält weiterhin seine ursprünglichen drei Zeilen und Lagerplatznotizen.

Verwendbare Zeilen von ungültigen Werten trennen

In diesem Schritt speicherst du ein Prüfskript und prüfst die Lieferung mit dessen tatsächlichen Views. Beginne in Terminal im Verzeichnis /home/labex/project/supplier-desk.

Das Geschäft akzeptiert einen bekannten Lieferanten, eine nicht leere SKU und einen Titel, der mehr als nur Leerzeichen enthält. Preise müssen aus 1–6 ASCII-Ziffern bestehen und eine positive Zahl darstellen. Revisionen müssen aus 1–3 ASCII-Ziffern bestehen und eine positive Zahl darstellen. Führende Nullen sind erlaubt. Diese engen Grenzen sind die Eingabevorgaben, kein Anspruch darauf, beliebige Zahlenformate zu validieren.

CREATE VIEW name AS SELECT ... speichert eine Abfrage unter einem Namen. DROP VIEW IF EXISTS entfernt nur diese gespeicherte Abfrage, falls sie vorhanden ist; die Quelltabelle wird dabei nicht entfernt. WHERE behält Zeilen, deren Bedingung wahr ist. BETWEEN 1 AND 6 schließt beide Grenzen ein; NOT BETWEEN erkennt Werte außerhalb dieser Grenzen.

Ein CASE-Ausdruck wählt den ersten zutreffenden Grund aus. WHEN gibt eine Bedingung an und THEN deren Ergebnis; ohne Übereinstimmung ergibt sich NULL, was hier keinen Fehler bedeutet. IS NULL prüft dieses Fehlen. OR verbindet alternative Gründe, einen Wert abzulehnen. NOT IN (SELECT code FROM suppliers) prüft, ob der Lieferant nicht in der Lieferantentabelle enthalten ist. trim entfernt umgebende Leerzeichen, und length zählt Zeichen.

Für die Zahlenprüfung erkennt GLOB '*[^0-9]*' jedes Zeichen außerhalb der ASCII-Ziffern: * steht für eine beliebige Zeichenfolge und [^0-9] für ein einzelnes Zeichen, das keine Ziffer ist. Die separate Längenprüfung lehnt leere Zeichenfolgen ab. CAST(... AS INTEGER) wandelt Text in eine Ganzzahl um, wird aber erst nach dessen Ziffern- und Längenprüfungen verwendet. Eine Typumwandlung allein ist unsicher: Text wie 12kits darf nicht als 12 akzeptiert werden.

Du erstellst vier Views. delivery_check bewahrt die ursprünglichen Werte und einen Grund auf. typed_delivery wandelt nur gültige Zeilen um. delivery_errors listet die Nummern abgelehnter Zeilen und deren Gründe auf. delivery_ready stellt Werte für eine spätere Aktualisierung bereit. Das Skript entfernt nur diese benannten Views, damit es erneut ausgeführt werden kann; es löscht niemals Bestands- oder Quellzeilen.

Der nächste Linux-Befehl speichert dieses SQL in inspect.sql. > ersetzt die Zieldatei. <<'SQL' beginnt ein wörtliches Here-Dokument: Füge den gesamten Block ein, einschließlich des abschließenden SQL allein in einer Zeile. Führe diesen cat-Befehl nicht in SQLite aus.

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

Wenn das Terminal weiter auf Eingaben wartet, fehlt die Endmarkierung: Gib SQL allein ein. Hast du dich in einer Zeile vertippt, wiederhole den vollständigen Block, um das Skript zu ersetzen. Du benötigst weder einen Editor noch ein Zurücksetzen.

Führe ein Skript mit der Linux-Eingabeumleitung < aus. -bail weist SQLite an, beim ersten Fehler anzuhalten, statt mit späteren Anweisungen fortzufahren. Bei Erfolg wird keine Ausgabe erwartet:

sqlite3 -bail stock.db < inspect.sql

Verwende bei einem gewöhnlichen Dateinamen- oder Syntaxfehler ls, kehre mit dem absoluten cd aus Schritt 1 zurück, korrigiere inspect.sql mithilfe des vollständigen Blocks und führe es erneut aus. Der Bestand wurde nicht verändert.

SQLite akzeptiert auch eine einzelne SQL-Anweisung in Anführungszeichen nach dem Datenbankpfad. -header -column wählt eine lesbare Anzeige für diesen einzelnen Befehl. Die Anführungszeichen halten Leerzeichen und Semikolons in einem einzigen Shell-Argument zusammen:

sqlite3 -header -column stock.db 'SELECT * FROM delivery_errors ORDER BY delivery_line;'

Erwartet wird:

delivery_line  reason
-------------  ----------------
4              invalid price
5              unknown supplier
6              missing SKU

Der Grund gehört zu einer Lieferzeile, sodass ein abgelehnter Artikel zur Quelle zurückverfolgt werden kann. Kein Quellwert wurde korrigiert oder gelöscht.

sqlite3 -header -column stock.db 'SELECT * FROM delivery_ready ORDER BY supplier,sku;'

Erwartet werden drei akzeptable Zeilen: Norths Schraube mit 135 Cent/Revision 3, Reparaturband mit 450/Revision 1 und Wests Schraube mit 190/Revision 2. Die beiden Schrauben bleiben getrennte Identitäten. typeof zeigt den tatsächlichen SQLite-Werttyp an:

sqlite3 -header -column stock.db 'SELECT sku,typeof(price_cents),typeof(revision) FROM delivery_ready ORDER BY supplier,sku;'

Beide Typspalten sollten integer anzeigen. Lies zum Schluss den tatsächlichen Bestand:

sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'

Er enthält weiterhin seine ursprünglichen Werte und Lagerplatznotizen. Du hast eine wiederverwendbare Wareneingangsprüfung vorbereitet; die Änderungen des Lieferanten hast du noch nicht übernommen. Das nächste unabhängige angeleitete Lab beginnt mit einer neuen Datenbank und einer größeren zwischengespeicherten Lieferung.

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.