# excel-import Generisches Kommandozeilen-Tool zum Import von Tabellendateien in Oracle- und PostgreSQL-Datenbanken. Unterstützte Formate: | Format | Endung | Paket | |--------|--------|-------| | Excel 97–2003 | `.xls` | `xlrd` | | Excel 2007+ | `.xlsx`, `.xlsm`, `.xlsb` | `openpyxl` | | OpenDocument (LibreOffice) | `.ods` | `odfpy` | ## Voraussetzungen - Python 3.10+ - Für PostgreSQL: `psycopg2-binary` - Für Oracle: `oracledb` (kein Oracle Client nötig, nutzt den Thin-Modus) ## Installation ```bash pip install -e . ``` ## Schnellstart ```bash # 1. Struktur der Excel-Datei anzeigen excel-import inspect meine_datei.xlsx # 2. Starter-Konfiguration automatisch erzeugen excel-import generate-config meine_datei.xlsx --dsn "postgresql+psycopg2://user:pass@localhost/mydb" # 3. Konfiguration anpassen (siehe unten) # 4. Import ausführen excel-import run meine_datei.xlsx import_config.yaml ``` --- ## Befehle ### `inspect` Zeigt die Sheets, Spalten und Zeilenzahl einer Excel-Datei — ohne Datenbank-Verbindung. ```bash excel-import inspect datei.xlsx ``` Beispielausgabe: ``` Sheets in datei.xlsx: [0] Artikel Columns (4): Artikelnummer, Bezeichnung, Preis, Interne Notiz Rows: 1250 [1] Kunden Columns (3): Kundennummer, Name, E-Mail Rows: 340 ``` --- ### `generate-config` Erzeugt eine YAML-Konfigurationsdatei aus der Struktur der Excel-Datei. Die Datei kann danach manuell angepasst werden. ```bash excel-import generate-config datei.xlsx \ --dsn "postgresql+psycopg2://user:pass@localhost/mydb" \ --output config.yaml ``` | Option | Standard | Beschreibung | |--------|----------|--------------| | `--dsn` | PostgreSQL-Beispiel | SQLAlchemy-DSN der Zieldatenbank | | `--output` / `-o` | `import_config.yaml` | Ausgabedatei | --- ### `run` Führt den Import anhand einer YAML-Konfigurationsdatei durch. ```bash excel-import run datei.xlsx config.yaml excel-import run datei.xlsx config.yaml --verbose ``` | Option | Beschreibung | |--------|--------------| | `-v` / `--verbose` | Ausführliche Ausgabe inkl. SQL-Details | --- ## Konfigurationsdatei Die YAML-Konfiguration steuert, welche Sheets importiert werden, wie Spalten gemappt werden und welcher Import-Modus verwendet wird. ```yaml # SQLAlchemy DSN — Beispiele: # PostgreSQL: postgresql+psycopg2://user:pass@localhost/mydb # Oracle: oracle+oracledb://user:pass@localhost:1521/?service_name=MYDB dsn: "postgresql+psycopg2://user:pass@localhost/mydb" default_varchar_length: 255 # Fallback-Länge für Text-Spalten sheets: - sheet: "Artikel" # Sheet-Name oder Index (0, 1, ...) header_row: 0 # 0-basierter Zeilenindex der Kopfzeile skip_rows: 0 # Zeilen vor der Kopfzeile überspringen target_table: "artikel" # Ziel-Tabelle (wird angelegt, falls nicht vorhanden) mode: "replace" # append | replace | upsert upsert_keys: [] # Primärschlüssel-Spalten für Upsert columns: - source: "Artikelnummer" target: "artikelnummer" dtype: "VARCHAR(50)" # optional: Typ-Override - source: "Bezeichnung" target: "bezeichnung" - source: "Preis" target: "preis" dtype: "NUMERIC(12,2)" - source: "Interne Notiz" target: "interne_notiz" skip: true # Spalte nicht importieren ``` ### Import-Modi | Modus | Verhalten | |-------|-----------| | `append` | Zeilen werden an die bestehende Tabelle angehängt | | `replace` | Tabelle wird geleert (TRUNCATE), dann neu befüllt | | `upsert` | Zeilen werden eingefügt oder aktualisiert (anhand `upsert_keys`) | ### Spalten-Konfiguration | Feld | Pflicht | Beschreibung | |------|---------|--------------| | `source` | ja | Spaltenname in der Excel-Datei | | `target` | ja | Spaltenname in der Datenbank | | `dtype` | nein | Typ-Override, z.B. `VARCHAR(100)`, `NUMERIC(12,2)`, `DATE` | | `skip` | nein | Spalte komplett ignorieren (`true`/`false`) | Unterstützte Typ-Overrides: `VARCHAR(n)`, `TEXT`, `CLOB`, `INTEGER`, `NUMBER`, `NUMERIC(p,s)`, `DECIMAL(p,s)`, `FLOAT`, `DATE`, `DATETIME`, `TIMESTAMP`, `BOOLEAN`. Ohne `dtype` erkennt das Tool den Typ automatisch aus den Daten. ### Verbindungs-DSN **PostgreSQL:** ``` postgresql+psycopg2://user:password@host:5432/datenbankname ``` **Oracle (Thin-Modus, kein Client nötig):** ``` oracle+oracledb://user:password@host:1521/?service_name=MYSERVICE ``` ``` oracle+oracledb://user:password@host:1521/SID ``` --- ## Tabellen-Verwaltung Existiert die Ziel-Tabelle noch nicht, wird sie automatisch angelegt. Der Spaltentyp wird aus den Daten und der Spalten-Konfiguration abgeleitet. Enthält `target_table` einen Punkt (z.B. `dl.tabelle1`), wird der Teil vor dem Punkt als Datenbankschema interpretiert. Existiert dieses Schema noch nicht, wird es ebenfalls automatisch angelegt. Existiert die Tabelle bereits, werden die Daten entsprechend dem gewählten Modus eingefügt — die Tabellenstruktur wird **nicht** geändert. Soll die Struktur angepasst werden, muss die Tabelle vorher manuell geändert oder gelöscht werden. --- ## Beispiele ### Vollständiger Import mit Upsert ```yaml dsn: "postgresql+psycopg2://user:pass@localhost/mydb" sheets: - sheet: "Kunden" target_table: "kunden" mode: "upsert" upsert_keys: ["kundennummer"] columns: - source: "Kundennummer" target: "kundennummer" dtype: "VARCHAR(20)" - source: "Name" target: "name" - source: "E-Mail" target: "email" ``` ### Sheet per Index ansprechen ```yaml sheets: - sheet: 2 # drittes Sheet (0-basiert) header_row: 1 # Kopfzeile ist in Zeile 2 (0-basiert: 1) skip_rows: 0 target_table: "rohdaten" mode: "append" columns: [] # alle Spalten importieren, keine Umbenennung ``` ### Mehrere Sheets in einem Lauf ```yaml dsn: "oracle+oracledb://scott:tiger@localhost:1521/?service_name=ORCL" sheets: - sheet: "Artikel" target_table: "artikel" mode: "replace" columns: - source: "ArtNr" target: "art_nr" dtype: "VARCHAR(50)" - source: "Preis" target: "preis" dtype: "NUMERIC(12,2)" - sheet: "Lager" target_table: "lagerbestand" mode: "replace" columns: [] ``` --- ## Projektstruktur ``` excel-import/ ├── pyproject.toml ├── examples/ │ └── import_config.yaml ├── excel_import/ │ ├── config.py # Konfigurationsklassen + YAML-Loader │ ├── reader.py # Excel-Einlesen (.xls + .xlsx) │ ├── schema.py # Automatische Typ-Erkennung + DDL │ ├── importer.py # Import-Logik (append/replace/upsert) │ └── cli.py # Kommandozeilen-Interface └── tests/ ├── test_config.py ├── test_reader.py └── test_importer.py ``` ## Tests ausführen ```bash pip install -e ".[dev]" pytest ```