SQL-miljö Standard SQL * Interaktiv * Embedded statisk dynamisk moduler CLI (Call Level Interface) JDBC, SQLJ Java Interface Standard embedded statisk SQL Ett programmeringsspråk (värdspråk) använder sig av SQL för att kommunicera med en relationsadatabas. Värdspråket kan vara: • ADA •C • COBOL • FORTRAN • PASCAL • PL/1 Pascal-program med ’inbakad’ SQL Preprocessor Förkompilering Ev. felmeddelanden Preparerat Pascalprogram Pascalkompilator Kompilering Exekverbart program Ev. felmeddelanden Pascal-program EXEC SQL ; EXEC SQL ; EXEC SQL ; SQL-satser När SQL kommunicerar med ett annat språk, måste följande problem beaktas: 1 Dataöverföring mellan programvariabler i SQL och värdspråket 2 Felhantering 3 Bearbetning av en tuppel i taget Exempeldatabas Department(dnumber, dname, dmgr) Employee(enumber, ename, dnumber) Project(pnumber, pname, dnumber) Works(enumber, pnumber, hours) Department.dmgr << Employeer.enumber Employee.dnumber << Department.dnumber Project.dnumber << Department.dnumber Works.enumber << Employee.enumber Works.pnumber << Project.pnumber 1 Dataöverföring Berörda variabler deklareras i en särskild sektion i värdspråket Ex. EXEC SQL BEGIN DECLARE SECTION; TYPE str4=PACKED ARRAY[1..4] OF CHAR; str20 = PACKED ARRAY[1..20] OF CHAR; emptyp = RECORD enumber:str4; enamn:str20; dnumber:str4 END; VAR emprecord:emptyp; EXEC SQL END DECLARE SECTION; 2 Felhantering SQLCA (SQL Communication Area) Innehåller ett antal variabler för meddelanden och statusinformation till värdspråket. Varje databasanrop påverkar SQLCA. Skall ej deklareras! Införlivas i programmet med: EXEC SQL INCLUDE SQLCA; SQLCA innehåller 128 tecken Pos 1 - 12 13 - 16 17 - 88 89 - 112 113 - 120 121 - 128 okänt SQLCODE SQLERRM okänt SQLWARN okänt SQLCODE = 0 <0 >0 +100 OK Fel OK med reservation Data saknas Felhantering med hjälp av SQLCA 1 1 WHENEVER-sats 2 Test av SQLCODE EXEC SQL WHENEVER <villkor> <åtgärd>; Villkor: - SQLERROR - SQLWARNING - SQLMESSAGE - NOT FOUND Ex. Åtgärd: - CONTINUE - STOP - GO TO <satsnummer> EXEC SQL WHENEVER SQLWARNING CONTINUE; 2 Test av SQLCODE Ex. - IF SQLCODE < 0 THEN GO TO . . . - WHILE SQLCODE = 0 DO . . . - IF SQLCODE = NOT FOUND . . . 3 Bearbetning Databasen öppnas respektive stängs med: EXEC SQL CONNECT <databasnamn>; ... ... EXEC SQL DISCONNECT; Överföring av en enstaka rad från en tabell (Singleton Select) Ex. EXEC SQL SELECT enumber, enamn, dnumber INTO :enr, :ename, :dnumber FROM Employee WHERE enumber = ’4410’; alternativt. EXEC SQL SELECT * INTO :emprecord FROM Employee WHERE enumber = ’4410’; På samma sätt kan man göra INSERT, UPDATE eller DELETE av enstaka rader. Det går även att uppdatera flera rader med UPDATE eller DELETE Ex. EXEC SQL UPDATE Works SET hours = NULL WHERE pnumber = ’1008’; Ex. EXEC SQL DELETE FROM Project; Överföring av mer än en rad löses med cursor, en markör som pekar ut och behandlar en rad i taget. Ex. Hämta alla projekt och skriv ut nummer, namn och totalt antal timmar för varje projekt. I deklarationsdelen av Pascalprogrammet skrivs: EXEC SQL INCLUDE SQLCA; Pascalvariabler som har kontakt med SQLvariabler deklareras i DECLARE SECTION. EXEC SQL BEGIN DECLARE SECTION; VAR pno:PACKED ARRAY[1..4] OF CHAR; project:PACKED ARRAY[1..20] OF CHAR; hours:SMALLINT; EXEC SQL END DECLARE SECTION; Ex forts. Begin EXEC SQL DECLARE ptime CURSOR FOR SELECT pnumber, pname, SUM(hours) FROM Project, Works WHERE Project.pnumber = Works.pnumber GROUP BY Project.pnumber, pname; ... EXEC SQL OPEN ptime; ... EXEC SQL FETCH ptime INTO :pno, :project, :hours; While SQLCODE = 0 Do Begin Writeln(pno, project, hours:5); EXEC SQL FETCH ptime INTO :pno, :project, :hours; End; ... EXEC SQL CLOSE ptime; ... End. Vid uppdatering med cursor lägger man till FOR UPDATE i cursor-deklarationen Ex. EXEC SQL DECLARE w CURSOR FOR SELECT enumber, pnumber, hours FROM Works WHERE pnumber = : pnum FOR UDATE OF hours; Exempel med två cursor Hämta nummer och namn på varje avdelning samt nummer och namn på de anställda inom varje avdelning och skriv ut dessa uppgifter. Begin … EXEC SQL DECLARE d CURSOR FOR SELECT dnumber, dname FROM Department; EXEC SQL DECLARE e CURSOR FOR SELECT enumber, enam FROM Employee WHERE dnumber = :depno; EXEC SQL WHENEVER SQLERROR GO TO 99; EXEC SQL CONNECT ”company”; EXEC SQL OPEN d; ... EXEC SQL FETCH d INTO :depno, : depname; While SQLCODE = 0 Do forts. Ex forts. Begin Writeln(”Department: ”, depno, ” ”, depname); Writeln(”Empno”, ” Empname”; EXEC SQL OPEN e; EXEC SQL FETCH e INTO :eno, :ename; While SQLCODE = 0 Do Begin Writeln(eno, ” ”, ename); EXEC SQL FETCH e INTO :eno, :ename; End; EXEC SQL CLOSE e; Writeln; EXEC SQL FETCH d INTO :depno, depname; End; ... EXEC SQL CLOSE d; EXEC SQL DISCONNECT; ... End. Embedded dynamisk SQL Tre steg: • Generera SQL-sats • Kompilera (preparera) • Exekvera } ev. i ett steg Ex. ... VAR inmatning : CHAR(1000); ... inmatning := ’DELETE FROM Employee WHERE enumber = ’0025’; EXEC SQL PREPARE SQLPREPPED FROM :inmatning; EXEC SQL EXECUTE SQLPREPPED; ... Alternativt ex. ... VAR inmatning : Char(1000); ... Writeln(’Ange din SQL-sats: ’); Readln(inmatning); EXEC SQL EXECUTE IMMEDIATE :inmatning; If SQLCODE = 0 Then Writeln (’Rätt’) Else Writeln (’Fel’); ... Placeholders Ex. ... Inmatning:= ’SELECT enumber FROM Works WHERE hours < ?’; EXEC SQL PREPARE SQLPREPPED FROM :inmatning; ... hmin:= 5; EXEC SQL EXECUTE SQLPREPPED USING :hmin; ... SQLDA En datastruktur som används för att transportera data mellan applikationsprogrammet och databasen. SQLDA innehåller en räknare och ett antal variabler med information om bl a kolumner, datatyper och värden. SQLDA är inte officiell standard, men stöds av de flesta SQL-produkter. Jämförelse mellan statisk och dynamisk SQL Embedded statisk SQL + Färdigkompilerad programkod med optimerad accessväg ger snabb exekvering - SQL-satserna måste vara definierade från början Embedded dynamisk SQL + Mer flexibel än statisk SQL Databasobjekten behöver inte existera före exekveringen - Tar vanligtvis längre tid att exekvera, eftersom frågorna optimeras vid körningen Moduler En modul är ett separatkomplierat program som innehåller en eller flera procedurer. Varje procedur innehåller en enda SQL-sats. Ex. MODULE projectmodule; LANGUAGE Pascal; AUTHORIZATION Stina; PROCEDURE find_project (SQLCODE, :pno Character(4), :pname Character(20)) SELECT pname INTO :pname FROM Project WHERE pnumber = :pno; ... PROCEDURE del_project ...; PROCEDURE new_project ...; Ex. forts. I Pascal ... VAR number, pno: Packed Array[1..4] Of Char; pname : Packed Array[1..20] Of Char; returncode: Integer; ... Begin ... Write(’Ange projektnummer: ’); Readln(number); pno := number; Call find_project(returncode, pno, pname); If returncode = 0 Then Writeln(’Projektets namn är: ’, pname) Else . . . ... End. Call Level Interface Ett applikationsprogram använder sig av CLI-rutiner för att genomföra SQL-operationer. Vilka rutiner som ska användas bestäms dynamiskt. CLI innefattar en mängd rutiner (routins). En rutin är antingen en funktion eller en procedur. Exempel på rutiner: Cancel CloseCursor Connect Disconnect Execute Fetch GetCursorName GetConnectAttrr GetTypeInfo Prepare PutData SetCursorName CLI Descriptor Areas Har samma funktion som SCLDA i dynamisk SQL. Skillnad: SQLDA innehåller aktuella datavärden CLIDA innehåller pekare till variabler som innehåller sådana värden Många anser att CLI utgör ett ”renare” gränssnitt mot SQL. Följande är hämtat ur The SQL Standard av C J Date / H Darwen, 1997: ”CLI actually represents a better approach to the problem than dynamic SQL does. For this reason it is probably true to say that dynamic SQL would never have been included in the standard at all if CLI had been defined first.” Embedded SQL med Java DB2JDBC hanterar dynamisk SQL ingen förkompilering, API calls DB2SQLJ hanterar statisk SQL förkompilering med SQLJ Translator JDBC i botten för koppling till databasen och felhantering SQLJ Program SQLJ Routine Classes JDBC Interface DB2 Database Öppna databasen jmf EXEC SQL CONNECT ’company’; Connection con = null; String dbas = ”jdbc:db2:company”; DefaultContext ctx = DefaultContext.getDefaultContext(); If (ctx == null) { try { con = DriverManager.getConnection(dbas); con.setAutoCommit(false); ctx = new DefaultContext(con); } catch (SQLException e) { System.out.println (e); System.exit (1); } DefaultContext.setDefaultContext (ctx); } Singleton select Ex. #sql {DELETE FROM project}; Ex. string ename = null; #sql {SELECT ename INTO :ename FROM Employee WHERE enumber = ”4410”}; System.out.println(”Namn = ” + ename); Cursor Ex. (= ex. i standard emb. SQL) #sql iterator ptime (string pnumber, string pname, int hours); ... ptime pt; (jmf EXEC SQL OPEN ptime;) string str1 = null; string str2 = null; int i3 = 0; #sql pt = {SELECT pnumber, pname, SUM(hours) FROM project, Works WHERE Project.pnumber = Works.pnumber GROUP BY Project.pnumber, pname}; While (pt.next()) { str1 = pt.pnumber; str2 = pt.pname; i3 = pt.hours; System.out.println(str1 + str2 + i3); System.out.println(); } pt.close (); (jmf EXEC SQL CLOSE ptime;) Samma som föregående bild men med FETCH Ex. ... #sql iterator ptime (string pnumber, string pname, int hours); ... ptime pt; string str1 = null; string str2 = null; int i3 = 0; #sql pt = {SELECT pnumber, pname, SUM(hours) FROM project, Works WHERE Project.pnumber = Works.pnumber GROUP BY Project.pnumber, pname}; #sql {FETCH :pt INTO :str1, str2, i3} While (pt.endFetch()){ System.out.println(str1 + str2 + i3); #sql {FETCH :pt INTO :str1, :str2, :i3}; } ...