Programmierung von Datenbank Anwendungen

Werbung
Programmierung von
Datenbank Anwendungen
ESQL, ODBC, JDBC und co
IS: Datenbanken, © Till Hänisch 2000
Methoden
 
bisher interaktive Verwendung von SQL
 
 
 
Terminal
Skripte
Ausführen von SQL aus
Programmiersprache heraus
 
 
proprietäre APIs
standardisierte Schnittstellen
 
 
statisch (embedded SQL)
dynamisch (ODBC, JDBC)
IS: Datenbanken, © Till Hänisch 2000
Embedded SQL
 
Einbettung von SQL-Statements in Wirts-Sprache
 
 
 
i.w. gleiche Syntax wie bei interaktivem SQL,
zusätzlich Konstrukte für
 
 
 
 
 
C,COBOL, PL/1, FORTRAN, PASCAL,...
Vor-Übesetzung des Programms in Wirts-Sprache
(precompile)
Einbettung der SQL-Befehle
Fehlerbehandlung
Übergabe von Variableninhalten
Übergabe von Query-Ergebnissen
Einfache, sprachunabhängige Syntax für Precompiler
 
 
EXEC SQL Präfixc für SQL-Kommandos
":" als Kennzeichner für Variablen
IS: Datenbanken, © Till Hänisch 2000
Vorgehen
example.cp
C Source mit eingebettetem SQL
Precompiler für C
example.c
C Source, SQl durch DBMS-spezifische
Funktionsaufrufe ersetzt
C Compiler
example.o
Linker
example[.exe]
Object Code
DBMSLibrary
ausführbares Programm
IS: Datenbanken, © Till Hänisch 2000
Tupelvariablen
SQL liefert Tupelmenge, Darstellung in C, PASCAL,...
"Impedence mismatch"
Typkonzept des RDBMS und der Wirtssprache passen nicht zusammen
Lösung: Cursor
Iterator, Tupel-Zeiger für satzweise Verarbeitung
EXEC SQL DECLARE name CURSOR FOR select statm.
Operationen:
OPEN
führt Abfrage aus
CLOSE
FETCH name INTO :var1, :var2,...
überträgt Werte der Attribute des aktuellen Datensatzes in
Variablen und setzt Zeiger eins weiter
IS: Datenbanken, © Till Hänisch 2000
prinzipieller Aufbau
EXEC SQL BEGIN DECLARE SECTION;
Deklaration der Übergabevariablen
EXEC SQL END DECLARE SECTION;
EXEC SQL INCLUDE SQLCA;
EXEC SQL CONNECT :userid IDENTIFIED BY :password
EXEC SQL DECLARE c CURSOR FOR SELECT * FROM EMP;
EXEC SQL OPEN c;
for(;;) {
}
EXEC SQL FETCH ...
EXEC SQL CLOSE c;
EXEC SQL DISCONNECT;
IS: Datenbanken, © Till Hänisch 2000
Anmerkungen
 
statisches SQL
 
 
 
 
 
tupelweise Verarbeitung u.U. nicht effizient
 
 
wird im Programm fest definiert und kann vom Precompiler
ausgewertet werden
SQL muß vorher bekannt sein !
Wie ist isql implementiert ?
dynamisches SQL (in ESQL nicht möglich)
ein Funktionsaufruf pro Tupel - > Array Fetch,...
ESQL ist standardisiert
 
 
 
wie SQL selbst
passende Umgebung muß zum Programm gelinkt werden
Geht nicht, wenn Auswahl des DB-Systems erst zur Laufzeit
erfolgen soll ! -> ODBC
IS: Datenbanken, © Till Hänisch 2000
Native API, Beispiel OCI
 
 
 
