OR-MAPPER / JPA Persistentes Objektmodell im Programm Layers: RDBMS ->JDBC Driver(JDBC Driver(Third Party)) -> JDBC API(Standard JDBC Interface)->JPA Provider(JPA Implementierung( Third Party))->Java Persistence API (Standard ORM Interface (JEE)-> Java Program(Applikation mit pers. Objekten) Entities als persistente Objekte, POJO, Abbildung zur DB mit Annotations oder XML-File getter und setter nötig, kann erben und vererben, kann interfaces und abstract. Public odo prot.; Konstr ohne Argumente, keine finals, fields private odo prot. Default 1:1 Mapping; JPA-Conf: persistence-unit: name=Class; Angaben von Connstringparameter Schemaerstellungapproaches: Top down(businessmodel->db), Bottom up(db->business), middle-out(metadaten->db und business), meet middle @Entity: Klasse persistieren; @Id: Primary key, @Table(name=“tablename“), @Column(name=“columnnameindb“, unique=bool, optional=false, length=200, scale=10, precision=2) //alles ausser name optional @GeneratedValue bei Id (Strategy=GenerationType[AUTO,IDENTITY,SEQUENCE,TABLE]), @Temporal(TemporalType.TIMESTAMP, DATE, TIME) > Infos für Date/Time typen, Enums : Annotation mit @Enumerated(EnumType.STRING) , @Transient;nicht gespeichert; Unterstützt: Prim, Wrapper,Str,Enum,Byte/CharArray, Date, Cal, SerializableKlassen; Relationen:Collection von Entities, Referenzen Property Access : Annotation bei Getter, Bei Boolean auch isX Mapping: @OneToOne(optional=true), @JoinColumn(name=“namewieindb“, referencedColumName=“name“ insertable=bool, updatable=bool), @OneToOne(mappedBy=“attrnameinotherclass“) //invers, bezieht sich auf Attrubut in referenzierender Klasse @ManyToOne(optinal=bool)@JoinColumn(name=“customerref“) @OneToMany @JoinColumn(name=‘’dbfieldinrefernzierterklasse’’, referencedColumnName=’’EigenerPrimaryKeyInCode) @ManyToMany @JoinTable(name=“tablenameindb’’, joinColumns = {@JoinColumn(name=’’eigenerefinjointable’’)}, inverseJoinColumns = {@JoinColumn(name=’’Andererefinjointable’’)}) @ManyToMany(mappedBy =‘‘bankCustomers‘‘) //Hier AttributName im Code der anderen Klasse, über der ManyToMany steht ->name muss jeweils wie in db geschrieben sein Beispiel CRUD: EntityManagerFactory factory = Persistence.createEntityManagerFactory(‘’Bank’’) ; EntityManager em = factory.createEntityManager() ; //Neue Session !; Query query = em.createQuery(‘select a from BankAccount a’) ; List<BankAccount> l = query.getResultList() ;em.close() //besser mit finally Create : em.getTransaction().begin() ; BankCustomer c = new BankCustomer() ; c.setName(‚Bill‘);em.persist(c); em.getTransaction().commit();em.close() Update : //in transaction wrappen : BankAccount a = em.find(BankAccount.class, 1L)//Suche durch Primary key ; account.incBalance(200); Delete : //in transaction wrappen : em.remove(customer) ; Refresh: em.refresh(customer) -> In Datenbank anpassen Eager Loading: Target direkt laden, default by OneToOne ManyToOne, Lazy: erst wenn Zugriff. @OneToOne(fetch = FetchType.LAZY) Persistence Unit Menge von Entity Klassen und deren Mapping Bestimmt JPA Provider und DB-Anbindung Durch META-INF / persistence.xml beschrieben EntityManagerFactory Verwaltet eine Persistence Unit Persistence Context Verwaltet Menge von Entity Instanzen zur Laufzeit Entity ist managed => in Persistence Context Entity new oder detached => gehört zu keinem Context Definiert transaktionelle Session Entity Manager Verwaltet Persistence Con text Bietet Lifecycle-Operationen für Entity Instanzen an Abbildung, Typ-Diskriminator -> Sind immer nur bei Parentklasse zu notieren! Ausser natürlich Diskriminator-value @Inheritance(strategy = InheritanceType.SINGLE_TABLE) : Default, Alle Unterklassen in Obeklassen, effizient, Unnötigee Nulls, diskriminator nötig @DiscrimatorColumn(name= »type ») //Bei Parentklasse @DiscriminatorValue(« Retail ») //Bei Childklasse InheritanceType.Joined : Jede Klasse abgebildet und Joined, Viele Tabellen, Komplizierter, Diskriminator nötig, keine unnötige Columns -> Hierbei ist es noch nötig, bei allen Subklassen das Attribut @PrimaryKeyJoinColumn(name="idInParent") zu setzen, bei der Parentklasse wie folgt den Diskriminator vorzubereiten: @DiscriminatorColumn(name="type", discriminatorType =DiscriminatorType.STRING), @DiscriminatorValue(‘‘DistinctValue‘‘) Bei Kind TABLE_PER_CLASS: Oberklasse und Unterklasse, Gemeinsames Attributmapping, Nachteile bei Query Probleme mit Ordering Joins Relation mit Abhänbgigkeiten: @OneToMany(cascade=CascadeType.PERSIST, REMOVE) Konsistenz->wenn n :m Beziehung Konsistenz sein sollen, dann sollte man dies über add/remove Methoden jeweils überprüfen Dynamic Queries Anfragen mit JPQL: Übersetzt in SQL. Beispiel: -> Alle Klassennamen und Attribute wie in Code schreiben !!! Query q = em.createQuery(Select a from BankCustimer c join c.accounts a); select distict a.id, a.balance from BankAccount a where a.balance > 0 order by a.balance.desc Query Params : select a from BankAccount a where a.customer.name like ?1 ; select a from BankAccount a where a.customer.name like :name ; query.setParameter(1, « bob »), query.setParameter(« name », « Marc »), Verhinderung SQL Injection Query query = em.createQuery(« SELECT c FROM BANKCustomer c where c.name like :customerName ») ; query.setParameter(« customername », name) ; query.setMaxResults(1000) ; List<BankCustomer> list = query.getResultList() ; NamedQuery bei Klasse @NamedQuery(name= »x », query= ‘’SELECT c.name, SUM(a.balance) FROM BankCustomer c JOIN c.bankAccounts a GROUP BY c.name") ; em.createNamedQuery(« x ») ; vorgeparst optimiert -> Mit List<Object[]> list = query.getResultList() ; Beim iterieren dann mit index[0], index[1] Zugriff ->Namen von Attributen und Tablenamen immer so schreiben wie im Code definiert, nicht wie in Tabellen! Em.getransaction().begin()/.commit() ; Locking mit em.lock(entity, LockModeTyoe.PESSIMISTIC_WRITE) Beispiel Locking : em.getTransaction().begin(); BankAccount from =em.find(BankAccount.class, fromAccountId); BankAccount to = em.find(BankAccount.class, toAccountId); em.lock(from, LockModeType.PESSIMISTIC_WRITE); em.lock(to, LockModeType.PESSIMISTIC_WRITE); from.setBalance( from.getBalance() - amount); to.setBalance(to.getBalance() + amount); em.getTransaction().commit();em.refresh(from); em.refresh(to); STORED PROCEDURES Konsistenzprüfung, nahe bei Daten, Sicherheit(Verfeinerter Zugriffsschutz, Sicherheitsprüf mit Triggers, Auditing von Zugriffen mit Triggern), Datenbankobjekte, Aus anderen SP, Triggern, PL/SQL-Blöcken, Clientappl aufrufbar; Rechte: Grant Execute on Funktion to userxy; +: Alle Überprüfungen zentral, Zugriff nur via Prozduren, Verbessert Leistungsfähigkeit im Client-Server Zugriffsschutz mit Stored Procedures: Benutzer modifiziert über SP Daten, SP kapsen Daten und implementieren, Benutzer benötigen keine Privilegien für Modifikation der darunterliegenden Tabelle, Procedures laufen mit Rechten ihres Erstellers, Sollten vom Owner der Tabellen erstellt werden Procedure hat keinen Returnvalue und kann nicht in SQL gerufen werden, Function beides! Prozedur-Parameter In Postgresql: Eine Stored Procedure ist immer eine Funktion! CREATE [OR REPLACE] FUNCTION name ( [argname] argtype [,…]]) [RETURNS rettype | RETURNS TABLE (colname coltype [,…] ) ] AS $$ ..source.. $$ LANGUAGE plpgsql Ausgaben: RAISE NOTICE ‘Hello World!‘; CREATE OR REPLACE FUNCTION suche(matrnr numeric(10)) RETURNS void AS $$ DECLARE name varchar(30); semester numeric(2); BEGIN SELECT s.name, s.semester INTO name, semester FROM students s WHERE s.matrnr = matrnr; IF (name IS NULL) THEN RAISE NOTICE ‘Leider nicht gefunden‘; ELSE RAISE NOTICE ‘Name: %, Semester: %‘, name, semester; END IF; END; $$ LANGUAGE plpgsql; SELECT suche(0620611); CREATE OR REPLACE FUNCTION pow_mod(bigx bigint, n bigint, m bigint) RETURNS bigint AS $$ DECLARE x bigint; xx bigint; BEGIN IF n = 0 THEN RETURN 1; END IF; X:= bigx % m; Xx:=(x*x) % m; IF n % 2 = 0 THEN RETURN pow_mod(xx, n/2, m); ELSE RETURN (x * pow_mod(xx, (n-1)/2, m)) %m; END IF; END; $$ LANGUAGE plpgsql; PL/SQL(prozed. Sprache mit eingebettetem SQL Variable: nach DECLARE; Block: BEGIN .. END; Konstante: CONSTANT; Fehler anzeigen: SHOW ERRORS Beispiel: DECLARE Emp_name VARCHAR2(10); Emp_number INTEGER; Empno_out_of_range EXCEPTION; BEGIN Emp_number := 10001 ; IF Emp_number > 9999 OR Emp_number < 1000 THEN RAISE Empno_out_of_range ; ELSE SELECT Name INTO Emp_name FROM Angestellter WHERE PersNr = Emp_number ; DBMS_OUTPUT.PUT_LINE(‚Employee name is ‘|| Emp_name) ; END IF ; EXCEPTION WHEN Empno_out_of_range THEN DBMS_OUTPUT.PUT_LINE(‘Employee number ‘ || Emp_number || ‘is out of range.’) ; END ; - Employee number 10001 is out of range When: Fehlerbehandlung: TOO_MANY_ROWS, NO_DATA_FOUND, DUP_VAL_ON_INDEX Beispiel: CREATE OR REPLACE FUNCTION ZeitAnteilSumme (AngNr IN INTEGER, TotProzAnt OUT INTEGER) RETURNS INTEGER AS $$ BEGIN SELECT COALESCE(SUM( Zeitanteil ), 0) INTO TotProzAnt FROM Projektzuteilung pzt WHERE pzt.PersNr = AngNr; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION AngInProjekt (AngId IN INTEGER, ProjId IN INTEGER) RETURNS INTEGER AS $$ DECLARE p_n Projektzuteilung.ProjNr%TYPE:=-1; BEGIN SELECT (ProjNr, -1) INTO p_n FROM ProjektZuteilung WHERE PersNr = AngId AND ProjNr = ProjId; RETURN p_n; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION ProjektZuteilen (AngId IN INTEGER, ProjId IN INTEGER, ProzAnt IN DECIMAL, StartZeit IN DATE, ResCode OUT INTEGER) AS $$ DECLARE TotProzAnt ProjektZuteilung.Zeitanteil%TYPE; RealStartZeit DATE; BEGIN ResCode := 1; /*ok*/ IF( ProzAnt < 10 OR ProzAnt > 90 ) THEN ResCode := -1; RETURN; END IF; IF( AngInProjekt (AngId, ProjId) > 0 ) THEN ResCode := -3; RETURN; END IF; select ZeitAnteilSumme(AngId) into TotProzAnt; IF( (TotProzAnt + ProzAnt) > 100 ) THEN ResCode := -2; RETURN; END IF; IF( StartZeit IS NULL ) THEN RealStartZeit := now() ELSE RealStartZeit := StartZeit; END IF; INSERT INTO ProjektZuteilung VALUES ( AngId, ProjId, ProzAnt, RealStartZeit, NULL ); RETURN; EXCEPTION WHEN OTHERS THEN ResCode := -4; /*Ang oder Proj nicht vorhanden */ END; $$ LANGUAGE plpgsql; -- Test DO LANGUAGE plpgsql $$ DECLARE errcode DECIMAL:=0; zanteil integer:=0; BEGIN select projektzuteilen(1100, 25, 10, Null) into errcode; raise notice 'errcode: %', errcode; END; $$ NR bearbeitete rows: SQL%ROWCOUNT/NOTFOUND Funktionen (haben Rückgabewert) CREATE OR REPLACE FUNCTION AbteilungsSalaer (AbtName VARCHAR2) RETURNS NUMBER IS AbtSalaer NUMBER (10,2) := 0; BEGIN SELECT sum(A.salaer) INTO AbtSalaer FROM Angestellter A, Abteilung Abt WHERE A.AbtNr = Abt.AbtNr AND Abt.Name = AbtName; RETURN AbtSalaer; End AbteilungSalaer; Anstatt IS kann man auch AS schreiben. Aufruf, Verwendung in SQL: SELECT Name, AbteilungSalaer(Name) FROM Abteilung WHERE AbteilungSalaer(Name) > 40000 Aufruf aus PL/SQL: DECLARE TotSalaer Number; BEGIN TotSalaer := AbteilungSalaer(‚Entwicklung‘); Dbms_output.put_line(‚Salaer Entwicklung ‚ || TotSalaer); END; Weitere Variablen, die man benötigt, kann man nach dem IS deklarieren Baublöcke: IF (a = 10) THEN … ELSIF (a = 20 ) THEN .. ELSE .. END IF; FOR Counter in 1..20 // Mit FOR c IN REVERSE 1..15 startet c bei 15… LOOP X := Counter * 31; END LOOP; MOD(15, 4) ->3; MOD(15, 0) ->15; WHILE a > 1000 LOOP … END LOOP; Datentyp boolean gibt es, logische Operatoren: AND, OR, NOT Operationen auf String: LENGTH(myString), select SUBSTR(<string to parse>,<start position>,(<length>)) //Index startet bei 1!!! Stored Procedurs in pl/pgsql Bei OUT-Parametern diese nicht im Declare teil initialisieren!Erschafft neue Variable im Scope, die äussere ist dann null ELSEIF wird hier zusammen geschrieben!! DROP FUNCTION IF EXISTS fn3(int) ; CREATE OR REPLACE FUNCTION fn3(_persnr IN angestellter.persnr%TYPE, startZeit date DEFAULT current_date) RETURNS int – RETURNS void bei PROZEDUR ! AS $$ DECLARE Aname angestellter.name%TYPE ; Abtnr angestellter abtne%TYPE; BEGIN SELECT a.name, a.abtnr INTO aname, abtnr FROM angestellter a WHERE presnr = _persnr; RAISE NOTICE ‚DEBUG: NAME %; Abt.: %‘, aname, abtnr; --DEBUG / Warnung, bricht nicht ab RETURN abtnr; --RAISE SQLSTATE ‚22012‘; --User defined Exception! END ; $$ LANGUAGE plpgsql ; --Test Aufruf (Auch bei Prozeduren : SELECT fn3(1002); --Prozedur mit OUT Parametern: CREATE OR REPLACE FUNCTION myfn(myparam OUT int; pyparam2 out int) AS $$ BEGIN Myparam := 1 ; Myparam2 := 2 ; END ; $$ LANGUAGE plpgsql ; --AUFRUF : SELECT myparam FROM myfn() ; SELECT * FROM myfn() ; SELECT = functioktioniert nicht, immer SELECT INTO var bla….; Cursor: Handle or name for a private SQL area an area in memory inside db server in which a parsed statement, procedures or functions or kept; Problem bei erwähnten Zuweisungen von Tabellenwerten: funktioniert nur bei single-row-select; Abhilfe Cursors: Arbeiten sämtliche Tupel aus dem Ergebnis nacheinander ab-> Select-Statement liefert in Regel Menge von Tupels(Result Set)-> Cursor erlaubt sequentiellen Zugriff auf die einzelnen Tupels; Zyklus: Deklaration, Open, Fetch while not empty, close Normaler Cursor: DECLARE CurrAbtNr integer := 1; CURSOR AngCursor IS SELECT Salaer, PersNr, FROM Angestellter WHERE Angestellter.AbtNr=CurrAbtNr; SalSumme NUMBER(8, 2) := 0; AngSalaer Angestellter.Salaer%Type; AngPersNr Angestellter.PersNr%Type; BEGIN OPEN AngCursor // SQL Abfrage starten und Resultate in Puffer LOOP FETCH AngCursor INTO AngSalaer, AngPersNr; EXIT WHEN NOT FOUND SalSumme := SalSumme + AngSalaer; END LOOP; ClOSE AngCursor Mit Params: DECLARE CurrAbtNr integer; CURSOR AngCursor (ABtId in Abteilung.AbtNr%TYPE) IS SELECT Salaer, PersNr, FROM Angestellter WHERE Angestellter.AbtNr=CurrAbtNr; SalSumme NUMBER(8, 2) := 0; AngSalaer Angestellter.Salaer%Type; AngPersNr Angestellter.PersNr%Type; BEGIN FOR CurrAbtNr IN 1..2 LOOP OPEN AngCursor(CurrAbtNr); LOOP FETCH AngCursor INTO AngSalaer, AngPersNr; EXIT WHEN NOT FOUND SalSumme := SalSumme + AngSalaer; END LOOP; ClOSE AngCursor AngCursor%ROWTYPE -> Liefert Typ des Tupels -> NUR ORACLE!!!! Cursor for Update: CURSOR AngCursor IS SELECT Salaer, Chef, PersNr FROM Angestellter WHERE Angestellter.AbtNr=CurrAbtNr FOR UPDATE; OPEN AngCursor; LOOP FETCH AngCursor INTO AngRec //Positioniert Cursor auf das aktuelle Tupel, es wird gesperrt EXIT WHEN NOT FOUND UPDATE Angestellter SET Salaer = MinSaler WHERE CURRENT OF AngCursor; // Ändert das durch den Cursor refernzierte Tupel END LOOP; CLOSE AngCursor; Cursor mit Delete: LOOP FETCH GruppenCursor INTO gruppe,punktezahl; EXIT WHEN NOT FOUND;Punktezahl := punktezahl * 2; DELETE FROM Gruppen WHERE CURRENT OF GruppenCursor; INSERT INTO Gruppen VALUES(gruppe, punktezahl); END LOOP; Cursor Postgres: DROP TABLE IF EXISTS Top5; CREATE TABLE Top5 (Name VARCHAR(20), Persnr NUMERIC, Salaer DECIMAL(7,2); truncate Top5; DO LANGUAGE plpgsql $$ DECLARE c1 CURSOR IS SELECT name, persnr, salaer FROM angestellter ORDER BY salaer DESC; -- start with highest-paid angestellter my_name CHAR(20); my_persnr NUMERIC; my_salaer NUMERIC(7,2); BEGIN OPEN c1; FOR i IN 1..5 LOOP FETCH c1 INTO my_name, my_persnr, my_salaer; EXIT WHEN NOT FOUND; /* in case the number requested is more than the total number of employees */ INSERT INTO top5 VALUES (my_name, my_persnr, my_salaer); END LOOP; CLOSE c1;END; $$ Ref Cursor in Schema; PostgreSQL CREATE SCHEMA ANGPACKAGE AUTHORIZATION anguser; CREATE FUNCTION ANGPACKAGE.AbteilungsAngestellte(Abteilungsnummer IN INTEGER) RETURNS refcursor AS $$ DECLARE ref refcursor; BEGIN OPEN ref FOR select * from angestellter ang where ang.AbtNr = Abteilungsnummer; RETURN ref; END; $$ LANGUAGE plpgsql; DO LANGUAGE plpgsql $$ DECLARE AbtNr integer := 3; thecursor refcursor; angrec Angestellter%rowtype; abtname Abteilung.Name%TYPE; BEGIN select ANGPACKAGE.AbteilungsAngestellte(AbtNr) into thecursor; LOOP fetch NEXT from thecursor into angrec; EXIT WHEN NOT FOUND; select name into abtname from abteilung a where a.abtnr=angrec.abtnr; raise notice 'Angestellter: % Abteilung: %', angrec.PersNr, abtname; END LOOP; END; $$ TRIGGERS, UPDATABLE- MAT-VIEWS Triggers: Sicherstellen kompl. Kons.Bedingungen, Auditing, Immer Db.Objekt und Tabelle zugeordnet, Stored Procedures, keine Parameter, Auslösende Ereignise: INSERT, DELETE, UPDATE [ OF column]; Trigger Time: BEFORE, AFTER, INSEAD OF; Trigger Restriction; Typen: Row-Trigger(FOR EACH ROW): pro betroffenes Tupel, Statement-Trigger: pro SQL-Anweisung; Implizite Params(Row Tigger): OLD (UPDATE u DELETE)oder .old, NEW oder .new(INSERT u UPDATE); Haben Rechte des Owners, Owner benötigt CREATE TRIGGER Beispiele: CREATE OR REPLACE FUNCTION CheckInsertAng() RETURNS TRIGGER AS $CheckInsertAng$ BEGIN IF (TO_CHAR (sysdate,'DY') IN ('SAT', 'SUN')) THEN RAISE EXCEPTION 'Nur während Arbeitszeit erlaubt'; END IF; END; $CheckInsertAng$ LANGUAGE plpgsql; CREATE TRIGGER CheckInsertAng BEFORE INSERT OR UPDATE OR DELETE ON Angestellter EXECUTE PROCEDURE CheckInsertAng(); CREATE OR REPLACE FUNCTION cascade_updates() RETURNS TRIGGER AS $cascade_updates$ BEGIN IF OLD.AbtNr != NEW.AbtNr THEN UPDATE Angestellter A SET A.AbtNr = NEW.AbtNr WHERE A.AbtNr = OLD.AbtNr; END IF; END; $cascade_updates$ LANGUAGE plpgsql; CREATE TRIGGER cascade_updates AFTER UPDATE OF AbtNr ON Abteilung FOR EACH ROW EXECUTE PROCEDURE cascade_updates(); CREATE OR REPLACE FUNCTION check_SalaerAenderung() RETURNS TRIGGER AS $check_SalaerAenderung$ BEGIN RAISE EXCEPTION 'Salaer nicht um mehr als 10% ändern!'; END; $check_SalaerAenderung$ LANGUAGE plpgsql; CREATE TRIGGER check_SalaerAenderung BEFORE UPDATE OF Salaer ON Angestellter FOR EACH ROW WHEN ((new.Salaer < old.Salaer * 1.1)) EXECUTE PROCEDURE check_SalaerAenderung(); CREATE OR REPLACE FUNCTION increment_salaer(v_salaer Firma.SalaerSumme%TYPE) RETURNS void AS $increment_salaer$ BEGIN UPDATE Firma SET SalaerSumme = (NVL(SalaerSumme, 0) + v_salaer) ; END; $increment_salaer$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION berechne_SalaerSumme() RETURNS TRIGGER AS $berechne_SalaerSumme$ BEGIN IF DELETING THEN SELECT increment_salaer(-1 * old.salaer) ; ELSIF UPDATING THEN SELECT increment_salaer( new.Salaer old.Salaer ) ; END IF ; END ; $berechne_SalaerSumme$ LANGUAGE plpgsql; CREATE TRIGGER berechne_SalaerSumme AFTER INSERT OR UPDATE OF Salaer OR DELETE ON Angestellter FOR EACH ROW EXECUTE PROCEDURE berechne_SalaerSumme(); Ausführung von Triggers : 1) Alle BEFORE statement Triggers 2)Für jedes betroffene Tupel gilt : a)Führe alle BEFORE ROW b) Bearbeite TUPEL (Lock und UPDATE) c) Führe alle AFTER statement Trigger aus 3) Überprüfe allfällige derred integrity constraints 4) Führe alle AFTER aus LOGON/LOGOFF-Trigger: Bei Oracle CREATE, ALTER, DROP auf Trigger möglich INSTEAD-OF Trigger: Bei update auf views, anstelle normalen sql, leiten Modifikation von auf views zur darunterliegender Tabelle, Beispiel: CREATE OR REPLACE VIEW AbtLeiterInfo(AbtNae, ALPersNr, ALName) AS SELECT abt.name, ang.person, ang.name FROM Abteilung abt inner join abteilung aal on abt.abtnr = al.abtnr inner join angestellter ang on ang.persnr = al.abtchef; CREATE OR REPLACE FUNCTION AbtLeiterInfo_Insert() RETURNS TRIGGER AS $AbtLeiterInfo_Insert$ DECLARE rowcnt integer; DECLARE abtnr integer := 1001; BEGIN SELECT COUNT(*) INTO rowcnt FROM Abteilung Abt WHERE Abt.name = NEW.AbtName; IF rowcnt = 0 THEN select abtNr_SEQ.nextval INTO abtnr from dual; INSERT INTO Abteilung (abtnr, name) VALUES (abtnr, NEW.AbtName); ELSE SELECT Abt.abtnr INTO abtnr FROM abteilung Abt WHERE Abt.name = NEW.AbtName; END IF; SELECT COUNT(*) INTO rowcnt FROM Angestellter WHERE persnr = NEW.ALpersnr; IF rowcnt = 0 THEN INSERT INTO Angestellter (persnr, name, abtnr, salaer) VALUES (NEW.ALpersnr, NEW.ALName, abtnr, 10000); ELSE UPDATE Angestellter SET Name = NEW.ALName WHERE persnr = NEW.alpersnr; END IF; END; $AbtLeiterInfo_Insert$ LANGUAGE plpgsql; CREATE TRIGGER AbtLeiterInfo_Insert INSTEAD OF INSERT ON AbtLeiterInfo FOR EACH ROW EXECUTE PROCEDURE AbtLeiterInfo_Insert(); Updatable Views: Jede Kolonne view auf Kolonne einer einzigen Tabelle mappen, View darf nicht(SET,DISTINCT,aggregation, GROUP, ORDER, MODEL, Collection Expression, SUBQUERY, JOINS), Wenn Join: Das DML-Statement Nur eine der darunterliegenden Tabellen betreffen, Keine Indexe auf Views, aber sie greifen auf Indexe der Tabelle zu In Postgres: Für Triggers zu erst procedure schreiben, CREATE OR REPLACE FUNCTION update_abt_statistik() RETURNS TRIGGER AS $$ BEGIN TRUNCATE TABLE AbtStatistik; INSERT INTO AbtStatistik SELECT a.AbtNr, a.Name, sum(b.Salaer), count(b.persnr) FROM Abteilung a, Angestellter b WHERE a.AbtNr = b.AbtNr GROUP BY a.AbtNr, a.Name; RETURN NULL; END; $$ language plpgsql; dann CREATE TRIGGER log_update AFTER UPDATE ON accounts FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.* ) EXECUTE PROCEDURE log_account_update(); Materialized View: Definition: Zwischengespeicherte View; In Oracle können Views gespeichert werden. In PostgreSQL Mög. Probleme: Updates in Mat View nicht übernommen; Lösung: Setzten des Zeitpunktes der Synchonisation -Snapshot(nur am anfang geupdated) -eager(update wenn Änderung an Tabelle(sofort)) -lazy(update wenn Transaction commited) -...plus(user defined oder best. intervall update) INDEXSTRUKTUREN Datei Manager lokalisiert Seite desgesuchten Satzes und beauftragt Puffer-Manager, sie in den Hauptspeicher zut un, dieser meldet wenn fertig Heap: Collection von Data Pages(DP) : Physikalische Speicherung Date, Rows unsortiert, allgemein 8kB zu 64kB Extends, PageSplit bei Überläufen, wenn kein Platz -> Row chaining. lists of unordered records, retrieval inefficient as searching has to be linear Table Scan: Scanning aller Pages einer Tabelle: Starte bei erster, durch alle durch, extrahiere alle Rows die zur Query passen ->IOOptimierung durch Lesen ganzer Extends, ab Query mit 80% Ausbeute schneller als über Index Index: Datenstruktur für eff. Zugriff, Overhead bei Daten Ops: (Durchsuchen(Scan), (Equality Search), (Range Search), Insert(Lesen und schreiben mehrerer Seiten erforderlich, Löschung wie Insert)) Arten: B(+)-Bäume, Hash, Bitmap, Mehrdimensionale und mehrstufe Indexe B-Bäume: Geeignet für Sekundärspeicher, Range- und EqualSearch; Varianten(B+: Daten separat nur in Blättern analog ISAM, RBaum:mehrdimensional) Idee: Knotengrösse=Seitengrösse, Verzweigungsart hängt ab, wie viele Eintröge auf einer Seite Platz haben, durch ausbalancieren immer logarithmisch; B-Baum der Ordnung(m, l; m>3m l+1): (Vielwegsuchbaum von Grad k=m/2, Knoten entspricht Seite, Knoten hat Suchschlüssel und Söhnezeiger, Wurzel ist Blatt oder hat mindestens 2 Söhne, Jeder innere Knoten ausser Wurzel hat mindestens m/2 und höchstens m sortierte Söhne -> Mindestbelastung 50%, alle Blätter gleiche Höhe, h höchstens = 1 + (logm/2(N/2)) und mindestens = logm(N) Einfügen: Rekursive Suche nach Schlüssel, gibt Stelle, Schlüssel einfügen, Ist Knoten überfüllt teile: Lege neuen Knoten an und belege ihn mit Schlüsseln, die rechts vom mittleren Eintrag des überfüllten Knoten, Füge mittleren Eintrag von überfüllt in Vater, Verbinde Verweis rechts des neuen Eintrag im Vater mit neuen Knoten; Falls Vater überfüllt: Wenn Wurzel, neue Wurzel, sonst wiederholen Löschen: Wenn Anzahl der Schlüssel in einem Knoten null, zwei benachbarte Knoten zusammen, Liegt Schlüssel auf Blatt: entfernen. Liegt Auf Knoten und m/2 wird unterschritten -> Unterlaut: Es muss ein Ersatz-Knoten vom Bruder vie Vater geholt werden -> Falls Bruderknoten jetzt unterlaufen: Verschmelzen, Lösche leeren Knoten, Wenn Wurzel, Bestimme neue Wurzel, Falls Vaterknoten unterlaufen: Wiederholung B+ Baum: Datenelement nur in Blättern, Innere Knoten nur Zeiger, Zugriff immer zum Blattknoten, Blattknoten verkettet; Vorteile: Höhere Fanout, da die Datenmenge in Knoten kleiner, Kleinere Tiefe, weniger IO’s. Vorherrschend in DBS B+-Index: Diskbasierter B-Baum über einem Attribut, jeder Knoten enthält keine Non-Key Daten, Blatt-Knoten enthalten DiskReferenz auf das gespeicherte Tupel Clustered B+ Index: Wie B+-Index, aber Blatt-Knoten enthalten die Tuples, dh Tupels sind sortiert nach dem Attribut des Index Clustered Index: Blätter enthalten Data-Records in sortierter Reihenfolge, Oft für Primärschlüssel, Schneller Zugriff via Primärschlüssel, Nützlich für Kolonnen, die häufig in geordneter Reihenfolge oder range searches; Jeder Table nur einen Clustered Index, Da Rows physisch so gespeichert, etwa 5% Tablegrösse, vor allen anderen Indexes anlegen Unclustered Index: Blätter enthalten Referenzen auf Records-> Wie Index in Büchern, Meist als B-Bäume, jede Page in einem Index hat Page-Header, gefolgt von Indexrows. Jede Indexrow hat Schlüssel und Pointer zu anderen Page oder Data.row, Jede Page heisst Indexknoten, Jede Page in Blättern oder Zwischenstufen hat einen Pointer zu den nächsten und vorherigen Knoten in doppelt gelinkter Liste Hash: Hash-Funktion h: Ordnet Keys zu Records zu, 0..n-1 konkrete Schlüssel zu Blöcken, NULL-Werte nicht zugelassen; Problem: Overflows, Lösung: OverflowChains; Eigenschaften: EqualSearch, für optimierte Memory-Strukturen; Sehr gut für Punktqueries, wenn keine Overflowchains, Gut für MultipunktQueries; Nicht gut für range, prefix oder External Queries-> Reorganisation bei Overflowchaining Bitmap: Ordnet Attributswerte als Bitmuster; Eigenschaften: Geeignet für Wertebereiche mit geringer Kardinalität, Schnelle Abfrage vor allen AND/OR, Langsam bei Modifikationen; Einsatz in Datawarehouses Indexe wählen, Erstellen: Kriterien(Welche Indexe hat DBS, Tabellengrösse, Oft Daten löschen/einfügen, Welche Attribute für Queries, Wertebereiche); Index-Kandidaten sind Attribute(Die Schlüssel sind, An Joins teilnehmen, Sortierungen, Group by, Gleichheits-oder Bereichs) Indexe erstellen: Meist CREATE INDEX <indexName> ON <Table(attr)>; DROP INDEX <IndexName>; Unique Index : Automatisch bei PK-Attribut oder Unique-Constraint, Manuell sonst. Non-Unique: Manuell erstellen für zb FK-Attr, Range oder Point-Queries; Indexe über mehrere Attribute: evt Reihenfolge wichtig; Clustered-Index mit „included columns“->Wichtig fürindex-covering-queries Kosten Modell: Ziel(Abschätzen Anzahl IO-Ops für Anfrage, Zeit für in-memory Berechnungen = 0) Definitionen: (P= nr DataPages mit Füllfakter=67%, R=nr Records per Page, Für Tree Indexes: F=Fanout=Durchschn. nr Kinder in NonBlatt-Knoten, PI=nr Pages im Leaf-Level Index Heap: Heap Scan(Table Scan) : Anzahl I/O = P; Search nach eindeutigen Attr: nr I/O = ½ * P Abschätzung Nr Zugriffe Clustered Index: 20000 Blöcke zu 20 Records, Index mit 200 Einträgen F=200: Scan: I/O=P; Eindeutige Suche mit Gleichheitsbedingung: Annahme: Lokalisierung erster Page mit Gleichheitsbedingung in logF(P), Binary Search in Page liefert gesuchten Record: I/O = logF(P) F = nr Einträge Index Suche mit RangeSelektoren: Annahme: Lokalisierung des ersten Records wie Gleichheit, Sequentielles Lesen der n sortierten Records -> n / R Pages => I/O = logF(P) + Ceiling(n/R) Abschätzung Nr Zugriffe Unclustered Index: 20000 Blöcke a 20 Records Index mit 200 Einträgen Indexscan für sortierten Zugriff: Annahme: Grösse des IndexEintrags = 1/F Grösse der Records, NR Blätter = P * R /F, Anzahl DataEntries pro Index-Page = F, Anzahl DataEntries pro HeapPage =R, Anzahl DataEntries total = P*R, Verfahren: Scan Blätterlevel des Indexes für jedes Data-Entry lies den Record vom Heap Kosten: I/O = P*R /F + P*R Bemerkung: Evt schneller tablescan & sort Suche mit Gleichheitsbedingung: Annahme: Lokalisierung erster Page via Index, Lokalisierung des Records mit in memory binarysearch der geladenen Page, Lesen des Records mit einem Heap-Zugriff; I/O-Kosten: 1 + logF(P*R/F) Suche mit RangeBedingung: Annahme: Lokalisierung erster Records wie in Gleichheits, Sequentielles Lesen des Indexes und Laden des Records solange Suchbedingung erfüllt, N = Anzahl Tuples, weclche erfüllen, Anzahl I/O = logF(P/F) + n + n/F -> Wenn Suche mit mehr als 10%Ausbeute -> Table scan mit Sort besser Anzahl I/Os Scan Equality Search Range Abkürzung Search Heap P 0.5*P P P=Anzahl Pages PI=Anzahl Pages im Leaf-Level des Index Clustered PI=P logF(PI) logF(PI) + F=Index Fan Out=Anzahl Records per n/R IndexPage R=Anzahl Record per Page unclustered PI*R logF(PI) logF(PI) + n n=Anzahl Tupels, die Suchbedingung erfüllen Query Processing Query Engine Parst SQL und wandelt in Query Tree um-> Query Tree evt. Transformiert in äquivalenten Query Tree, Optimizer generiert Ausführungspläne und wählt den optimalsten basierend auf Statistik über Datenverteilung, Zur Laufzeit werden kompilierte Ausführungspläne mit den aktuellen Variablen gebunden und ausgeführt Queries auf Tabelle bei Oracle (Oracle bei Ausführung eine folgender Operationen: Table Scan, Index Range Scan, Index Unique Scan, Table Access bei Rowid, Sort(in memory odo filebased), Hash(in-memory oder file-based) Filterm View) Arten Queries: PointQueries, MultipointQuery, RangeQuery, PrefixMatchQuery, ExtremalQuery, OrderingQuery, GroupingQuery, JoinQuery Joins Klassischer Hash-Join: Mache kleinere in Memory-Hash-Table für kleinere Relationen :> Für jeden Record in einer grossen Relation, durchsuche den hash-table->Geht gut wenn kleinere Relation in memory passt->Falls nicht passt, in kleinere Buckets unterteilen Hash-Join mit Partitionierung: Partitioniere beide Relationen mit hash funktion h -> R Tupel in Partition i matchen nur S Tupel in Partition i-> Lese eine Partition von R ein, hashe mit h2->Scan matching Partition von S, Suche nach Matches Nested Loop Join: Optimierung durch Indexes Block Nested-Loop-Join: Optimierte Variante des Nested-Loop-Joins, Jeder Block der äusseren Relation wird mit jedem block der inneren Relation verglichen Sort Merge Join: Sortiere R und S auf der Join-Kolonne, dann scanne sie um einen merge zu machen und gebe die Resultattuple aus _> R wird einmal gescannt, jede S-gruppe wird einmal pro matchendes R-Tuple gescannt->wahrscheinlich im Tupel Kostenmodell: Joins für den Join R|X| R.A = R.B |S| Abkürzungen: N(R)=Anzahl Tupels R, N(S)=Anzahl Tuples S, P(R)=Anzahl Pages R, P(S)=Anzahl Pages S, S kleinere Relation, B = Anzahl Hauptspeicherblöcke Nested Loop (Block oriented): Jede Iteration liest B-2 Blöcke aus R und P(S) Blöcke von S -> Anzahl Iteration outer loop: P(R)/(B-2); Kosten= Anzahl I/Os -> P(R) + P(S)*P(R)/(B-2) Indexed Nested Loop (INL) I/O-Kosten: P(R) + c * P(R)*N(R), c: Kosten traversieren Index und Laden aller passenden Tupels s für ein Tupel aus r Hash Join I/O-Kosten: Partitionierung der beiden Relationen: 2*(P(R) + P(S)), Lesen Partitionen: P(R) + P(S); Total: 3*(P(R) +P(S)) Kann auf P(R) + P(S) reduziert werden, wenn gesammte erstellte Datenstruktur in Memory Platz hat, M >= P(S) Merge Join I/O-Kosten: ExternalSort(R) + ExternalSort(S) + R + S -> R*log(R) + S*log(S) + R + S Wahl JoinAlgo: Falls kein Index ->HashJoin. Falls Index, ist INL effizient wenn (Ein INL auf dem Index von S.B ist performanter als HK, falls #unterschiedlicher Werte von S.B ungefähr #Rows von S-> Häufig, da meistens FKJoins, Unabhängig von # falls es ein Covering Index, Falls S.B ein Clustered Index); Sonst kann HJ bessere Performance liefern Index in folgenden Situationen nützlich: Nested Loop: non equi join ein index auf join attr kann fulltablescan verhindern, Einfügen und löschen, FK constraint falls R.A Subset von S.B) Indexe auf kleinen Tabellen: (Bei <200 Tuples häufig nicht, Grösse hängt von Grösse Indexkeys verglichen mit Tupelgrösse, Alle Daten einer Tabelle sind in einer Page -> Table Scan schneller, Jedes Tuple belegt eine Page -> Index verbessert Performance Anfragetuning/Optimierung Typische Client-Server(Netzransfer grosser Daten, Ketten von FragenAntworten, PingPong) -> Lösungen(Eine komplexe Query schneller als viele kleine, Abfragen als Views, Stored Procedures) Phasen: Übersetzung(Transformation, finde geeignete interne Darstellung für Anfrage), logische Optimierung(Umformung des Anfrageterms aufgrund Heuristiken), Physische Optimierung(Erzeugung eines oder mehreren Ausführungsplänen, in denen die a bstrakte Algebraoperationen durch konkrete Algorithmen ersetzt) Auswahl günstigsten Plans: Basierend auf statistischen Informationen aus dem Katalog Berechnung von Kostenvoranschlägen für jeden möglichen Plan Optimierung/Heuristiken, Teilziele: (Selektion so früh wie möglich, Verkleinerung Zwischenergebnisse, Basisoperationen als ein Schritt ohne Zwischenspeicherung Zwischenresultate, Zusammenfassung gleicher Teilausdrücke-> Wiederverwendung Zwischenergebnisse) Logische, algebraische Optimierung: Basiert auf Termersetzung aufgrund Äquivalenzen, Auch regelbasierte Optimierung genannt Physische Optimierung (Einbezug von Indexes, Verbesserung Analyse mit Statistiken, Einsatz von Heuristiken und Kosten, Kostenbasierte Optimierung(Generiere alle möglichen Pläne, bewerte Kosten, behalte billigsten), Analyse, Abschätzen Kosten(Für jeden Plan: Kommunikationskosten(#Nachrichten, Menge übertragender Daten), Berechnungskosten(CPU, Pfadlängen), IOKosten(Seitenzugriffe), Speicherungskosten(Temp Speicherbelegung im DBPuffer und Hintergrundspeichern)) Analyse Statistiken (Infos über Relationen und Indexe, Typischerweise im SysKatalog, periodisch aktualisiert, SysKatalog (data Dictionary) enthält normalerweise(Kardinalität, Verteilungsinfos über attrs, Grösse: #Pages für jede Reli, Index Grösse: #Pages für jeden Index, IndexHöhe: #Nicht-Blatt-Stufen für jeden Index, Sonstige Angaben zu Indizes) Selektivität Sind vers. Strategien anwendbar braucht es Kostenfunktion; Selektivität eines Suchprädikats schätzt # der qualifizierten Tupel relativ zur Gesamt# der Tupel in Reli; Hohe Selektivität: tiefer Prozwert, QueryOptimizer bestimmt für optimalsten Planes die Selektivität für CRUD; Erstelle Indexes auf Kolonnen(Die Häufig in Where oder Join, Deren Daten hohe Selektivtät aufweisen) Abschätzen in Prozent (Lineare Interpolation): where a < 100 => (100/maxValueA) | where a > 100 => ((maxValueA100)/maxValueA) | where a between 210 and 300 => (300-210)/maxValueA für effektive Anzahl *AnzahlTupels rechnen Dichte Durchschnittliche prozentueller Anteil von Duplikaten(Falls tiefe Selektivität tief, Dichte hoch, Unique Index tiefe Dichte), Dichterverteilung: (Uniform: Anzahl DistinctValues /#Tupels) Selektivität und Optimizer QueryOptimizer kann mit Histogramme die Selektivität bestimmen(tiefe Selektivität -> nimm Index; Hohe Selektivität -> Table Scan, DBS bestimmt Histogramme basierend Stichprobe). Faustregel: Selektivität <= 0.1 DBMS verwendet Index, Selektivität >0.1 Dbms macht Table Scan Tipps zur Anfrageoptimierung Join(Klausel-Syntax, Ans Ende der Anfrage), Attributswertebereiche(Numerische Vergleiche schneller als Text, Schnellere Vergleiche am Anfang), Projektion(SELECT *) durch Angabe von Attributen ersetzen, SARG-able Queries verwenden, Wenn möglich verwenden(DISTINCT, GROUPBY, HAVING, Unterabfrage durch JOIN, UNION durch WHERE ersetzen) SARG-able Queries Jede Query wird zuerst analysiert um Suchargumente zu finden -> Nur Suchargumente können Indexe benutzen, Ein Suchargument ist entweder ein exakt Match oder eine Range, Suchargumente können Listen sein, die mit AND verknüpft sind, Eine Seite des Vergleiches ist Konstante oder auflösbare Variable, Eine Seite des Vergleichs ist Kolonnenname Best Practices Indexing Bevor Indexes erstellt werden, ist gutes Verständnis wichtig(logisches physisches design, datencharakteristik, wie werden daten verwenden), Wie Benutzer Daten zugreifen(Welche Queries werden ausgeführt, Wie oft), Nur Indexes erstellen, die verwendet werden(Nur eingesetzt, wenn sie 90% der Tupel eliminieren, Attribute der Indexes kurz halten, keine langen Strings, Clustered Indexes gut für Ranges und Sorting, NonClustered gut für pointQueries aber auch range oder order, Covering Index: enthält alle Attribute für die Abfrage sehr schnell -> Abwägen zwischen Geschwindigkeit und Indexgrösse), Kandidaten für Indexes(PK und FK, Attribut auf denen häufig range, Attribute die häufig in sortierter Reihenfolge, Attribute die häufig in Aggregationen gruppiert), Attribute ohne Index(Selten in Queries vorkommen, Die wenig unterschiedliche Werte aufweisen, Alle BLOB-Typen) DATENSTRUKTUREN:ARRAYS, DICTIONARIES, GRAPHS Defi: Gruppierung von variablen Nummer von Datenitems; Implementierung von Collections(Listen, Sets, Bags, Trees oder Graphen, enum kann Liste oder Set); Semantik:Linear Collection(Listen/Arrays, PrioQueues, Heaps), AssoCollections(Sets, Multisets, AssoArrays), Graphen und Trees Arrays: Collection mit fixer oder variabler Länge, keine Sets, jene modelliert man als seperate Tabelle, Postgresql: Basistyp, variable Länge; Index started bei 1, Syntax: CREATE TABLE sal_emp(name text, pay_quarterly integer[], schedule text[][]); INSERT INTO sal_emp VALUES(‚Bill’, ARRAY[100, 1000], ARRAY[[‘hello’, ‘bye’], [‘lul’, ‘lal’]]) ; Direkt auswählen : SELECT ARRAY[1, 2, 3+5]->{1, 2, 8} ; Subslices : SELECT schedule[1 :2][1 :1] ( [von:bis] ) FROM sal_emp WHERE name =’Bill’ ; -> {{‘hello’}, {‘lui’}} ; SELECT * FROM sal_emp WHERE 100 = ANY(pay_quarterly)//Wert irgendwo im array ; Operationen auf Arrays : Ist x IN : <@ (SELECT ARRAY[2, 7] <@ ARRAY[1, 7, 2]; -> true);; Gleichheit: = (SELECT ARRAY[1.1, 2.1, 3.1]::int[] = ARRAY[1, 2, 3]; -> true);; Haben Elemente gemeinsam/überlappen:&& (SELECT ARRAY[1, 4, 3] && [2, 1]; -> true; Concatinate/Verbinden: || SELECT ARRAY[1,2] || ARRAY[3,4]-> {1,2,3,4,}); Array functions: array_ndims(arr)=>nr of dimensions of array array_dims(arr)=>dimensionen des arrays als String), array_append(arr, el), array_cat(arr1, arr2), array_lower(arr, int)-> untere Bound des Levels, array_upper(arr, int)->Obere Bound, array_length(arr, intDim)-> Gibt länge als int, Select array_append((select b from aa where a = 1), 5); Select array_prepend(5, (select b from aa where a = 2));, select array_to_string((select b from aa where a = 1), ';' == delimiter); select unnest((select b from aa where a = 1)); -> flattet den array, jedes element auf eigene zeile Modellierung von Graphen mit PunktTable und Recursive Queries CREATE TABLE graph(id serial, node1 int, node2 int); WITH RECURSIVE graph_cte(node1, node2) AS (SELECT node1, node2 from graph UNION ALL SELECT node2 AS "node1", node1 AS "node2" FROM graph), paths(node1, node2, path) AS(SELECT node1, node2, ARRAY[node1] AS "path" FROM graph_cte b1 WHERE b1.node1 = 2 --Start Node UNION ALL SELECT b2.node1, b2.node2, p.path || b2.node1 FROM graph_cte b2 JOIN paths p ON (p.node2 = b2.node1 AND b2.node1 <> ALL (p.path[2:array_upper(p.path, 1)]) )) SELECT path || node2 AS "path" FROM paths WHERE node2 = 6 --Ende node ORDER BY array_length(path, 1), path; Alle Untergebenen des Angestellten 1010, auch die Untergebenen der Untergebenen with recursive leafs(idchef) as (values(1010) union all select persnr from angestellter a, leafs l where a.chef = l.idchef) select persnr from angestellter a, leafs l where a.persnr = l.idchef; Dictionaries oder AssoArrays: Paare von Schlüssel-Werte Paare: Implementiert beides als String, Keys eindeutig sein Modellierung: CREATE TABLE kvptable (id serial PRIMARY KEY, eventid int NOT NULL, key text NOT NULL, value text); INSERT INTO kvptable (eventid,key,value) VALUES (1,'Medication Name','Ibuprofen'), (2,'Medication Name', 'Curcumin'), (1,'Route','Oral: Capsule'), (2,'Route','Intravenous: Liquid'), (1,'Dosage','50mg'), (2,'Dosage','100mg'); BeispielQueries Dictionaries modelliert : SELECT eventid, key, value FROM kvptable WHERE key = 'Medication Name' AND value = 'Ibuprofen' Find a list of eventid's for which our medication matches: 1. Variante : SELECT eventid FROM kvptable WHERE key = 'Route' AND value = 'Oral: Capsule' AND eventid IN ( SELECT eventid FROM kvptable WHERE key = 'Dosage' AND value = '50mg' AND eventid IN (SELECT eventid FROM kvptable WHERE key = 'Medication Name' AND value = 'Ibuprofen' )); 2. Variante : SELECT k1.eventid FROM kvptable k1 INNER JOIN kvptable k2 ON k1.eventid = k2.eventid INNER JOIN kvptable k3 ON k2.eventid = k2.eventid WHERE k1.key = 'Route' AND k1.value = 'Oral: Capsule' AND k2.key = 'Dosage' AND k2.value = '50mg' AND k3.key = 'Medication Name' AND k3.value = 'Ibuprofen'; Implementation mit hstore: CREATE EXTENSION hstore; CREATE TABLE hstoretable ( id serial PRIMARY KEY, eventid int NOT NULL, kvp hstore NOT NULL); INSERT INTO hstoretable (eventid,kvp) VALUES (1,hstore('Medication Name','Ibuprofen') || hstore('Route','Oral: Capsule') || hstore('Dosage','50mg')), (2,hstore('Medication Name','Curcumin') || hstore('Route','Intravenous: Liquid') || hstore('Dosage','100mg')); BeispielQueries Dictionaries mit hstore : SELECT eventid, kvp FROM hstoretable WHERE kvp @> hstore('Medication Name','Ibuprofen'); SELECT eventid, kvp FROM hstoretable WHERE kvp @> hstore('Medication Name','Ibuprofen') AND kvp @> hstore('Route','Oral: Capsule') AND kvp @> hstore('Dosage','50mg'); Implementation in postgres mit hstore(): CREATE TABLE test (col1 integer, col2 text, col3 text); INSERT INTO test VALUES (123, 'foo', 'bar'); SELECT hstore(t) FROM test AS t; List all keys : SELECT akeys(mykvpfield) FROM.. –Get all key-value-pairs: SELECT each(mykvpfield) FROM… --Get Key Value SELECT mykvpfield ->’name’ FROM… --Test if key exists : ..WHERE mykvpfield ? ‘<value>’ Index für GIST/GIN : CREATE INDEX hidx ON testhstore USING GIST(h) ; select akeys(tags) from planet_osm_point Order By tags LIMIT 5; select distinct akeys(tags) as keys from planet_osm_point Order By keys limit 10 ; select distinct each(tags) as keys from planet_osm_point Order By keys limit 10 ; select tags->'name' from planet_osm_point where osm_id = 11210062 or osm_id=50075375; select * from planet_osm_point where tags?'parking'; SELECT hstore_to_array(tags) as "linear array" FROM planet_osm_point where osm_id in (11210062,50075375); Datenstrukturen: Trees, Semistrukturierte Daten(XML, JSON) Baumstrukturen: Typische Anfragen(Alle Kinderknoten, Vor-Nachgänger) Trees: Azyklischer verbundener Graph, falls verbunden und hierarchisch ist es ein rooteter Tree, geordneter Tree: Ordnung Kinder strikt definiert Baum-Implementation RDBMS: Generische, relationale Implementation: Adjazenzliste(Columns : ID, ParentID ; einfach; Schelle Knoten moves, inserts und deletes; aufwendig zum Finden von: Level, ancestry & descendence paths; Nutze CTE um zu travers) Nested-Set-Modell(Art von Modified Preorder Tree Traversal; Columns:Left, Right; Einfach: Level, ancestry, descendants; movesm insers und deletes aufwendiger als Adja, Verlangt Ordnung, grosser Aufwand Ummodelung, Preorder durchlaufen, erster Besuch lft nummerien, dann rgt) Materialisier Pfad:(Root kriegt Pfad 1, alle andere Knoten Pfad des Vaters + Nummer innerhalb seiner Brüder, Nachfolger einfach, ancestors schwierig aber effizient, Kinder: Ineffizient, Vater: Effizient) Tree-Typ ‚Ltree‘ (label tree) in Postgres: Hierarchisch strukturierte Beschriftungen(labels), Indexierbar mit btree und GIST, Suchfunktion wie Volltext, label:[A-Za-z0-9_], max 256 Bytes; label path: Sequenz von lables, separiert mit ‚.‘, max 65KB, Beispiele(L1.L2, Top.Countries.Europe), Limitiert bei Tiefe, Nachfolger einfach, Vorfahren tricky Installation: CREATE EXTENSION ltree; Operatoren: Ist linkes Arg. Nachfolger von rechts?: SELECT 'Welt.Europa.Schweis.SG'::ltree <@ 'Welt.Europa.Schweiz'::ltree; Kleinster gem. Vorgänger: SELECT lca('Welt.Europa.Schweiz'::ltree, 'Welt.Europa.Malta'::ltree); Beispiel: Suche alle Nachkommen des Knoten mit Id 2; als: normale Query, mit lpath, left_n und right_n als nested set with recursive tmp as ( select id, name from skills where id = 2 Union All select s.id, s.name from tmp Join skills s on s.parent_fk = tmp.id) Select * from tmp where id != 2; with p as (select path from skills where id = 2) Select u.id, u.name from skills u, p where u.path <@ p.path and u.id != 2; select u.id, u.name from skills u where u.mpath @@ '2' and u.id != 2; select u.id, u.name from skills u, (select * from skills where id = 2) as p where (u.left_n BETWEEN p.left_n AND p.right_n) AND u.left_n != p.left_n; XML-Funktionen: XML aus Tabelle: table_to_xml(‚tblname‘, True, 'False', ''); XML aus Query: select query_to_xml('select s.vorname, l.name from students s inner join laptops l on l.personid = s.matnr', True, 'False', ''); -- # Fügen Sie einen neuen XML Record ein. insert into studentsxml values('<blub>whatever</blub>'); -- # Führen Sie folgenden XPATH Abfragen durch: -- ## Ausgabe aller =[studenten]=, vollständige Records SELECT xpath('/students', c) FROM studentsxml; -- ## Ausgabe des ersten =[studenten]=, vollständiger Record SELECT xpath('/students/student[position()=1]', c) FROM studentsxml; -- ## Ausgabe der Matrikelnummer =[matnr]= des ersten =[studenten]= SELECT xpath('/students/student[position()=1]/matnr/text()', c) FROM studentsxml; -- ## Ausgabe des Laptop Record mit lid=222 SELECT xpath('//laptop[@lid=222]', c) FROM studentsxml; -- ## Ausgabe des =[vorname]= des Studenten mit zwei Laptops, ''Hinweis: verwenden sie das count() Prädikat'' SELECT xpath('//*[count(laptop)=2]/parent::student/vorname/text()', c) FROM studentsxml; JSON: CREATE TABLE rpg_items (c1 serial, data json); INSERT INTO rpg_items (data) VALUES ('{"name":"sword","buy":"500","sell":"200","description":"basic sword","attack":"10"}'); INSERT INTO rpg_items (data) VALUES ('{"name":"shield","buy":"200","sell":"80","description":"basic shield","defense":"7"}'); INSERT INTO rpg_items (data) VALUES ('{"name":"shield","buy":"200","sell":"80","description":"basic shield","defense":[7,8,9]}'); INSERT INTO rpg_items (data) VALUES ('{"name":"shield","buy":"200","sell":"80","description": {"basic shield":[1]},"defense":[7,8,9]}'); -- # Erstellen Sie eine oder mehrerere Tabellen mit Spalten von unterschiedlichen Typen inklusive Array-Typen CREATE TABLE rpg_items_defense (c1 serial, buy int, sell int, description text, defense int); CREATE TABLE rpg_items_attack(int serial, fields int[], description text); INSERT INTO rpg_items_defense (buy, sell,description, defense) VALUES (200, 80, 'basic shield', 7); INSERT INTO rpg_items_attack (fields, description) VALUES ('{500,200,10}','basic sword'); SELECT row_to_json(row(buy,sell,description,defense)) FROM rpg_items_defense; SELECT row_to_json(row(array_to_json(fields), description)) FROM rpg_items_attack; JSON Operationen: json->int (get JSON array element) ‘[1,2,3]’::json->2 json->text (get JSON object field) ‘{“a”:1, “b”:2}’::json->’b’ json->>int (get JSON array element as text) ‘[1,2,3]’::json->>2 json->>text (get JSON object field as text) ‘{“a”:1, “b”:2}’::json->>’b’ json#>array of text (get JDON object at path) ‘{“a”:[1,2,3], “b”:[4,5,6]}’::json#>’{a,2}’ json#>>array of text (get JDON object at path as text)‘{“a”:[1,2,3], “b”:[4,5,6]}’::json#>>’{a,2}’ OBJEKTRELATIONALE DATENBANKSYSTEME Schwächen RDBMS(Keine Koordinaten, keine Identität ausser Werte der Tupel, Keine Meth auf Datenstrukturen, keine benutzerdefinierten Datentypen, Keine Referenzen, Keine Vererbung von Tabellen, Mengen-Ansatz versus Objekt Objektrelationale Datenbanksysteme (ORDBMS): (Kompatibel mit best. DBs, evolutionärer Ansatz) Paradigma:( Funktionale Erweiterung um OO, strukturelle Objektorientierung, Standard SQL:99, Eigenschaften( Benutzerdefinierte ObjektTypen mit Vererbung und Methoden, Objekt-Tabellen und Vererbung, Packages, Objektidentität- und Referenzen, Collections, ObjectViews Postgres(Benutzerefinierte Objekttypen mit Vererbung, ohne Methoden, mit Operatoren; Objekt-Tabellen nur Tabellen, Multiple Inheritance; Packages mittels Schema, OIDS aber keine Referenzen, mit Collections, ohne Object Views) Oracle(Benutzerdefinierte ObjektTypen mit Vererbung und Methoden:Single Inheritance; Objekt-Tabellen und Vererbung, Packages, OIDS und Referenzen, beschränkte Collections, keine ObjectViews) Objekttypen: Abstract Data Type(ADT), User Defined Type(UDT), Definiert durch(Name des Typs, Attribute(Name, Typ), Methoden); Verwendet für Definition von Objekttabellen, Definition von Attributen in relationalen oder in Objekttabellen CREATE TYPE NameTyp AS (NStr varchar(20)); CREATE TYPE PersonenTyp AS (NName NameTyp, VName varchar(20)); Typen in relationalen Tabellen: Column Objects: Typen können auch für komplexe Attribute in relationalen Tabellen verwendet werden, werden dann als ColumnObject bezeichnet; Postgres(OID fest eingebaut); Oracle(Tuples der relationen Tabellen haben keine OIDS) Beispiele: CREATE TABLE Angestellter (PersNr int PK, PersInfo PersonenTyp); INSERT INTO Angestellter VALUES(100, PersonenTyp(‚Graf‘, ‚Michael‘)); SELECT persnr, (persinfo).nname from Angestellter; (Zugriff auf objektwertige Attribute mit ‚Punkt‘-Operator) SELECT * FROM Person WHERE persinfo = PersonenTyp(‚Graf‘, ‚Michael‘)); Order By und VergleichsOperatoren auf benutzerdefinierten Typen nicht definiert! Tabellenvererbung PostgreSQL: Mehrfachvererbung mit INHERITS, Constraints werden nicht geerbt -> z.B. Primary Keys! CREATE TABLE a (id int PRIMARY); CREATE TABLE a1 (remarks text) INHERITS(a); CREATE TABLE a2 (fkey int references a) INHERITS(a); CREATE TABLE b (remarks text) INHERITS(a1,a2); SELECT * from a; // Selektiert auch Tupel von a1 und a2 und b! -> Liskovsches Substitutionsprinzip: ein Programm, das Objekte einer Basisklasse T verwendet, funktioniert auch mit Objekten der davon abgeleiteten Klasse S, ohne dabei das Programm zu verändern, Primary Key Constraint wirkt nur auf Tabelle A, durch einfügen in a1,a2 und b können ids mehrfach vorkommen obschon id PK ist. "TRUNCATE a CASCADE;" alle Tabellen leeren, "DROP TABLE a CASCADE;" alle Tabellen löschen (wegen der Vererbung). Objekttabellen: Sind Instanzen von Objekttypen, Bei Deklaration können Defaults und Constraints angegeben werden; Jedes Tupel(RowObject) hat OID Object Identifiers (OIDs) Jedes Row-Object wird mit einem systemgenerierten 16 byte-OID systemweit eindeutig identifiziert Aggregations-Datenmodelle: Umfassen: Arrays, Dictionaries, geschachtelte Strukturen, Collection von related objects die wir als Unit behandeln wollen => Unit(für Daten-Manipulationi, Konsistent, Speicherung) BASE: Basically Availability -> immer erreichbar, Soft State -> Konsistenz ist kein fester Zustand, Eventual Consistency ->Daten sind MANCHMAL Konsistent Konsistenz: Kann erhöht werden durch mehr Knoten -> zum Preis einer erhöhten Latenz CAP Theorem (Kompromiss zwischen 2 Eigenschaften muss getroffen werden): Consistency -> Daten sind gleich, bei jeder Replikation auf jedem Server, Availability -> Daten müssen immer verfügbar sein Partition Tolerance -> DB läuft trotz Netzwerk- und Maschinen Ausfälle NoSQLKategorien(Key/Value, Dokument, Column-Familiy-Stores, Graph, OODBMS, XML) Key/Value-Datenbanken -> Aggregations Orientiert, haben Metadaten, Value ist opak für DB, Dokumenten-Datenbanken -> Auch Aggregations Orientiert, Value ist strukturiertes Dokument: JSON oder BSON z.B MongoDB Dokument-Begriffe: DB/Schema -> Instanz/DB, Table -> Collection, Row -> Document, rowid -> _id, Join -> DBRef Column-Family Stores: Jeder Key verknüpft mit mehreren Attributen (Columns -> Google BigTable Graph- und schemalose DBS: Beziehungen sind wichtig -> Nodes (Person) und Beziehungen (mag, besitzt, hat Adresse, …) DBS-> nichtuniforme Daten oder implizites Schema Verteilte Datenbank Modelle Arten des Verteilens(Sharding(horizontales Partitionieren/skalieren), Replikation), Replikation(Master-Slave, Master-Master) Konsistenz kann in verteilten DBs durch mehr Knoten erhöht werden, zum Preis höhere Latenz; Durability zum Teil zu Gunsten Performance opfern; Quorem: Konsistenz oder Durability opfern durch Mindestanzahl Map Reduce: Materialisierte Views sind gespeichertes MapReduce-inkrementell aktualisiert, Parallele (Aggregierungs)-Anfragen einfacher und scheller, können nichts mehr wie UDFs, Schreiben meist auf Disk MongoDB:(BSON, MasterSlave, Sharding, C++, Query:(<db>.<collection>.find({„<JSONobject>“:<value>}) CREATE: DB: use <DBname>, Collecton: <DBname>.createCollection(<name>, options); kann nun mit db angesprochen werden db.unicorns.insert(„{name“:‘Aurora‘,“ gender“:‘f‘,“ weight“:450); READ: db.<collection.find({"<JSONobject":<value>}) d = db.unicorns.find: d({name:'Aurora'}); d({gender:'f',loves:'apple'});d( {gender: 'f',loves:{ $all:['apple' ,'carrot']}}); d({gender:'f',$and:[{loves:'apple'},{loves:'carrot'}]}); d({gender:'f',loves:{ $in:['apple','carrot']}}) d({gender:'f',$or:[{loves:'apple'}, {loves:'carrot'}]}); d({gender:'f',vampires: {$exists:0}}); d({gender:'m', $and: [{weight:{$gte:600}},{weight:{$lte:900}}]})—zwischen; Operatioren:($ne Not equal to;$lt Less than;$lte Less than or equal to;$gt Greater than;$gte Greater than or equal to;$exists Check for the existence of a field;$all Match all elements in an array;$in Match any elements in an array;$nin Does not match any elements in an array; $elemMatch Match all fields in an array of nested documents;$or or;$nor Not or;$size Match array of given size; $mod Modulus;$type Match if field is a given datatype;$not Negate the given operator check; d({gender:'f',$or:[{loves:'apple'}, {loves:'carrot'}]},{_id:0})—Unterdrückt _id, sonst 1 UPDATE: mit $set, sonst ganzer Record ersetzt d.update({name: 'Roooooodles'}, {$set: {weight: 590}}) d.update({name: 'Aurora'}, {$push: {loves: 'sugar'}}) DELETE: db.unicorns.remove({name: 'Aurora'}) JOINS: d.findOne({_id: db.employees.findOne({name:'Moneo'}).manager}) RECOVERY Konsistenzbedingungen: Regeln, denen die Daten genügen müssen, Transaktion: konsistenzerhaltende Operation. Fehlerklassen Lokale Fehler einer Transaktion: Ursachen: Fehler in der Applikations-Software, Abbruch einer Transaktion - Auftreten häufig Inkonsistenz: Festgeschriebene Änderungen der abgebrochenen Transaktionen Recovery: Rollback oder 'lokales undo', Zeit für Recovery kurz (innert Millisekunden) Fehler mit Hauptspeicherverlust: Ursachen: Fehler in der DB-System-Software, Hardwarefehler, Stromausfall - Auftreten relativ selten Inkonsistenz: Verlust von Änderungen, die noch nicht auf die Disk zurückgeschrieben wurden Recovery: Recovery via Log Files REDO (abgeschlossene Trx wiederholen), UNDO (aktive/offene Trx zurücksetzen), Zeit für Recovery mittel (innert Minuten) Fehler mit Hintergrundspeicherverlust (Medien- oder Externspeicher) Ursachen: Hardwarefehler im Disk-Treiber, Headcrash, Naturgewalten, Hacker/Viren - Auftreten sehr selten (verheerende Wirkung) Inkonsistenz: Auf der Disk gespeicherte Daten unbrauchbar Recovery: Recovery via Full Backup + Logs, Logs sollten sich auf separatem Medium befinden, Zeit für Recovery sehr lang (mehrere Stunden) LogFile-Techniken WAL-Prinzip: Write-Ahead Logging, vor commit alle Log-Einträge schreiben (für REDO im Fehlerfall), vor write (Disk) alle Log-Einträge schreiben (für UNDO im Fehlerfall) Checkpoints: Log-File bei vielen Trx. sehr gross, nur Analyse ab letztem Checkpoint nötig, Häufige Checkpoints:Schlechtere Perf. & kürzere Recovery-Dauer, REDO Trx. (Winner), UNDO Trx. (Loser) Backup: Backup-Arten Logischer Backup: + Flexibel, - Langsam (bei PG single threaded), * Export/SQL, * z.B. pg_dump, pg_restore Physischer Backup: Online: + Konsistent, - Bei 24/7 Betrieb inakzeptabel, * Keine Trx./Writes während Backup, * z.B. DB-Files kopieren, Offline: - Inkonsistent, - Recovery aufwändiger, + Trx. während Backup erlaubt, * z.B. pg_basebackup Inkrementelles Backup: volle Sicherung zeitaufwendig -> Sichern der veränderten Datenbankseiten (= inkrementelles Backup) Wiederherstellung: letzte Full Backup und alle danach erstellten inkrementellen Backups eingespielen anschliessend nur LogDateien nach letztem inkrementellen Backup Point-In-Time-Recovery (PITR): Wiederherstellung der DB auf einen definierten Zeitpunkt Backup Planung Was? (welche Daten) - Wie lange zurück? – Wie? (Methode, Medium) – Maximale Ausfallzeit? – Recovery Tests! PostrgeSQL Logisches Backup: Pg_dump und pg_restore (psql), custorm format, immer ganze db wenn möglich, kann komprimieren, ev.mit split aufteilen, Physischer Backup: Offline Backup( Dateien kopieren, geht nur für identische versionen, inkrementell schwierig), Online Backup, PITR(Base Backup mit pg_basebackup, WAL Log Archiving => PITR, Replikation, PITR kombinieren physisches Backup und WAL, braucht weniger Speicherplatz als FullBackup, Warm Standby) VERTEILTE DATENBANKSYSTEME(VDBMS) Definition: kooperierenden DBMS, verteilt auf verschiedenen Computern (Knoten, Site) eines Netzwerks, jeder Knoten hat eine autonome Verarbeitungsfunktionalität: kann lokale Applikationen ausführen, jeder Knoten partizipiert an globalen Applikationen, welche Datenzugriff auf die verschiedenen Knoten benötigen Anforderungen: Datenverteilungs- Transparenz(Benutzer sollen Queries formulieren können ohne auf die konkrete Verteilung der Daten zu achten), Atomarität von verteilten Transaktionen(Benutzer sollen Transaktionen schreiben, die auf Daten in den einzelnen Knoten zugreifen ohne sich um die Verteilung zu kümmern. Alle Änderungen auf den einzelnen Knoten sollen entweder commited oder aborted werden) Homogene VDBMS Gleiche Software auf allen Knoten Alle Nodes kennen sich untereinander Ein System gegenüber Client (transparent) Gleich wie zentral. DBMS nur das Daten fragmentiert/repliziert werden Heterogenes VDBMS Knoten mit unterschiedlicher Software - Problem: Verteilung der Transaktionen Nodes mit unterschiedlichen Schemas Problem: Ausführung von Queries Nodes kennen sich ev. Nicht – Problem: beschränkte Funtionalität für Kooperation weitere Probleme: - Unterschiedliche Protokolle - Unterschiedliche Datenmodelle Verteilte Datenhaltung Fragmentierung (Teile Relation r in Fragmente auf, die genug Information für Rekonstruktion haben) Horizontal(shardening) Tupel auf separate Nodes verteilt Vertikal - Einzelne Attribute auf sep. Nodes - Gemeinsame Schlüsselspalte nötig Transparenz Benutzer sieht globale Sicht, kennt die Replikation der Fragmente nicht Queries werden auf der Relation definiert, nicht auf den Fragmenten Replikation Redundante Speicherung auf Nodes Vorteile: Verfügbarkeit, Parallelität, Reduzierter Datentransfer Nachteile: Teure Updates (Propag. auf Nodes), Komplexe Synchronsation, Evt. Inkonsistente Zustände Verteilte Transaktionen Transaktion auf einer Datenbank ist immer lokal zu einer Session, mehrere verteilte RM Two Phase Commit (2PC) 2PC-Protokoll sichert Atomarität von verteilten Transaktionen Komponenten: Transaction Manager (koordiniert Transaktionen über mehrere Resource Managers), Resource Manager ((z.B. DBMS, Message Queue System) unterstützt lokale Transaktionen), Applikation (steuert Transaktionen) Phase 1: "prepare to commit“ - Alle Anfragen ob Trx. machbar wäre - Alle „Ready“ goto Phase 2 Phase 2: "commit Sind alle Ready: commit - Ein einziger Fehler: abort, alle rollback OBJEKTORIENTIERTE OODBMS Anwendung(Audio/Video, Umweltdaten, 3DModelle, Spez in wissenschaft, software engeniering) DBS kennt mehr Semantik:(Bessere Konsistenz Daten, Zugriff auf Daten schneller, da ganzes Objekt als Einheit laden oder speichern, Manipulieren der Daten wird einfacher) -Eher komplexe Daten und keine Anfragen; Klassen und Typen mit Extensionen, Projektion kann neue Typen generieren Architektur: 2-tier C/S: Fat Client:(Applikationscode, DBCode(Impl. Persistenten Klassen, Queries), DBServer(Datenspeicherung, Locking) OODBMS-Manifesto:Complex Objects, Object Identity, Encapsulation, Types and Classes, Type and Class Hierarchies, Overriding Overloading and late binding, Computational completeness, Extensibility, Persistence, Efficiency, Concurrency, Reliability, Declarative query Language Warum nicht POJOS serialisieren(Nicht vollständig( keine Klassendef, Probleme mit Schemaevo), Nicht orthogonal(Serialisierbare Klasse müssen Interface), Nicht Persistenz(Object Identity geht verloren), Nicht skalierbar(Objekt-Graph als ganzes serialisiert), Keine Transaktionen,Nicht concurrent) Persistenz Strategien(Orthogonale Persistenz, Persi Strat(By Reachability, by Instantiating, By Inheritance) Persistence By Reachability(Root object über namen gebindet, Alle Objekte über Referenz auch persistent, Laden von DB automatisch beim navigieren, Alle DB-OPS über Transaktion) 2 Architekturen: PageServer und ObjectServer, bei ersterem erhält Client nur Page OID systemweit eindeutig, auch nach löschen, Pointer Swizzling(Konversion von logischen Referenzen zu direkten Pointer Referenzen, Typisches Problem beim Deserialisieren) Es gibt logische OIDs(Nichts mir Speicherort, Umsetzungstabelle) und physische OIDs(Wiederverwendung nicht ausgschlossen nach DBReorganistation, Verschiebung teuer, aber sonst schneller) Gründe gegen: (Vielfach einfacheres Niveau als SQL, Mangel an komm. Datentypen wie Date, schlechte I18n, Kaum Objectview, wenig Tools, wenig Interoperabilität, Einführungsaufwand, Fehlende Drittprodukte) DB4O (Kein O/R-Mapper, Keine Anpassung an Klassen, Eine Zeile um zu speichern, Local und Client-Server, ACID-Transactions, ObjectCaching, Garbagecol, Verwaltung und Versionierung Schema, Java & .Net, kleiner Memory) Local“embedded“(Standalone db, 1, Prozess oder Thread aufs Mal), ClientServerMode(TCP/IP zum Server, Methode Db4o.openServer(fname, port), openClient(host, port, user, pass), „Embedded Mode“:(Kein Netzwerk, Port 0) Object Container (representiert dbfo-db, verwaltet Transaktionen und Referenzen und Lebenszyklus) ObjectContainer database = Db4o.openFile(„pub.db“); Speicher: database.store(object)-Deep Copy, Persistence by Reachability Abrufen: database.queryByExample(obj)-> genauer match// queryByExample(obj.Class) -> Alle von Klasse Nativ: ObjectSet<Publication> pubs = database.query(new Predicate<Publication>() { public boolean match (Publication pub) { return pub.getYear()> 2001;}}); Auch SOAD-Zugriffe möglich: Query query = db.query(); query.constrain(obj.class); query.descend(attr ).constrain( Integer.valueOf(2001)),greater(); query.execute(); Update: Objekt holen, Attribute ändern und wieder storen, tiefe 1, nur basistypen und Strings, Sonsts cascadeOnDelete() Löschen: Objekt holen, dann database.delete(obj); kaskadiert default nicht, sonsts Db4o.configure().objectClass(Author.class).cascadeOnDelete(true); Constraints nicht direkt unterstützt: Jede Klasse kann aber die Methoden objectOnDelete(), objectOnUpdate(), objectCanDelete() und objectCanUpdate(), die jeweils entsprechend aufgerufen werden Indexe mit Annotation @Indexed NoSQL Charakterisierung Kategorien MongoDB-CRUD-Beispiele Anforderung Web2.0: Grosse Datenmengen, Schreibzugriffe in verteilten Datenbanken, Hochverfügbarkeit RDBMS: Single Point Failure, Kennen Scaling up, nicht Scaling out, einfache Parallelisierung Definition: Nicht-Relational, Schemafrei, Archi auf BASE -> verzicht auf ACID!!!, Einfache Datenrepli, Einfaches API, keine Querynorm für NoSQL Neu: Verteilt, OpenSource, horizontal skalierbar, Applikationsdatenbanken, teil eines einzelnen Projekts, Webservice Transaktionsmanager Stellt sicher, dass alle RM’s entweder mit Commit oder Rollback abschliessen Ausserdem muss mit allen Fehlersituationen umgehen können, die durch das Kommunikationssystem verursacht werden Fehlersituationen Absturz des Transaktionsmanagers Absturz vor COMMIT gesendet -> Rückgängigmachen der Transaktion durch Versenden einer ABORT-Nachricht Absturz nachdem RM ein READY mitgeteilt haben -> Blockierung der RM, (Absturz des Koordinators-> Verfügbarkeit des RM bez. Anderer Transaktionen drastisch eingeschränkt) Absturz eines Resource-Managers Antwortet RM nicht innerhalb Timeout-Intervalls nicht auf die PREPARE-Nachricht -> RM abgestürzt; Koordinator schickt ABORTNachricht an alle Agenten, Abgestürzter Agent schaut beim Wiederanlauf in seine Log-Datei: Kein READY-Eintrag bezüglich Transaktion T -> RM führt abort durch, Failed to Koordinator READY-Eintrag aber kein commit-Eintrag -> RM fragt TM, was aus Transaktion T geworden ist; TM teilt COMMIT oder ABORT mit -> RM macht je nach dem REDO oder UNDO COMMIT-Eintrag vorhanden -> RM weiss, dass ein (lokales) REDO der Transaktion nötig ist Nachricht verloren PREPARE-Nachricht des TM an einen RM geht verloren oder READY-(oder FAILED-)Nachricht eines RM geht verloren -> Nach Timeout-Intervall TM geht TM davon aus, dass betreffender RM nicht funktionsfähig ->t ABORT-Nachricht an alle RM (Transaktion gescheitert), RM erhält im Zustand READY keine Nachricht zum Koordinator -> Rm ist blockiert, bis COMMIT- oder ABORT-Nachricht vom TM kommt X / Open XA (DTM) Standardisiert Schnittstellen zwischen Komponenten in verteilten Systemen Synchronisation in VDBMS Serialisierbarkeit Lokale Serialisierbarkeit an jeder der an den Transaktion beteiligten Stationen reicht nicht aus -> deshalb muss man bei der Mehrbenutzersynchronisation auf globaler Serialisierbarkeit bestehen Beispiel: Lokal serialisierbare Historie, Global nicht serialisierbar Zwei-Phasen-Sperrprotokoll in VDBMS 2PL wird auf verteilte Systeme adaptiert(Locken der Objekte, Lesen / Modifizieren, Freigabe der Locks) Jeder Knoten partizipiert in der Ausführung des Commit-Protokolls um globale Serialisierbarkeit zu gewähren Globaler Lock-Manager VDBMS unterhält einen globalen Lockmanager, Wenn Transaktion T was sperren will -> Anfrage an globalen Lockmanager. Entscheidung: ja: Lockmanager Meldung an anfragenden Knoten, nein: Anfrage verzögern bis Lock gewährt werden kann; Vorteil: Deadlock-Erkennung wie im Ein-Rechner-Fall Nachteile: Jede Sperranforderung einer Transaktion verursacht eine Nachricht, Transaktion muss synchron darauf warten -> schlechter Durchsatz und Antwortzeit; Zentraler Knoten ist Engpass für Leistung und Verfügbarkeit ("single point of failure"); Keine Knotenautonomie Lokaler Lock Manager Der verteilte Algorithmus greift auf die lokalen Lock-Manager auf den einzelnen Knoten zu, Der lokale Lock-Manager kontrolliert den Zugriff auf die lokalen Data-Items. Locks werden im Rahmen des 2PCProtokolls allokiert und freigegeben; Vorteile: Arbeit ist verteilt; robuster gegen Ausfälle Nachteile: Deadlock-Detektion schwierig; Performance kann stark beeinträchtigt werden Zeitstempel basierte Synchronisation Jede Transaktion bekommt einen global eindeutigen Zeitstempel Hauptproblem: Generierung einer global eindeutigen Zeit(lokal eindeutigen Zeitstempel + anhängen Rechner-Id Konfliktoperationen verschiedener Transaktionen müssen stets in der Reihenfolge der Transaktionszeitmarken erfolgen; Bedingungen verletzt -> betroffene Transaktion zurückgesetzt und mit neuen Zeitstempel wiederholt; Überprüfung mit Schreib- und Lese-Zeitstempeln an den Datenobjekten, (Write Time Stamp) WTS bez. der Lesezeitstempel (read time stamp) RTS entspricht Transaktionszeitmarke derjenigen Transaktion, die das Objekt zuletzt geändert bez. gelesen hat, wenn gilt:(Ts(T) < WTS(x)): Lesezugriff auf Objekt x nicht zulässig Für Schreibzugriff muss gelten:(Ts (T) > Max(RTS(x), WTS(x), Deadlocks in VDBMS Verteilter Deadlock Erkennung von Deadlocks Mit Timeout: - Nach Ablauf zurücksetzen/neu starten - Simpel - Zu lang: Ressourcen schlecht genutzt - Zu kurz: „Deadlocks“ wo keine wären Zentralisiert - Nodes geben Infos zu waits weiter - Zyklus im Graphen = Deadlock - Hoher Aufwand / Messaging - PhantolDeadlocks (Fehlalarm) durch überholte Messages bei Info-Austausch Vermeidung Optimistisch: - Validierung nach Trx. Abschluss Timestamp-basierend - Entscheidung ob Trx. Serialisierbar Distributed Query Processing Verteilte Anfragen Zentralisiertes System -> Kosten der Query bestimmt von der Anzahl Disk-I/O's Verteiltes System -> Kosten der Query bestimmt von: -Anzahl Netzwerk I/O's; -Performance-Gewinn durch paralleles Abarbeiten auf mehreren Knoten) Beispiel: SELECT AVG(s.age) FROM Sailer s WHERE s.rating > 3 AND s.rating < 7; Horizontale Fragmentierung:Tupels mit rating < 5 in Zürich, Tupels mit rating >= 5 in Genf Ausführung der Query: Führe die Query auf dem Knoten Zürich und Genf aus => Resultat auf dem aufrufenden Knoten zusammenführen, Wenn die WHERE-Klausel S.rating > 6 hiesse, dann wäre nur der Knoten Genf beteiligt Verteilte Queries Vertikale Fragmentierung:Tupels mit tis, sid und rating in Zürich, Tupels mit tid, sname, age in Genf Ausführung der Query: Relation über Tupel-Identifier tid durch Join rekonstruieren, Auswertung der Query) Relation repliziert auf beiden Knoten, Ausführung der Query(Wahl des Knotens abhängig von lokalen und Transport-Kosten) Einfache Joins R ist in der Site N1, S in der Site N2 gespeichert, Für jede Anfrage vom Knoten Ni muss das System die Resultate auf Ni liefern Query Processing Strategien Einfache Strategien: Join wird auf einem Knoten ausgeführt(Ship Whole: Relation vollständig an den Join-Knoten übertragen(Minimale Nachrichtenanzahl, hohes Datenvolumen); Fetch as needed: Für jedes Tupel der lokal vorliegenden Relation wird der Wert des Join-Attributs an den anderen Rechner geschickt, welcher das passende Tupel überträgt; Hohe Nachrichtenanzahl, eventuell tieferes Datenvolumen (bei hoher join-Selektivität)), Kostenabschätzung anhand der Kommunikationsanteile(Nachrichtenanzahl (#Nachrichten), Übertragungsvolumen (Anzahl Attributwerte #AW)) Semi Join Reduktion des Overhead des Fetch-As-Needed-Ansatzes, Übertrage sämtliche Verbungattributwerte der ersten Relation in einer Nachricht an den Knoten der zweiten Relation übertragen, Daraufhin können alle Verbundpartner in einer Nachricht an den ersten Knoten zurückgesendet werden, wo dann die Join-Bildung erfolgt REPLIZIERTE DATENBANKSYSTEME Theorie Konfiguration Synchronisationsprotokolle Merge Replikation Replikation in kommz. Systemen PRO's: Verteilung der Daten -> High Avalability, Skalierbarkeit (Lastverteilung), Vorbeugung Datenverlust, Grundlage Clients im Disconn. Mode Anwendungen: Load-Balancing, Remote Servers, Real-Time OLAP, Hochverfügbarkeit (Warm Standby), Mobile Datenbanken) Loadbalancing: High Performance Cluster, hochverfügbar skalierbar, Master-Master asynchron oder synchron, Sehr schnelle Verbindungen) Anwendungen distributed geographic data processing with high availability(Asynchroner Multi-Master (Peer-Peer), Verteilte Standorte, (Mittel-)schnelle Verbindung mit relativ hoher Verfügbarkeit Real Time OLAP: Online Analytics Processing)(Backup und Data Warehouse, Asynchronous Master-Slave, Schnelle / mittelschnelle Verbindung, Räumlich zentralisiert oder verteilte Standorte Warm Standby: Aynchronous / Synchronous Master-Slave, Schnelle, hochverfügbare Verbindung, Zentraler Standort (synchron) oder geographisch verteilt (asynchron) Mobile Datenbanken: Master-Master / Merge Replikation, Disconnected Clients, (auch) langsame Verbindungen mit tiefer Bandbreite Korrektheit: Schwache vs. strenge Konsistenz, Protokolle/Verfahren für replizierte Daten damit nur eine Kopie verändert wird, trotz Partitionierung des Netzwerks Starke Konsistenz (synchron): ACID -> 1-Kopien-Äquivalenz, alle Replikate wechselseitig konsistent, Replikation mit Write-All-ReadAny, 2-Phasen-Commit Protokoll, grosser Protokoll-Overhead, Verfügbarkeit/Fehlertoleranz reduziert, Vollständige Replikationstransparenz: Jeder Zugriff liefert jüngsten transaktionskonsistenten Objektzustand Schwache Konsistenz (asynchron): BASE -> Eventuell keine strikte Serialisierung von Änderungen, paralelle Änderung mit nachträglicher Konfliktbehebung, Primäre Kopie der Daten, Replikas nicht allzeit identisch, Verfügbarkeit/Fehlertoleranz/Performanz hoch Skalierung: Vertikal => HW eines Systems aufrüsten, Horizontal/Sharding => Data Set auf mehrere Systeme verteilen Master-Slave: Primäre Kopie auf Master, Updates auf Master => Publisher, Updates auf (Read-Only) Slaves propagiert => Subscriber Multi-Master: Jeder Master kann Updates durchführen, komplexe Sync notwendig, Asynchrone Replikation, Konflikte durch gleichzeitige Änderungen Replikationsarten: Synchron (eager)-> Master aktualisiert alle Replikas in einer 2 PC Transaktion, Daten sind immer konsistent (ACID), Verteiltes Locking kann zu Deadlocks und Wartebedingungen führen, Verfügbarkeit eingeschränkt durch Fehlersituationen (zum Beispiel Knoten nicht verfügbar) <-Asynchron (lazy) mit Versionsnummern Änderungen am Master werden nach Abschluss der Transaktion asynchron an die Replikas weitergeleitet, Synchronisation entweder periodisch oder nach Commit, -> Replikas werden nicht zur selben Zeit aktualisiert, -> Konflikte können auftreten (sogenannte eventually consistent reads), Höhere Performance als bei synchroner Replikation, Höhere Verfügbarkeit, da ein Ausfall eines Knotens nicht zum Ausfall des gesamten Systems führt Update Propagation: Wie werden die Update-Operationen beim Master erkannt und weitergeleitet?, Triggers: Bei jedem Update auf dem Master feuert ein Trigger, der die Änderungen in eine "update propagation table" einträgt), Log-Analyse: Das Log-File wird gesnifft und die Änderungen werden daraus hergeleitet, In der Regel effizientere Methode Synchronisationprotokolle für Multi-Master Konfigurationen Read-One/Write-All Strategie (ROWA): Write => Synchrone Änderung aller Replikate bei Abschluss der Transaktion -> Alle Replikate sind konsistent, Bevorzugte Behandlung von Lesezugriffen(Lesen eines beliebigen (lokalen) Replikas, Hohe Verfügbarkeit für Leser), Sehr hohe Kosten für Änderungstransaktionen(Schreibsperren bei allen Rechnern anfordern, Propagierung der Änderungen im Rahmen des CommitProtokolls (2PC)), Verfügbarkeitsproblem: Änderungen von Verfügbarkeit aller kopienhaltenden Knoten abhängig(Sei P die mittlere Wahrscheinlichkeit, dass ein Knoten verfügbar ist, Bei N Kopien beträgt die Wahrscheinlichkeit, dass geschrieben werden kann: P ^ N, Wahrscheinlichkeit, dass gelesen werden kann: 1 - (1 - P)^N, Variante: Zur Abschwächung des Verfügbarkeitsproblems wurde die Write-All-Available-Variante vorgeschlagen, bei der nur die Replikate der verfügbaren Rechner gesperrt und aktualisiert werden müssen. Für einen ausgefallenen Rechner werden die nicht vorgenommenen Modifikationen eigens protokolliert und nach Wiedereinbringen des Rechners nachgefahren) Majority Protocol: Lesen oder Schreiben eines Objektes verlangt Zugriff auf Mehrheit der Replikate, Jedes Replikat kann gleichzeitig von mehreren Transaktionen gelesen, jedoch nur von einer Transaktion geändert werden, Änderungstransaktionen auf Datenelement Q:(Die Mehrheit der Replikate muss von den lokalen Lockmanagern mit Sperren belegt werden, Schreiboperationen werden auf allen gesperrten Replikaten ausgeführt -> Versionsnummer wird erhöht -> mindestens ein Replikat ist aktuell), Vor- und Nachteile(Funktioniert auch bei Ausfall einiger Knoten, Funktioniert nicht bei 2 Knoten, Hoher Kommunikationsaufwand(2(n/2 + 1) Messages für Lock-Requests, (n/2 + 1) Messages für Unlock-Requests), Potential für verteilten Deadlock auch bei nur einem Datenobjekt q:(Zum Beispiel jede von 3 Transaktionen hält locks auf 1 / 3 der Replikate von Q) Quorum Consensus: Verallgemeinerung der bisher vorgestellten Verfahren, Jede Kopie erhält bestimmte Anzahl von Stimmen (votes), Protokoll:(Leser erfordert R Stimmen (Read-Quorum), Schreiben erfordert W Stimmen (Write-Quorum), R + W > V (= Summe aller Stimmen)(- > Ein Objekt kann nicht zur gleichen Zeit gelesen und geändert werden , -> Jedes Lese-Quorum enthält mindestens eine aktuelle Kopie), W > V / 2(-> Ein Objekt kann nicht gleichzeitig (auch bei Netzwerk-Partitionierung) von mehr als einer Transaktion geändert werden Beispiel: Objekt A sei an vier Rechner R1 bis R4 repliziert Stimmenverteilung <2, 1, 1, 1>, das heisst V = 5, Sei R = 3, W = 3, -> R1 nicht beteiligt: drei Rechner, um einen Lese- oder Schreibzugriff abzuwickeln, -> R1 beteiligt: R1 und ein anderer Rechner für eine Schreiboperation, Sei R = 2, W = 4 (Bevorzugung der Lesezugriffe, -> R1 beteiligt: R1 und zwei andere Rechner für Schreibzugriff, Lesezugriffe lokal auf R1, -> R1 nicht beteiligt: Objekt nicht änderbar Eigenschaften:Festlegung von V, R und W erlaubt Trade-Off zwischen Lese- und Schreibkosten sowie zwischen Leistung und Verfügbarkeit, Majority-Protokoll Spezialfall: Eine Stimme pro Kopie, R und W einfache Mehrheit Snapshot-Replikation: Bisherige Verfahren sehr aufwendig -> Synchronisation und Kommunikationskosten, Snapshot = Einfacheres Verfahren mit schwächeren Konsistenzanforderungen, Snapshot = materialisierte View (verteilt), nur für Lesezugriffe, Aktualisierung periodisch oder nach jeder Änderung in der Ursprungstabelle Merge Replikation Unsynchronisierte Änderung eines replizierten Objekts an mehreren Knoten (Multi-Master) mit asynchroner Propagierung der Änderungen, Performance- und Verfügbarkeitsvorteile gegenüber synchroner Aktualisierung, Unabdingbar für mobile DB-Nutzung (disconnected client) mit Änderungsbedarf (zum Beispiel Aussendiest-Mitarbeiter) (Eintragung neuer Kunder / Aufträge (geringe Konfliktgefahr)), Probleme:(Nachträgliche Konflikt-Behandlung, Mischen paralleler Änderungen (Merge-Replikation)) Konflikte Merge Replikation Konfliktmöglichkeiten für(Update, Insert (zum Beispiel Eindeutigkeit), Delete), Konflikterkennung( Objektänderungen enthaltenen Zeitstempel v der Vorgängerversion und neuen Wert, Konflikt: falls lokaler Zeitstempel einer zu aktualisierenden Objektversion abweicht von v, Konfliktwahrscheinlichkeit wächst mit Anzahl der änderbaren Replikate und Aktualisierungsverzögerung), Anwendungsspezifische Konflikt-Behandlung (reconciliation)(Vordefinierte Strategien in externer DBS: "last timestamp wins", "site priority", Benutzerdefinierte Konfliktauflösungsroutinen oder manuelle Konfliktbehebung, Gefahr von "lost updates" und anderen Anomalien (keine Seralisierbarkeit)) Konflikte Update conflicts: Bsp. 2 gleichzeitige Updates auf unterschiedlichen Replikas mit unterschiedlichen Werten => timestamp vergleichen Uniqueness conflicts: 2 Nodes wollen zu mir einen neuen Eintrag replizieren, beide haben den selben PK Delete conflicts: Passiert, wenn zwei Transaktionen von verschiedenen Nodes passiert, bei der eine Transaktion ein Data-Record löschen will und die andere sie updaten oder löschen wll Ordering conflicts: kein Beispiel Konflikte können automatisch durch vordefinierte Strategien oder durch applikationsspezifische (programmierte) Regeln auflöst werden) MongoDB:Brücke zwischen key-value(schnell und skalierbar) und traditionellen RDBMS mit vielen Funktionen Replika Sets: Normal 3 Nodes pro Set, 1 Primary Node wird gewählt, W nur an Primary/immer nur 1 Primary, Daten werden nach W repliziert, Load-Balancing für R Automatic Failover: Verbindung wird durch Heartbeat überprüft, bei Ausfall des Primary wir ein neuer Primary bestimmt Syncing: Oplog ist eine geordnete Liste aller W's, Nach W auf Primary wird die Primary Oplog nachgeführt, Secondaries kopieren die Primary Oplog und führen die Änderungen asynchron nach Majorities: Ein Primary bleibt nur, solange er eine Mehrheit der Members erreicht, Nur eine Mehrheit an Members kann einen Primary wählen => sicherstellung dass nur ein Primary vorhanden ist, eine W gilt als safe, wenn sie an eine Mehrheit repliziert wurde Write Concerns: Wert, welche für eine W Operation abgegeben werden kann, Errors ignored/Unacknowledged: fängt: Netzwerk errors, Ack (default): Ack nach In-Memory Eingang auf Primary, fängt Netzwerk-, Duplicate Key- other Errors, Journaled: Ack nach Eintrag ins Oplog, stellt sicher das W einen shutdown überlebt, Replica Ack: Ack nach Replizierung, OPTIONAL: Wert für Anzahl Replizierungen Read Preference: Definiert die Routen für R-Operationen, Primary (default): Alle R von Primary, wenn unerreichbar=> Error, primaryPreferred: Falls Primary unerreichbar => Secondary, Secondary/secondaryPreferred: (vertauscht), Nearest: Automatisch nächster Vorteile: Latenz verbessern, R throughput erhöhen, für Backup-Operationen, R während failover ermöglichen, Secondary liefert zuverlässlichere Daten!!! Sharding: Partitionierung (Unterteilung) einer DatenKollektion auf mehrere Systeme unter Einhaltung einer geordneten Form Query Routers (mongos): Interface für Clients/Applikationen, Verarbeitet Anfragen von Clients/Apps, ein Sharded Cluster verfügt normal über mehrere mongos => Load-Balancing, Config Servers: Speichern die Metadaten des Sharded Clusters, Enthalten Mapping Clusterdaten Shards, Query Router verwendet dies für Datenzugriff, Production sharded Clusters verfügen über genau 3 Config Servers Sharded Collection Balancing: Shard Keys => Chunks (Splitting/Balancing) => Shards, Shard Key: z.B. Index-Feld, werden in Chunks aufgeteilt (Range oder Hashbasiert auf Shard Key, Chunk: Zusammenhängende Range von Shard Keys innerhalb eines Shards, Splitting => Chunk der zu gross wird wird aufgeteilt, Shard: Unterschied an Anzahl Chunks zwischen Shards zu gross => Chunks von einem Shard auf anderen migrieren