-- Demo-Datenbank für eine Datenbank-Verbindung (lesender Zugriff über den Datenbank-Treiber für PostgreSQL). -- Anlegen: psql postgresql://aiw:aiw@127.0.0.1:55433/postgres -c "CREATE DATABASE demo_shop" -- psql postgresql://aiw:aiw@127.0.0.1:55433/demo_shop -f beispiele/demo_shop.sql -- Im Workspace unter Einstellungen → MCP-Server → Datenbank hinzufügen: Host localhost, Port 55433, Datenbank demo_shop, -- Benutzer shop_leser, Passwort shop-leser-demo, TLS aus (nur lokal). DROP SCHEMA IF EXISTS shop CASCADE; CREATE SCHEMA shop; COMMENT ON SCHEMA shop IS 'Erfundene Shop-Daten für Tests'; CREATE TABLE shop.kunden ( id serial PRIMARY KEY, name text NOT NULL, stadt text NOT NULL, segment text NOT NULL CHECK (segment IN ('Privat', 'Gewerbe', 'Öffentlich')), seit date NOT NULL ); COMMENT ON TABLE shop.kunden IS 'Kundenstamm'; CREATE TABLE shop.produkte ( id serial PRIMARY KEY, name text NOT NULL, kategorie text NOT NULL, listenpreis numeric(10, 2) NOT NULL ); COMMENT ON TABLE shop.produkte IS 'Sortiment mit Listenpreis in Euro'; CREATE TABLE shop.bestellungen ( id serial PRIMARY KEY, kunde_id int NOT NULL REFERENCES shop.kunden, bestellt_am date NOT NULL, status text NOT NULL CHECK (status IN ('offen', 'versendet', 'storniert')) ); COMMENT ON TABLE shop.bestellungen IS 'Bestellköpfe; Positionen in shop.positionen'; CREATE TABLE shop.positionen ( bestellung_id int NOT NULL REFERENCES shop.bestellungen, produkt_id int NOT NULL REFERENCES shop.produkte, menge int NOT NULL, preis numeric(10, 2) NOT NULL, PRIMARY KEY (bestellung_id, produkt_id) ); COMMENT ON TABLE shop.positionen IS 'Bestellpositionen mit tatsächlichem Stückpreis (nach Rabatt)'; SELECT setseed(0.42); INSERT INTO shop.produkte (name, kategorie, listenpreis) VALUES ('Laptop Pro 14', 'Hardware', 1499), ('Monitor 27"', 'Hardware', 329), ('Dockingstation', 'Zubehör', 189), ('Headset Office', 'Zubehör', 79.90), ('Office-Lizenz', 'Software', 129), ('Cloud-Speicher 1 TB', 'Software', 99), ('Wartungsvertrag', 'Service', 450), ('Schulung (Tag)', 'Service', 890); INSERT INTO shop.kunden (name, stadt, segment, seit) SELECT (ARRAY['Müller', 'Schmidt', 'Schneider', 'Fischer', 'Weber', 'Meyer', 'Wagner', 'Becker', 'Hoffmann', 'Koch'])[1 + (g % 10)] || ' ' || (ARRAY['GmbH', 'AG', 'KG', '', 'e.K.'])[1 + (g % 5)], (ARRAY['Hamburg', 'München', 'Köln', 'Berlin', 'Leipzig', 'Stuttgart', 'Dresden', 'Bremen'])[1 + floor(random() * 8)::int], (ARRAY['Privat', 'Gewerbe', 'Gewerbe', 'Öffentlich'])[1 + floor(random() * 4)::int], date '2019-01-01' + floor(random() * 2000)::int FROM generate_series(1, 120) g; INSERT INTO shop.bestellungen (kunde_id, bestellt_am, status) SELECT 1 + floor(random() * 120)::int, date '2025-01-01' + floor(random() * 365)::int, (ARRAY['versendet', 'versendet', 'versendet', 'versendet', 'offen', 'storniert'])[1 + floor(random() * 6)::int] FROM generate_series(1, 800); INSERT INTO shop.positionen (bestellung_id, produkt_id, menge, preis) SELECT DISTINCT ON (b.id, p.id) b.id, p.id, 1 + floor(random() * 5)::int, round(p.listenpreis * (1 - (ARRAY[0, 0, 0, 0.05, 0.1])[1 + floor(random() * 5)::int]), 2) FROM shop.bestellungen b CROSS JOIN LATERAL (SELECT id, listenpreis FROM shop.produkte ORDER BY random() + b.id * 0 LIMIT 1 + floor(random() * 3)::int) p; ANALYZE shop.kunden, shop.produkte, shop.bestellungen, shop.positionen; -- Rolle, die nur lesen darf DO $$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'shop_leser') THEN CREATE ROLE shop_leser LOGIN PASSWORD 'shop-leser-demo'; END IF; END $$; GRANT CONNECT ON DATABASE demo_shop TO shop_leser; GRANT USAGE ON SCHEMA shop TO shop_leser; GRANT SELECT ON ALL TABLES IN SCHEMA shop TO shop_leser;