243 lines
8.3 KiB
PL/PgSQL
243 lines
8.3 KiB
PL/PgSQL
-- ============================================================================
|
|
-- KREMPL PostgreSQL Schema
|
|
-- Reduziert 61 Millionen MySQL-Zeilen auf 68k Zeilen mit Arrays
|
|
-- ============================================================================
|
|
|
|
-- Datenbank erstellen (als postgres User ausführen)
|
|
-- CREATE DATABASE krempl_data
|
|
-- WITH ENCODING 'UTF8'
|
|
-- LC_COLLATE='de_DE.UTF-8'
|
|
-- LC_CTYPE='de_DE.UTF-8';
|
|
|
|
-- User erstellen
|
|
-- CREATE USER krempl_user WITH PASSWORD 'PASSWORT_HIER_SETZEN';
|
|
-- GRANT ALL PRIVILEGES ON DATABASE krempl_data TO krempl_user;
|
|
|
|
-- Nach \c krempl_data:
|
|
-- GRANT ALL ON SCHEMA public TO krempl_user;
|
|
|
|
-- ============================================================================
|
|
-- Extensions aktivieren
|
|
-- ============================================================================
|
|
|
|
-- Trigram-Suche für ähnliche Strings
|
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
|
|
|
-- Schnellere Array-Operationen
|
|
CREATE EXTENSION IF NOT EXISTS intarray;
|
|
|
|
-- Query-Performance Tracking (optional)
|
|
-- CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
|
|
|
-- ============================================================================
|
|
-- Tabelle 1: GERÄTE (ca. 1.4 Mio Zeilen)
|
|
-- ============================================================================
|
|
|
|
DROP TABLE IF EXISTS geraete CASCADE;
|
|
|
|
CREATE TABLE geraete (
|
|
id BIGINT PRIMARY KEY,
|
|
nr VARCHAR(100),
|
|
marke VARCHAR(100),
|
|
typ VARCHAR(200),
|
|
zusatz_nummer VARCHAR(100),
|
|
modell_bezeichnung VARCHAR(200),
|
|
produktionsstart VARCHAR(50),
|
|
produktionsende VARCHAR(50),
|
|
wgtext1 VARCHAR(200),
|
|
wgtext2 VARCHAR(200),
|
|
zusatz TEXT,
|
|
bezeichnung_original TEXT,
|
|
typ_de VARCHAR(200),
|
|
typ_fr VARCHAR(200),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Standard-Indexe für schnelle Lookups
|
|
CREATE INDEX idx_geraete_nr ON geraete(nr);
|
|
CREATE INDEX idx_geraete_marke ON geraete(marke);
|
|
CREATE INDEX idx_geraete_typ ON geraete(typ);
|
|
|
|
-- Trigram-Indexe für LIKE-Suchen (z.B. "AEG%" oder "%123%")
|
|
CREATE INDEX idx_geraete_nr_trgm ON geraete USING GIN(nr gin_trgm_ops);
|
|
CREATE INDEX idx_geraete_typ_trgm ON geraete USING GIN(typ gin_trgm_ops);
|
|
CREATE INDEX idx_geraete_marke_trgm ON geraete USING GIN(marke gin_trgm_ops);
|
|
|
|
-- Full-Text Search Index (Kombination: nr + typ + marke)
|
|
CREATE INDEX idx_geraete_fts ON geraete
|
|
USING GIN(to_tsvector('german',
|
|
COALESCE(nr,'') || ' ' ||
|
|
COALESCE(typ,'') || ' ' ||
|
|
COALESCE(marke,'') || ' ' ||
|
|
COALESCE(modell_bezeichnung,'')
|
|
));
|
|
|
|
COMMENT ON TABLE geraete IS 'Geräte-Stammdaten aus Krempl (ca. 1.4 Mio Einträge)';
|
|
|
|
-- ============================================================================
|
|
-- Tabelle 2: ERSATZTEIL-MAPPING (68k Zeilen statt 61 Mio!)
|
|
-- ============================================================================
|
|
|
|
DROP TABLE IF EXISTS ersatzteil_mapping CASCADE;
|
|
|
|
CREATE TABLE ersatzteil_mapping (
|
|
ersatzteil_id BIGINT PRIMARY KEY,
|
|
geraet_ids BIGINT[], -- Array statt Millionen einzelner Zeilen!
|
|
geraet_count INT,
|
|
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- GIN Index für Array-Suche "Welche Ersatzteile passen zu Gerät X?"
|
|
-- Ermöglicht: WHERE geraet_ids @> ARRAY[123456]
|
|
CREATE INDEX idx_mapping_geraet_array ON ersatzteil_mapping
|
|
USING GIN(geraet_ids);
|
|
|
|
-- Standard B-Tree Index für schnelle Ersatzteil-Lookups
|
|
CREATE INDEX idx_mapping_ersatzteil ON ersatzteil_mapping(ersatzteil_id);
|
|
|
|
COMMENT ON TABLE ersatzteil_mapping IS 'N:N Mapping Ersatzteil↔Gerät als Arrays (reduziert 61M Zeilen auf 68k)';
|
|
COMMENT ON COLUMN ersatzteil_mapping.geraet_ids IS 'Array aller Geräte-IDs die zu diesem Ersatzteil passen';
|
|
COMMENT ON COLUMN ersatzteil_mapping.geraet_count IS 'Anzahl Geräte (für schnelle Anzeige ohne Array-Count)';
|
|
|
|
-- ============================================================================
|
|
-- Tabelle 3: ERSATZTEILE (Metadaten, optional)
|
|
-- ============================================================================
|
|
|
|
DROP TABLE IF EXISTS ersatzteile CASCADE;
|
|
|
|
CREATE TABLE ersatzteile (
|
|
id BIGINT PRIMARY KEY,
|
|
navision_id BIGINT,
|
|
originalnummer VARCHAR(100),
|
|
marke VARCHAR(100),
|
|
ean VARCHAR(50),
|
|
altenr VARCHAR(100),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Indexe für Join mit Shop-Items
|
|
CREATE INDEX idx_ersatzteile_navision ON ersatzteile(navision_id);
|
|
CREATE INDEX idx_ersatzteile_original ON ersatzteile(originalnummer);
|
|
CREATE INDEX idx_ersatzteile_ean ON ersatzteile(ean);
|
|
|
|
COMMENT ON TABLE ersatzteile IS 'Ersatzteil-Metadaten aus Krempl';
|
|
|
|
-- ============================================================================
|
|
-- Tabelle 4: PASSENDWIE (optional)
|
|
-- ============================================================================
|
|
|
|
DROP TABLE IF EXISTS passendwie CASCADE;
|
|
|
|
CREATE TABLE passendwie (
|
|
row_id SERIAL PRIMARY KEY, -- Auto-increment ID für interne Zwecke
|
|
id BIGINT NOT NULL, -- Krempl Ersatzteil-ID (NICHT unique!)
|
|
navision_id BIGINT,
|
|
vertreiber_id VARCHAR(100),
|
|
vertreiber VARCHAR(200),
|
|
bestellcode VARCHAR(200),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
-- Eindeutigkeit: Ein Ersatzteil kann mehrere Vertreiber-Codes haben
|
|
UNIQUE(id, vertreiber_id, bestellcode)
|
|
);
|
|
|
|
CREATE INDEX idx_passendwie_id ON passendwie(id);
|
|
CREATE INDEX idx_passendwie_navision ON passendwie(navision_id);
|
|
CREATE INDEX idx_passendwie_vertreiber ON passendwie(vertreiber_id);
|
|
CREATE INDEX idx_passendwie_bestellcode ON passendwie(bestellcode);
|
|
|
|
COMMENT ON TABLE passendwie IS 'PassendWie-Zuordnungen: Ein Ersatzteil hat mehrere Vertreiber-Bestellcodes (206k Zeilen)';
|
|
|
|
-- ============================================================================
|
|
-- Hilfsfunktionen
|
|
-- ============================================================================
|
|
|
|
-- Funktion: Zähle Geräte in Array (Alternative zu geraet_count-Spalte)
|
|
CREATE OR REPLACE FUNCTION count_geraete(ersatzteil_id_param BIGINT)
|
|
RETURNS INT AS $$
|
|
SELECT COALESCE(array_length(geraet_ids, 1), 0)
|
|
FROM ersatzteil_mapping
|
|
WHERE ersatzteil_id = ersatzteil_id_param;
|
|
$$ LANGUAGE SQL STABLE;
|
|
|
|
-- Funktion: Auto-Update updated_at Timestamp
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Trigger für geraete.updated_at
|
|
CREATE TRIGGER update_geraete_updated_at
|
|
BEFORE UPDATE ON geraete
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- ============================================================================
|
|
-- Views für vereinfachte Queries (optional)
|
|
-- ============================================================================
|
|
|
|
-- View: Ersatzteile mit Geräte-Anzahl
|
|
CREATE OR REPLACE VIEW v_ersatzteile_stats AS
|
|
SELECT
|
|
em.ersatzteil_id,
|
|
em.geraet_count,
|
|
e.navision_id,
|
|
e.originalnummer,
|
|
e.marke
|
|
FROM ersatzteil_mapping em
|
|
LEFT JOIN ersatzteile e ON e.id = em.ersatzteil_id
|
|
ORDER BY em.geraet_count DESC;
|
|
|
|
COMMENT ON VIEW v_ersatzteile_stats IS 'Ersatzteile mit Statistiken (Anzahl passender Geräte)';
|
|
|
|
-- ============================================================================
|
|
-- Performance-Optimierung
|
|
-- ============================================================================
|
|
|
|
-- Statistiken aktualisieren (nach Import ausführen!)
|
|
-- ANALYZE geraete;
|
|
-- ANALYZE ersatzteil_mapping;
|
|
-- ANALYZE ersatzteile;
|
|
-- ANALYZE passendwie;
|
|
|
|
-- ============================================================================
|
|
-- Grant Permissions
|
|
-- ============================================================================
|
|
|
|
-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO krempl_user;
|
|
-- GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO krempl_user;
|
|
-- GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO krempl_user;
|
|
|
|
-- ============================================================================
|
|
-- Fertig!
|
|
-- ============================================================================
|
|
|
|
-- Beispiel-Query: Geräte zu Ersatzteil
|
|
/*
|
|
SELECT g.*
|
|
FROM ersatzteil_mapping em
|
|
JOIN geraete g ON g.id = ANY(em.geraet_ids)
|
|
WHERE em.ersatzteil_id = 7764466071
|
|
LIMIT 100;
|
|
*/
|
|
|
|
-- Beispiel-Query: Ersatzteile zu Gerät
|
|
/*
|
|
SELECT ersatzteil_id
|
|
FROM ersatzteil_mapping
|
|
WHERE geraet_ids @> ARRAY[123456]::bigint[]
|
|
LIMIT 100;
|
|
*/
|
|
|
|
-- Beispiel-Query: Full-Text Suche
|
|
/*
|
|
SELECT id, nr, marke, typ
|
|
FROM geraete
|
|
WHERE to_tsvector('german', nr || ' ' || typ) @@ plainto_tsquery('german', 'AEG Kühlschrank')
|
|
LIMIT 50;
|
|
*/
|