Oracle Call Interface (CLI)
kompliziert, mächtig, Oracle spezifisch
bestimmter Funktionen nur mit OCI
 
 
mehrere Transaktionen
BLOBs
static char cmd[] = "INSERT INTO MESSAGE(SEVERITY,CODE) VALUES (:Severity,:Code)";
if (!olog(&lda, hda, (unsigned char *)pszUserid, -1,
(unsigned char *)pszPassword, -1, (unsigned char *)pszNetAlias, -1,
(ub4)OCI_LM_DEF))
if (!oopen(&cda, &lda, (text *) 0, -1, -1, (text *) 0, -1))
if (!oparse(&cda,(unsigned char *) cmd,-1,0,2))
ProcessMessage(&msg); /* normaler C-Code */
if ((!obndrv(&cda,(unsigned char *)":Severity",-1,(unsigned char *) Severity,
strlen(Severity), VARCHAR2_TYPE,-1,0,0,-1,-1))
|| (obndrv(&cda,(unsigned char *)":Code",-1,(unsigned char *)Code,
strlen(Code),VARCHAR2_TYPE,-1,0,0,-1,-1)))
if (!oexec(&cda))
IS: Datenbanken, © Till Hänisch 2000
Embedded SQL
 
 
 
 
SQL wird in Standard C (COBOL,...) eingebettet
Quellcode datenbankunabhängig
Precompiler, der OCI erzeugt
ausführbares Programm ist datenbankabhängig
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR pszUserid[20];
VARCHAR pszPassword[20];
VARCHAR Severity[5];
VARCHAR Code[10];
EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT :username IDENTIFIED BY :password;
ProcessMessage(&msg); /* normaler C-Code */
EXEC SQL INSERT INTO MESSAGE(Severity, Code)
VALUES (:Severity, :Code);
IS: Datenbanken, © Till Hänisch 2000
Warum ODBC ?
Am Anfang waren die Daten, sie waren unformatiert, und Dunkelheit herrschte auf der
Erde. Und Codd sagte: „Es werde ein relationales Datenmodell“. Und so geschah es.
Und Codd sagte: „Die Daten sollen von den Datenstrukturen der Applikationsprogramme
getrennt werden, so daß eine Datenunabhängigkeit entstehe“. Und es war gut.
Und die DBMS-Hersteller sagten: „Wir wollen fruchtbar sein und uns mehren“. Und so
geschah es.
Und die Benutzer sagten: „Wir wollen Applikationen einsetzen, um auf die Daten von allen
DBMS-Herstellern zuzugreifen“
Und die Applikationsentwickler senkten die Häupter und sagten: „Wir müssen durch das
finstere Tal mit den Precompilern oder CLI‘s, Kommunikationsstacks und Protokollen aller
Hersteller wandern“. Und es war nicht gut
...
Und so entstand ODBC
(Kyle Geiger, Inside ODBC)
IS: Datenbanken, © Till Hänisch 2000
ODBC-Architektur
Anwendung
ODBC
Treibermanager
ODBC
Treiber
ODBC
Treiber
ODBC
Treiber
Datenbank
Datenbank
Datenbank
IS: Datenbanken, © Till Hänisch 2000
ODBC
 
 
 
Open Database Connectivity
Industriestandard (Microsoft, IBM,...)
datenbankunabhängig
static char cmd[] = "INSERT INTO MESSAGE(SEVERITY,CODE) VALUES (?,?)";
rc=SQLAllocEnv(&henv);
rc=SQLAllocConnect(henv,&hdbc);
rc=SQLConnect(hdbc,“Kurs",SQL_NTS,ODBC_USERNAME,SQL_NTS,ODBC_PASSWORD,SQL_NTS);
rc=SQLAllocStmt(hdbc,&hstmt);
rc = SQLPrepare(hstmt,cmd,SQL_NTS);
rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_VARCHAR,
strlen(Severity),0,Severity,0,NULL);
rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_VARCHAR,
strlen(Code),0,Code,0,NULL);
rc = SQLExecute(hstmt);
IS: Datenbanken, © Till Hänisch 2000
JDBC
 
Java Database Connectivity
import java.sql.*;
class Employee
{
public static void main (String args [])
throws SQLException
{
DriverManager.registerDriver(new com.sybase.jdbc.SybDriver());
Connection conn =
DriverManager.getConnection ("jdbc:sybase:Tds:vaio:9898",
"ba", "isdb00");
Statement stmt = conn.createStatement ();
ResultSet rset = stmt.executeQuery ("select empno,ename from emp");
// Iterate through the result and print the employee names
while (rset.next ())
System.out.println (rset.getInt(1) + " " + rset.getString (2));
}
}
IS: Datenbanken, © Till Hänisch 2000
JDBC - Statement
Statement stmt = conn.createStatement ();
Stmt.executeQuery(“SELECT * FROM EMP“);
PreparedStatement ps = conn.prepareStatement(
„SELECT * FROM EMP WHERE EMPNO = ?“);
 
 
Kann SQL-Anweisungen ausführen
Spezialfall: PreparedStatement:
 
 
Bei mehrfacher Ausführung bleibt SQL-Text gleich,
muß nicht bei jeder Ausführung analysiert werden
Bietet u.U. bessere Performance
IS: Datenbanken, © Till Hänisch 2000
JDBC - ResultSet
ResultSet rset = stmt.executeQuery ("select empno,ename from emp");
while (rset.next ())
System.out.println (rset.getInt(1) + " " + rset.getString (2));
}
 
 
 
 
 
