19 Funktion

MySQL-Funktionen sind ähnlich wie Stored Procedures, mit dem Hauptunterschied, dass sie einen Wert zurückgeben müssen. Sie können in SQL-Anweisungen wie normale MySQL-Funktionen verwendet werden.

19.1 Einfache Funktion erstellen

DELIMITER //

CREATE FUNCTION rabatt_berechnen(
    p_preis DECIMAL(10,2),
    p_kundentyp CHAR(1)
) 
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    DECLARE v_rabatt DECIMAL(10,2);
    
    CASE p_kundentyp
        WHEN 'A' THEN SET v_rabatt = p_preis * 0.1;
        WHEN 'B' THEN SET v_rabatt = p_preis * 0.05;
        ELSE SET v_rabatt = 0;
    END CASE;
    
    RETURN v_rabatt;
END //

DELIMITER ;

-- Verwendung in einer Abfrage
SELECT 
    produkt_id,
    preis,
    kundentyp,
    rabatt_berechnen(preis, kundentyp) AS rabatt
FROM produkte
JOIN kunden USING (kunde_id);

19.2 Deterministische vs. Nicht-deterministische Funktionen

-- Deterministische Funktion (gleiche Eingabe = gleiche Ausgabe)
CREATE FUNCTION mwst_berechnen(
    p_nettobetrag DECIMAL(10,2)
) 
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    RETURN p_nettobetrag * 0.19;
END;

-- Nicht-deterministische Funktion
CREATE FUNCTION zufallspreis(
    p_min DECIMAL(10,2),
    p_max DECIMAL(10,2)
)
RETURNS DECIMAL(10,2)
NOT DETERMINISTIC
BEGIN
    RETURN p_min + RAND() * (p_max - p_min);
END;

19.3 Funktionen mit komplexer Logik

DELIMITER //

CREATE FUNCTION kundenrabatt_berechnen(
    p_kunden_id INT
)
RETURNS DECIMAL(5,2)
READS SQL DATA
BEGIN
    DECLARE v_umsatz DECIMAL(10,2);
    DECLARE v_kundentyp CHAR(1);
    DECLARE v_rabatt DECIMAL(5,2);
    
    -- Gesamtumsatz des Kunden ermitteln
    SELECT SUM(betrag)
    INTO v_umsatz
    FROM bestellungen
    WHERE kunde_id = p_kunden_id
    AND datum >= DATE_SUB(CURRENT_DATE, INTERVAL 1 YEAR);
    
    -- Kundentyp ermitteln
    SELECT kundentyp
    INTO v_kundentyp
    FROM kunden
    WHERE kunde_id = p_kunden_id;
    
    -- Rabatt basierend auf Umsatz und Kundentyp
    SET v_rabatt = CASE
        WHEN v_kundentyp = 'A' AND v_umsatz >= 10000 THEN 15
        WHEN v_kundentyp = 'A' AND v_umsatz >= 5000 THEN 10
        WHEN v_kundentyp = 'B' AND v_umsatz >= 10000 THEN 10
        WHEN v_kundentyp = 'B' AND v_umsatz >= 5000 THEN 5
        ELSE 0
    END;
    
    RETURN v_rabatt;
END //

DELIMITER ;

19.4 Funktionen mit Tabellenverarbeitung

DELIMITER //

CREATE FUNCTION lagerbestand_pruefen(
    p_produkt_id INT
)
RETURNS VARCHAR(20)
READS SQL DATA
BEGIN
    DECLARE v_bestand INT;
    DECLARE v_min_bestand INT;
    
    -- Aktuellen und minimalen Bestand ermitteln
    SELECT lagerbestand, mindestbestand
    INTO v_bestand, v_min_bestand
    FROM produkte
    WHERE produkt_id = p_produkt_id;
    
    -- Status zurückgeben
    RETURN CASE
        WHEN v_bestand IS NULL THEN 'Unbekannt'
        WHEN v_bestand = 0 THEN 'Nicht verfügbar'
        WHEN v_bestand < v_min_bestand THEN 'Nachbestellen'
        ELSE 'Verfügbar'
    END;
END //

DELIMITER ;

-- Verwendung in einer Abfrage
SELECT 
    produkt_id,
    bezeichnung,
    lagerbestand,
    lagerbestand_pruefen(produkt_id) AS status
FROM produkte;

19.5 Datum- und Zeitfunktionen

DELIMITER //

CREATE FUNCTION arbeitstage_berechnen(
    p_start_datum DATE,
    p_end_datum DATE
)
RETURNS INT
DETERMINISTIC
BEGIN
    DECLARE v_tage INT;
    DECLARE v_arbeitstage INT;
    DECLARE v_datum DATE;
    
    SET v_tage = DATEDIFF(p_end_datum, p_start_datum);
    SET v_arbeitstage = 0;
    SET v_datum = p_start_datum;
    
    WHILE v_datum <= p_end_datum DO
        -- Wenn kein Wochenende
        IF DAYOFWEEK(v_datum) NOT IN (1, 7) THEN
            SET v_arbeitstage = v_arbeitstage + 1;
        END IF;
        SET v_datum = DATE_ADD(v_datum, INTERVAL 1 DAY);
    END WHILE;
    
    RETURN v_arbeitstage;
END //

DELIMITER ;

19.6 String-Funktionen

DELIMITER //

CREATE FUNCTION formatiere_telefon(
    p_telefon VARCHAR(20)
)
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
    DECLARE v_nummer VARCHAR(20);
    
    -- Nur Zahlen behalten
    SET v_nummer = REGEXP_REPLACE(p_telefon, '[^0-9]', '');
    
    -- Format: +49 (xxx) xxx-xxxx
    IF LENGTH(v_nummer) = 11 AND LEFT(v_nummer, 2) = '49' THEN
        RETURN CONCAT('+', 
            LEFT(v_nummer, 2), ' ',
            '(', SUBSTRING(v_nummer, 3, 3), ') ',
            SUBSTRING(v_nummer, 6, 3), '-',
            RIGHT(v_nummer, 4)
        );
    ELSE
        RETURN p_telefon;
    END IF;
END //

DELIMITER ;

Praktische Tipps:

  1. Performanceoptimierung:
-- Häufig verwendete Werte in Variable speichern
CREATE FUNCTION berechne_zwischensumme(
    p_bestell_id INT
)
RETURNS DECIMAL(10,2)
READS SQL DATA
BEGIN
    DECLARE v_summe DECIMAL(10,2);
    
    SELECT SUM(menge * einzelpreis)
    INTO v_summe
    FROM bestellpositionen
    WHERE bestell_id = p_bestell_id;
    
    RETURN COALESCE(v_summe, 0);
END;
  1. Fehlerbehandlung:
CREATE FUNCTION sichere_division(
    p_zahl1 DECIMAL(10,2),
    p_zahl2 DECIMAL(10,2)
)
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    IF p_zahl2 = 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Division durch Null!';
    END IF;
    
    RETURN p_zahl1 / p_zahl2;
END;
  1. Wiederverwendbarkeit:
-- Generische Funktion für verschiedene Anwendungsfälle
CREATE FUNCTION format_waehrung(
    p_betrag DECIMAL(10,2),
    p_waehrung CHAR(3)
)
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
    RETURN CASE p_waehrung
        WHEN 'EUR' THEN CONCAT(FORMAT(p_betrag, 2), ' €')
        WHEN 'USD' THEN CONCAT('$', FORMAT(p_betrag, 2))
        WHEN 'GBP' THEN CONCAT('£', FORMAT(p_betrag, 2))
        ELSE FORMAT(p_betrag, 2)
    END;
END;