executeQuery liefert ResultSet-Objekt zurück
Kapselt Cursor, kann Ergebnis zeilenweise
durchgehen
Steht nach executeQuery vor dem ersten Datensatz
next() geht einen Datensatz weiter, liefert true
zurück, solange aktueller Datensatz gültig
Zugriff auf Spalten mit getXXX (getInt, getString,...)
IS: Datenbanken, © Till Hänisch 2000
JDBC - PreparedStatement
PreparedStatement ps = conn.prepareStatement(
„SELECT * FROM EMP WHERE EMPNO = ?“);
for(...) {
ps.setInt(1,4711);
ResultSet rset = ps.execute();
...
}
 
Platzhalter werden mit setXXX mit Werten belegt
 
Erster Parameter ist Index des Parameters
IS: Datenbanken, © Till Hänisch 2000
JDBC - Transaktionskontrolle
conn.setAutoCommit(false);
Conn.commit();
Conn.rollback();
Conn.setTransactionIsolation(
Connection.TRANSACTION_SERIALIZABLE);
 
AutoCommit führt nach jedem execute ein commit durch (oft
Standard, sinnvoll ?)
 
 
Manuelle Commit-Steuerung oft sinnvoller
Nicht alle Isolationlevel werden von allen Datenbanken
unterstützt (siehe Datenbanken II)
IS: Datenbanken, © Till Hänisch 2000
stored procedures mit mysql
 
seit Version 5 unterstützt mysql stored
procedures/functions
 
 
 
 
Kapselung von Zugriffen
eigene Funktionen für SQL (reporting)
Dinge, die in SQL schwierig sind ....
Trigger
 
 
Implementierung von Business Logik
Sicherung von Konsistenz
 
Syntax:
 
http://www.mysqltutorial.org/mysql-stored-procedure-tutorial.aspx
IS: Datenbanken, © Till Hänisch 2000
Kapselung von Zugriffen
 
Beispiel emp: Gehalt ändern
-- create table emp_history(wann datetime not null,
-- von int not null, nach int not null, wer varchar(255) not null);
delimiter '$$';
drop procedure if exists change_salary$$
create procedure change_salary (l_empno int, l_new_sal int)
begin
declare l_sal int;
select sal into l_sal from emp where empno=l_empno;
update emp set sal = l_new_sal where empno=l_empno;
insert into emp_history (wann,von, nach, wer)
values (now(),l_sal,l_new_sal,user());
end$$
delimiter ;
IS: Datenbanken, © Till Hänisch 2000
Ergebnis
mysql> call change_salary(7369,1000);
Query OK, 1 row affected (0.07 sec)
mysql> select * from emp;
+-------+--------+-----------+------+------------+------+------+--------+
| empno | ename | job
| mgr | hiredate
| sal | comm | deptno |
+-------+--------+-----------+------+------------+------+------+--------+
| 7369 | smith | clerk
| 7902 | 1980-12-17 | 1000 | NULL |
20 |
14 rows in set (0.00 sec)
mysql> select * from emp_history;
+---------------------+-----+------+----------------+
| wann
| von | nach | wer
|
+---------------------+-----+------+----------------+
| 2011-11-27 16:15:14 | 800 | 1000 | root@localhost |
+---------------------+-----+------+----------------+
1 row in set (0.00 sec)
IS: Datenbanken, © Till Hänisch 2000
Zugriffsrechte
-- alice hat zunächst keine Rechte auf emp
grant select on test.emp to 'alice'@'localhost';
grant execute on procedure test.change_salary to 'alice'@'localhost';
-- als Benutzer alice
mysql> update test.emp set sal=3000 where empno=7369;
ERROR 1142 (42000): UPDATE command denied to user 'alice'@'localhost' for table '
mysql> call test.change_salary(7369,5000);
Query OK, 1 row affected (0.03 sec)
mysql> select * from test.emp;
+-------+--------+-----------+------+------------+------+------+--------+
| empno | ename | job
| mgr | hiredate
| sal | comm | deptno |
+-------+--------+-----------+------+------------+------+------+--------+
| 7369 | smith | clerk
| 7902 | 1980-12-17 | 5000 | NULL |
20 |
mysql> select * from emp_history;
+---------------------+------+------+-----------------+
| wann
| von | nach | wer
|
+---------------------+------+------+-----------------+
| 2011-11-27 16:36:23 | 3000 | 5000 | alice@localhost |
IS: Datenbanken, © Till Hänisch 2000
Eigene Funktionen
 
Beispiel emp: Gehalt
delimiter '$$';
drop function if exists gehalt$$
create function gehalt (l_empno int) returns int
begin
declare l_sal int;
declare l_comm int;
declare retval int;
select sal,comm into l_sal, l_comm from emp where empno=l_empno;
if (l_comm is null) then
set l_comm = 0;
end if;
set retval = l_sal + l_comm;
return retval;
end$$
delimiter ;
IS: Datenbanken, © Till Hänisch 2000
Ergebnis:
mysql> select ename,gehalt(empno) as Gehalt from emp;
+--------+--------+
| ename | Gehalt |
+--------+--------+
| smith |
1000 |
| allen |
1900 |
| ward
|
1750 |
| jones |
2975 |
| martin |
2650 |
| blake |
2850 |
| clark |
2450 |
| scott |
3000 |
| king
|
5000 |
| turner |
1500 |
| adams |
1100 |
| james |
950 |
| ford
|
3000 |
| miller |
1300 |
+--------+--------+
14 rows in set (0.00 sec)
IS: Datenbanken, © Till Hänisch 2000
Erzeugung von Nummern
 
Beispiel: Rechnungsnummern (2011/21)
mysql> select make_rno();
+------------+
| make_rno() |
+------------+
| 2011/3
|
+------------+
1 row in set (0.00 sec)
mysql> select make_rno();
+------------+
| make_rno() |
+------------+
| 2011/4
|
+------------+
1 row in set (0.00 sec)
mysql> select * from numbers;
+----------+---------+
| tab
| current |
+----------+---------+
| rechnung |
4 |
+----------+---------+
1 row in set (0.00 sec)
mysql>
IS: Datenbanken, © Till Hänisch 2000
Aufgabe:
 
 
Erstellen Sie eine Funktion make_rno, die eine Nummer
der Form YYYY/nnnn erzeugt. YYYY ist das aktuelle Jahr
( mysql: YEAR(NOW()) ), nnnn eine laufende Nummer, die
Sie aus der Tabelle NUMBERS erzeugen.
  Nachteil: Am Jahresbeginn muß die Nummer in der
Tabelle zurückgesetzt werden
Erweitern Sie die Funktion so, dass automatisch im neuen
Jahr wieder bei 1 begonnen wird. Erweitern Sie dazu die
Tabelle numbers um ein Attribut Jahr, in dem Sie das Jahr
zur letzten erzeugten Nummer speichern. Prüfen Sie dann
in make_rno, ob Sie sich noch im gleichen Jahr befinden,
falls nicht, setzen Sie current auf 1.
IS: Datenbanken, © Till Hänisch 2000
schwierige Dinge ...
 
Beispiel: Hierarchie ausgeben
delimiter '$$';
drop function if exists emp_level$$
create function emp_level (l_empno int) returns int
begin
declare l int;
declare mgr_id int;
set l = 0;
select mgr into mgr_id from emp where empno=l_empno;
while (mgr_id is not null) do
set l = l + 1;
set l_empno = mgr_id;
select mgr into mgr_id from emp where empno=l_empno;
end while;
return l;
end$$
IS: Datenbanken, © Till Hänisch 2000
delimiter ;
Ergebnis:
 
Beispiel: Hierarchie ausgeben
mysql> select ename,empno,mgr,emp_level(empno) from emp;
+--------+-------+------+------------------+
| ename | empno | mgr | emp_level(empno) |
+--------+-------+------+------------------+
| smith | 7369 | 7902 |
3 |
| allen | 7499 | 7698 |
2 |
| ward
| 7521 | 7698 |
2 |
| jones | 7566 | 7839 |
1 |
| martin | 7654 | 7698 |
2 |
| blake | 7698 | 7839 |
1 |
| clark | 7782 | 7839 |
1 |
| scott | 7788 | 7566 |
2 |
| king
| 7839 | NULL |
0 |
| turner | 7844 | 7698 |
2 |
| adams | 7876 | 7788 |
3 |
| james | 7900 | 7698 |
2 |
| ford
| 7902 | 7566 |
2 |
| miller | 7934 | 7782 |
2 |
+--------+-------+------+------------------+
IS: Datenbanken, © Till Hänisch 2000
14 rows in set (0.00 sec)
schöner:
delimiter '$$';
drop function if exists emp_pos$$
create function emp_pos (l_empno int) returns char(30)
begin
declare l varchar(255);
declare mgr_id int;
set l = '';
select mgr into mgr_id from emp where empno=l_empno;
while (mgr_id is not null) do
set l = concat(l,'-');
set l_empno = mgr_id;
select mgr into mgr_id from emp where empno=l_empno;
end while;
return l;
end$$
delimiter ;
IS: Datenbanken, © Till Hänisch 2000
Ergebnis:
mysql> select concat(emp_pos(empno),ename)
order by length(emp_pos(empno)) asc;
+------------------------------+
| concat(emp_pos(empno),ename) |
+------------------------------+
| king
|
| -blake
|
| -clark
|
| -jones
|
| --allen
|
| --james
|
| --ward
|
| --scott
|
| --ford
|
| --miller
|
| --martin
|
| --turner
|
| ---smith
|
| ---adams
|
+------------------------------+
14 rows in set (0.00 sec)
from emp
IS: Datenbanken, © Till Hänisch 2000
mit Oracle:
SELECT LPAD(' ',2*(LEVEL-1)) || empno
empno,ename,job,mgr,sal,deptno,LEVEL
FROM emp
CONNECT BY PRIOR empno = mgr
START WITH job = 'PRESIDENT';
EMPNO
-----------7839
7566
7788
7876
7902
7369
7698
7499
7521
7654
7844
7900
7782
7934
ENAME
---------KING
JONES
SCOTT
ADAMS
FORD
SMITH
BLAKE
ALLEN
WARD
MARTIN
TURNER
JAMES
CLARK
MILLER
JOB
MGR
SAL
DEPTNO LEVEL
--------- ---------- ---------- ------ ----PRESIDENT
5000
10
1
MANAGER
7839
2975
20
2
ANALYST
7566
3000
20
3
CLERK
7788
1100
20
4
ANALYST
7566
3000
20
3
CLERK
7902
800
20
4
MANAGER
7839
2850
30
2
SALESMAN
7698
1600
30
3
SALESMAN
7698
1250
30
3
SALESMAN
7698
1250
30
3
SALESMAN
7698
1500
30
3
CLERK
7698
950
30
3
MANAGER
7839
2450
10
2
IS: Datenbanken, © Till Hänisch 2000
CLERK
7782
1300
10
3
Trigger
 
Beispiel emp: Änderung protokollieren
delimiter '$$';
drop trigger if exists t_emp$$
create trigger t_emp before update on emp for each row
begin
insert into emp_history(wann,von,nach,wer)
values (now(),old.sal,new.sal,user());
end$$
delimiter ;
IS: Datenbanken, © Till Hänisch 2000
Ergebnis:
mysql> source emptrig.sql
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> update emp set sal = 1000 where empno=7369;
Query OK, 1 row affected (0.37 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from emp;
+-------+--------+-----------+------+------------+------+------+--------+
| empno | ename | job
| mgr | hiredate
| sal | comm | deptno |
+-------+--------+-----------+------+------------+------+------+--------+
| 7369 | smith | clerk
| 7902 | 1980-12-17 | 1000 | NULL |
20 |
mysql> select * from emp_history;
+---------------------+------+------+-----------------+
| wann
| von | nach | wer
|
+---------------------+------+------+-----------------+
| 2011-11-27 16:45:21 | 5000 | 1000 | root@localhost |
+---------------------+------+------+-----------------+
3 rows in set (0.00 sec)
IS: Datenbanken, © Till Hänisch 2000
Trigger: Konsistenz
 
 
Manchmal ist Integrität komplexer, als durch
constraints abbildbar, z.B:
depts, die nur inaktive Mitarbeiter haben, dürfen
gelöscht werden.
mysql> select * from emp order by deptno;
+-------+--------+-----------+------+------------+------+------+--------+--------+
| empno | ename | job
| mgr | hiredate
| sal | comm | deptno | active |
+-------+--------+-----------+------+------------+------+------+--------+--------+
| 7934 | miller | clerk
| 7782 | 1982-01-23 | 1300 | NULL |
10 | N
|
| 7839 | king
| president | NULL | 1981-11-17 | 5000 | NULL |
10 | N
|
| 7782 | clark | manager
| 7839 | 1981-06-09 | 2450 | NULL |
10 | N
|
| 7902 | ford
| analyst
| 7566 | 1981-12-03 | 3000 | NULL |
20 | J
|
| 7876 | adams | clerk
| 7788 | 1983-01-12 | 1100 | NULL |
20 | J
|
| 7788 | scott | analyst
| 7566 | 1982-12-09 | 3000 | NULL |
20 | J
|
| 7566 | jones | manager
| 7839 | 1981-04-02 | 2975 | NULL |
20 | J
|
| 7369 | smith | clerk
| 7902 | 1980-12-17 | 1000 | NULL |
20 | J
|
| 7698 | blake | manager
| 7839 | 1981-05-01 | 2850 | NULL |
30 | J
|
IS: Datenbanken, © Till Hänisch 2000
Trigger: Konsistenz
delimiter '$$';
drop trigger if exists t_dept$$
create trigger t_dept before delete on dept for each row
begin
declare l_numemps int;
declare msg varchar(255);
select count(*) into l_numemps from emp
where deptno = old.deptno and active = 'J';
if (l_numemps > 0) then
set msg = concat('Unable to delete dept ',old.deptno);
signal sqlstate '45000' set MESSAGE_TEXT=msg;
end if;
end$$
mysql> delete from dept where deptno=20;
ERROR 1644 (45000): Unable to delete dept 20
mysql> delete from dept where deptno=10;
ERROR 1451 (23000): Cannot delete
or ©
update
a parent row: a foreign key constrain
IS: Datenbanken,
Till Hänisch 2000
m
Beispiel: Anwendungslogik
CREATE OR REPLACE TRIGGER TUpdAuftrag
BEFORE UPDATE ON Auftrag FOR EACH ROW
BEGIN
IF ((:old.Status = Globvar.Stat_Auftrag_abgerechnet)
AND (USER <> '&1')) THEN
Error.raise_error(Error.en_Abgerechnet);
END IF;
END;
CREATE OR REPLACE TRIGGER TDelAuftrag
BEFORE DELETE ON Auftrag FOR EACH ROW
BEGIN
IF (:old.Status = Globvar.Stat_Auftrag_abgerechnet) THEN
Error.raise_error(Error.en_Abgerechnet);
IS: Datenbanken, © Till Hänisch 2000
END IF;
END;
Aktive Datenbanken
 
 
„normale“ Datenbanken speichern
Daten
Aktive Datenbanken führen Aktionen
aus (ECA-Modell)
 
Event
 
 
Condition
 
 
Z.B. Änderung von Daten, Zeitpunkt,...
Bedingung, unter der Action ausgeführt wird
Action
IS: Datenbanken, © Till Hänisch 2000
Anwendungen von Triggern
 
 
 
 
 
Denormalisierte Relationen
Protokollierung
Replikation
Materialized views
Einfache Integritätsbedingungen durch
constraints, komplexere ?
 
 
Business rules
Z.B. „Fakturierte Aufträge dürfen nicht
geändert werden“
IS: Datenbanken, © Till Hänisch 2000
Events bei mysql
 
 
 
Events werden regelmäßig (cron)
ausgelöst.
Wartungsarbeiten
Statistik
set GLOBAL event_scheduler=ON;
create EVENT fill_t ON SCHEDULE
EVERY 1 minute DO
insert into t values(77,42);
IS: Datenbanken, © Till Hänisch 2000
Performance
SQL> describe lagerbewegung
Name
----------------------------------------NUMMER
ART
VORGANGSNUMMER
BESTELLNUMMER
TEILENUMMER
MENGE
LAGERORT
DATUM
Null?
-------NOT NULL
NOT NULL
NOT
NOT
NOT
NOT
SQL> select count(*) from lagerbewegung;
COUNT(*)
---------159804
IS: Datenbanken, © Till Hänisch 2000
NULL
NULL
NULL
NULL
Type
----------------NUMBER
NUMBER(2)
VARCHAR2(10)
NUMBER
VARCHAR2(15)
NUMBER(10,3)
VARCHAR2(12)
DATE
Szenario
 
 
 
 
Lesen aller Records aus einer Tabelle
und schreiben in andere, zunächst intern
durch DB
Dann verschiedene Programmiertechniken
Idee: Bearbeitung der Records nötig, z.B.
ergänzen von Werten ...
In Programmen commit alle 1000 records,
sonst ziemlich langsam ....
SQL> create table fastest as select * from lagerbewegung;
Table created.
Elapsed: 00:00:03.91
IS: Datenbanken, © Till Hänisch 2000
Stored procedure
create or replace procedure perf_proc as
cursor c is select * from lagerbewegung;
c_rec c%rowtype;
i integer;
begin
open c;
i := 0;
fetch c into c_rec;
while c%found loop
insert into perf(nummer,art,vorgangsnummer,...)
values (c_rec.nummer,c_rec.art...);
i := i + 1;
if ((i mod 1000) = 0) then
commit;
end if;
fetch c into c_rec;
end loop;
close c;
SQL> execute perf_proc
end;
PL/SQL procedure successfully completed.
/
Elapsed: 00:01:56.90
IS: Datenbanken, © Till Hänisch 2000
Java, dynamisches SQL
ResultSet rset = stmt.executeQuery ("select nummer, ...
where rownum < 10000"); // Zunächst nur 10.000 records ....
while (rset.next ()) {
sql = "insert into perf(nummer,art...)"
+ “values (" + rset.getInt(1) + "," + ...)";
ins.execute(sql);
NumberOfRecords++;
if ((NumberOfRecords % CommitInterval) == 0)
conn.commit();
}
oracle$ java Perftest
elapsed time: 38.146 seconds für 10.000 records,
Entspricht insgesamt (extrapoliert) ca. 11 Minuten
IS: Datenbanken, © Till Hänisch 2000
Prepared statement
PreparedStatement ps = conn.prepareStatement("insert into perf(...,
values (?,?,?,?,?,?,?,to_date(?,'yyyy-mm-dd hh24:mi:ss'))");
ResultSet rset = stmt.executeQuery ("select nummer ... ");
while (rset.next ()) {
ps.setInt(1,rset.getInt(1));
...
ps.execute();
NumberOfRecords++;
if ((NumberOfRecords % CommitInterval) == 0)
conn.commit();
}
oracle$ java PerftestPrepared
elapsed time: 17.573 seconds für 10.000 records,
Entspricht insgesamt (extrapoliert) ca. 5 Minuten !
IS: Datenbanken, © Till Hänisch 2000
Vergleich
Technik
Dauer
Table Copy ohne
Transaktion
Stored procedure
4 Sekunden
Java mit
dynamischem
Statement
Java mit prepared
statement
11 Minuten
2 Minuten
5 Minuten
IS: Datenbanken, © Till Hänisch 2000
Besser ... (DB-spezifisch)
create or replace procedure perf_bulk as
type recs is table of lagerbewegung%rowtype;
data recs;
cursor c is select * from lagerbewegung;
i integer;
begin
open c;
loop
fetch c bulk collect into data limit 10000;
forall i in 1..data.count
insert into perf values data(i);
commit;
exit when c%notfound;
end loop;
close c;
end;
SQL> execute perf_bulk
PL/SQL procedure successfully completed.
Elapsed: 00:00:13.36
IS: Datenbanken, © Till Hänisch 2000
NB: Commit-Frequenz
Commit alle n records
Dauer (sec)
1
1132
10
384
100
285
1000
280
10000
275
Eine Transaktion
259
(*)
(*): möglicherweise phys. Speicher zu klein
IS: Datenbanken, © Till Hänisch 2000
Herunterladen