Matthew P. Johnson, OCL1, CISDD CUNY, F20041 OCL1 Oracle 10g: SQL & PL/SQL Session #6 Matthew P. Johnson CISDD, CUNY Fall, 2004
Matthew P. Johnson, OCL1, CISDD CUNY, F Agenda Last time: Views, Constraints This time: Begin programming for SQL Embedded SQL Pro*C, SQLJ CLI SQL/CLI in C JDBC in Java DBI/DBDs in Perl PSMs Future: PL/SQL, Triggers
Matthew P. Johnson, OCL1, CISDD CUNY, F Today’s agenda 1. Go through Join exercises 2. Discuss Pro*C 3. First part of lab 4. Break 5. Discuss CLI, Embedded SQL, JDBC 6. Second part of lab
Matthew P. Johnson, OCL1, CISDD CUNY, F New topic: SQL Programming Can write SQL queries in a SQL interpreter Command prompt SQL*Plus (sqlplus) in Oracle mysql in MySQL Good for experimenting, not for anything non-trivial Better: use a standard programming language Host language talks to SQL/DB
Matthew P. Johnson, OCL1, CISDD CUNY, F Using two languages Q: Why not use just one language? “[W]e can quickly dispense with the idea…” (Ullman, p351) Q: Why not do everything in the host lang.? A: What Oracle provides is highly non-trivial Query interpretation, optimizing Queries stay constant across host languages Q: Why not do everything in SQL? A: Not designed as a general-purpose language No recursion (no factorial!), not Turing-complete No, e.g., Swing library Germ of OO: modularize
Matthew P. Johnson, OCL1, CISDD CUNY, F Impedance mismatch problem Big problem, though: impedance mismatch Data structures in our app-programming lang. don’t automatically map onto those in SQL Different types/representations for data In SQL: tables with scalar fields In C: scalars, records (containing records…), pointers, arrays In Java: scalars, objects, references, arrays In Perl: scalars, lists/arrays, hashes/assoc.
Matthew P. Johnson, OCL1, CISDD CUNY, F SQL/host interface in embedded SQL So Q: how to transfer data between? A: Shared variables Some vars in the program can be used by SQL Prefix var with a : After query, look here for received data SQL commands embedded in app. code Identified by EXEC SQL Source code is preprocessed before regular compilation Result is (e.g.) a C program with library calls
Matthew P. Johnson, OCL1, CISDD CUNY, F Programs with Embedded SQL Host language + Embedded SQL Preprocessor Host Language + function calls Host language compiler Executable Preprocessor Host language compiler Oracle’s Pro*C gcc prog.pc prog.c a.out
Matthew P. Johnson, OCL1, CISDD CUNY, F Interface: SQL / Host Language Values get passed through shared variables. Colons precede shared variables in SQL statements EXEC SQL demarcates every SQL statement The variable SQLSTATE provides error messages and status reports “00000” ~ success “02000” ~ tuple not found Used in loops EXEC SQL BEGIN DECLARE SECTION; char productName[30]; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQL BEGIN DECLARE SECTION; char productName[30]; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION;
Matthew P. Johnson, OCL1, CISDD CUNY, F Embedded SQL example Context: Product (pname, price, quantity, maker) Purchase (buyer, seller, store, pname) Company (cname, city) Person(name, phone, city) Goal 1: Insert a new row in Purchase Goal 2: Look up price of product by name
Matthew P. Johnson, OCL1, CISDD CUNY, F Embedded SQL example: insert void simpleInsert() { EXEC SQL BEGIN DECLARE SECTION; char pn[20], cn[30]; /* product-name, company-name */ double p int q; /* price, quantity */ char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; /* get values for name, price and company somehow */ EXEC SQL INSERT INTO Product(pname, price, quantity, maker) VALUES (:pn, :p, :q, :cn); } void simpleInsert() { EXEC SQL BEGIN DECLARE SECTION; char pn[20], cn[30]; /* product-name, company-name */ double p int q; /* price, quantity */ char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; /* get values for name, price and company somehow */ EXEC SQL INSERT INTO Product(pname, price, quantity, maker) VALUES (:pn, :p, :q, :cn); }
Matthew P. Johnson, OCL1, CISDD CUNY, F Embedded SQL example: look-up int getWindowsPrice() { EXEC SQL BEGIN DECLARE SECTION; double p; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQLSELECT price INTO :p FROM Product WHERE Product.name = ‘Windows’; return p; } int getWindowsPrice() { EXEC SQL BEGIN DECLARE SECTION; double p; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQLSELECT price INTO :p FROM Product WHERE Product.name = ‘Windows’; return p; }
Matthew P. Johnson, OCL1, CISDD CUNY, F Embedded SQL example: look-up What about search for arbitrary product? Q: Will this work? int getPrice(char *name) { EXEC SQL BEGIN DECLARE SECTION; int p; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQLSELECT price INTO :p FROM Product WHERE Product.name = :name; return p; } int getPrice(char *name) { EXEC SQL BEGIN DECLARE SECTION; int p; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQLSELECT price INTO :p FROM Product WHERE Product.name = :name; return p; }
Matthew P. Johnson, OCL1, CISDD CUNY, F Embedded SQL example: look-up int getPrice(char *name) { EXEC SQL BEGIN DECLARE SECTION; char n[20]; int p; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; strcpy(n, name); /* copy name to local var */ EXEC SQLSELECT price INTO :p FROM Product WHERE Product.name = :n; return p; } int getPrice(char *name) { EXEC SQL BEGIN DECLARE SECTION; char n[20]; int p; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; strcpy(n, name); /* copy name to local var */ EXEC SQLSELECT price INTO :p FROM Product WHERE Product.name = :n; return p; }
Matthew P. Johnson, OCL1, CISDD CUNY, F Cursors For product’s price, looked up single (scalar) value Q: What if we SELECT multiple fields? E.g., find all info for some product A: Just list destination vars separated by commas Q: What if find multiple rows? E.g., find all products above a certain price Use a cursor to step through the results Each result placed in an array Using cursors: 1. Declare the cursor 2. Open the cursor 3. Fetch tuples one by one 4. Close the cursor
Matthew P. Johnson, OCL1, CISDD CUNY, F Cursor loop structure Each time around loop, we Do a FETCH to obtain next row Examine SQLSTATE to check success Can say: What is NO_MORE_TUPLES? #define NO_MORE_TUPLES !(strcmp(SQLSTATE,”02000”)) if(NO_MORE_TUPLES) break;
Matthew P. Johnson, OCL1, CISDD CUNY, F Multiple-row look-up example void productToXML() { EXEC SQL BEGIN DECLARE SECTION; char pn[20], cn[30]; double p; int q; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE crs CURSOR FOR SELECT pname, price, quantity, maker FROM Product; EXEC SQL OPEN crs;... void productToXML() { EXEC SQL BEGIN DECLARE SECTION; char pn[20], cn[30]; double p; int q; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE crs CURSOR FOR SELECT pname, price, quantity, maker FROM Product; EXEC SQL OPEN crs;...
Matthew P. Johnson, OCL1, CISDD CUNY, F Multiple look-up example printf(“ \n”); while (1) { EXEC SQL FETCH FROM crs INTO :n, :p, :q,:c; if (NO_MORE_TUPLES) break; printf(“ \n”); printf(“ %s \n”, n); printf(“ %d \n”, p); printf(“ %d \n”, q); printf(“ %s \n”, c); printf(“ \n”); } EXECT SQL CLOSE crs; printf(“ \n”); } printf(“ \n”); while (1) { EXEC SQL FETCH FROM crs INTO :n, :p, :q,:c; if (NO_MORE_TUPLES) break; printf(“ \n”); printf(“ %s \n”, n); printf(“ %d \n”, p); printf(“ %d \n”, q); printf(“ %s \n”, c); printf(“ \n”); } EXECT SQL CLOSE crs; printf(“ \n”); }
Matthew P. Johnson, OCL1, CISDD CUNY, F More on Cursors Cursors can traverse both stored tables and queries Cursors can modify a relation as well as read it Cursors can be protected against changes to the underlying relations Can determine the order in which the cursor will get tuples by the ORDER BY keyword in the SQL query The cursor can be a scrolling one: can go forward, backward +n, -n, Abs(n), Abs(-n)
Matthew P. Johnson, OCL1, CISDD CUNY, F Cursor on query not table EXEC SQL DECLARE c CURSOR FOR SELECT beer, price FROM Sells WHERE bar = ‘Izzy''s'; EXEC SQL OPEN CURSOR c; while(1) { EXEC SQL FETCH c INTO :theBeer, :thePrice; if (NOT FOUND) break; /* format and print beer and price */ } EXEC SQL CLOSE CURSOR c; EXEC SQL DECLARE c CURSOR FOR SELECT beer, price FROM Sells WHERE bar = ‘Izzy''s'; EXEC SQL OPEN CURSOR c; while(1) { EXEC SQL FETCH c INTO :theBeer, :thePrice; if (NOT FOUND) break; /* format and print beer and price */ } EXEC SQL CLOSE CURSOR c;
Matthew P. Johnson, OCL1, CISDD CUNY, F Modifications with cursors As we traverse through result set, can modify the current row Can also modify with arb. WHERE clauses NB: In regular SQL, usually modify sets of rows (UPDATE WHERE …) With cursors, we update the last row fetched Simple example: in RentStab table, we decide we want to raise (e.g., by 5%) all our prices Unless price > 2000, in which case they’re deleted
Matthew P. Johnson, OCL1, CISDD CUNY, F Modification by cursor example void raisePrices() { EXEC SQL BEGIN DECLARE SECTION; double p; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE crs CURSOR FOR SELECT price FROM RentStab; EXEC SQL OPEN crs;... void raisePrices() { EXEC SQL BEGIN DECLARE SECTION; double p; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE crs CURSOR FOR SELECT price FROM RentStab; EXEC SQL OPEN crs;...
Matthew P. Johnson, OCL1, CISDD CUNY, F Modification by cursor example while (1) { EXEC SQL FETCH FROM crs INTO :p; if (NO_MORE_TUPLES) break; if (p < 2000) EXEC SQL UPDATE RentStab SET price = 1.05*price; WHERE CURRENT OF RentStab; else EXEC SQL DELETE FROM RentStab WHERE CURRENT OF RentStab; } EXECT SQL CLOSE crs; } while (1) { EXEC SQL FETCH FROM crs INTO :p; if (NO_MORE_TUPLES) break; if (p < 2000) EXEC SQL UPDATE RentStab SET price = 1.05*price; WHERE CURRENT OF RentStab; else EXEC SQL DELETE FROM RentStab WHERE CURRENT OF RentStab; } EXECT SQL CLOSE crs; }
Matthew P. Johnson, OCL1, CISDD CUNY, F A mention of concurrent access What if the DB changes while our cursor is looping? I.e., after we opened the cursor, but while we’re fetching Should we see the changes? Maybe, maybe not make these changes invisible by declaring insensitive Q: How can this be implemented? One crude way: delay any changes until all open insensitive cursors close Good idea: indicate read-only cursors so they won’t be held up: EXEC SQL DECLARE crs INSENSITIVE CURSOR FOR SELECT price FROM Product; EXEC SQL DECLARE crs INSENSITIVE CURSOR FOR SELECT price FROM Product; EXEC SQL DECLARE crs CURSOR FOR SELECT price FROM Product; FOR READ ONLY; EXEC SQL DECLARE crs CURSOR FOR SELECT price FROM Product; FOR READ ONLY;
Matthew P. Johnson, OCL1, CISDD CUNY, F Scrolling cursors Usually cursor just walks through rows 1 by 1 Other options: NEXT (default) or PREVIOUS FIRST or LAST RELATIVE +/-n RELATIVE 1 ~ NEXT RELATIVE –1 ~ ? ABSOLUTE +/-n ABSOLUTE 1 ~ FIRST (not 0!) ABSOLUTE –1 ~ LAST To use these, declare as SCROLL cursor EXEC SQL DECLARE crs SCROLL CURSOR FOR Product; EXEC SQL DECLARE crs SCROLL CURSOR FOR Product;
Matthew P. Johnson, OCL1, CISDD CUNY, F Pro*C e.g. Example script: sample1.pc See Pro*C tutorialPro*C tutorial Pro*C compiler is proc Must include /oracle/precomp/public Must link with shared library /oracle/lib/libclntsh.so Includes makefile proc.mk, but may require modifications sales% cp /oracle/precomp/demo/proc/sample1.pc. sales% proc sample1.pc sales% gcc -osample1 -I/oracle/precomp/public /oracle/lib/libclntsh.so sample1.c sales% sample1 sales% cp /oracle/precomp/demo/proc/sample1.pc. sales% proc sample1.pc sales% gcc -osample1 -I/oracle/precomp/public /oracle/lib/libclntsh.so sample1.c sales% sample1
Matthew P. Johnson, OCL1, CISDD CUNY, F Interface: SQL/Host Language Two languages: SQL, host (C/Java/whatever) Benefits: DB code (SQL is portable) SQL, host language focus on own strengths SQL code placed in host language code SQL and host language have diff. data types “impedance mismatch” Data transferred with “shared variables” Use cursors to access/modify data Error messages placed in SQLSTATE
Matthew P. Johnson, OCL1, CISDD CUNY, F Agenda Previously: Pro*C Next: Project part 3 is due next week More programming for SQL Embedded SQL Pro*C, SQLJ CLI SQL/CLI in C JDBC in Java DBI/DBDs in Perl PHP (HTML?) SPs
Matthew P. Johnson, OCL1, CISDD CUNY, F Recap: Embedded SQL Host language + Embedded SQL Preprocessor Host Language + function calls Host language compiler Executable Preprocessor Host language compiler Oracle’s Pro*C gcc prog.pc prog.c a.out
Matthew P. Johnson, OCL1, CISDD CUNY, F Limitation of embedded SQL Okay for apps with a fixed set of queries/updates Maybe very simple kiosks But consider, say, sqlplus or the sqlzoo website Processes arbitrary queries from user Can we do this with embedded SQL?
Matthew P. Johnson, OCL1, CISDD CUNY, F Dynamic SQL In dynamic SQL, query string can be taken as a parameter, passed to DB Two steps: Prepare: compiles/optimizes the string Execute: executes the query Combine together: EXECUTE IMMEDIATE But separate if query is executed many times (why?)
Matthew P. Johnson, OCL1, CISDD CUNY, F Dynamic SQL myquery = a SQL variable not prefixed by : void runQuery() { EXEC SQL BEGIN DECLARE SECTION; char *command; EXEC SQL END DECLARE SECTION; /* command set to some query string */ EXEC SQL PREPARE myquery FROM :command; EXEC SQL EXECUTE myquery; /* or just: */ EXEC SQL EXECUTE IMMEDIATE myquery; } void runQuery() { EXEC SQL BEGIN DECLARE SECTION; char *command; EXEC SQL END DECLARE SECTION; /* command set to some query string */ EXEC SQL PREPARE myquery FROM :command; EXEC SQL EXECUTE myquery; /* or just: */ EXEC SQL EXECUTE IMMEDIATE myquery; }
Matthew P. Johnson, OCL1, CISDD CUNY, F Dynamic SQL example Example script: sample8.pc See Pro*C tutorialPro*C tutorial Goal: find employees from department 10 Start with query as string sales% proc sample8.pc sales% gcc -I/oracle/precomp/public /oracle/lib/libclntsh.so –osample8 sample8.c sales% sample8 sales% proc sample8.pc sales% gcc -I/oracle/precomp/public /oracle/lib/libclntsh.so –osample8 sample8.c sales% sample8
Matthew P. Johnson, OCL1, CISDD CUNY, F Sketch of sqlplus Something like the sqlplus program could be written as a simple dynamic SQL future lab idea? EXEC SQL BEGIN DECLARE SECTION; char query[MAX QUERY LENGTH]; EXEC SQL END DECLARE SECTION; /* issue SQL> prompt */ /* read user's text into array query */ EXEC SQL EXECUTE IMMEDIATE :query; /* go back to reissue prompt */ EXEC SQL BEGIN DECLARE SECTION; char query[MAX QUERY LENGTH]; EXEC SQL END DECLARE SECTION; /* issue SQL> prompt */ /* read user's text into array query */ EXEC SQL EXECUTE IMMEDIATE :query; /* go back to reissue prompt */
Matthew P. Johnson, OCL1, CISDD CUNY, F Dynamic SQL example 2 Example script: sample10.pc See Pro*C tutorialPro*C tutorial Goal: recreate sqlplus Copy-able commands: sales% proc sample10.pc sales% gcc -I/oracle/precomp/public /oracle/lib/libclntsh.so –osample10 sample10.c sales% sample10 sales% proc sample10.pc sales% gcc -I/oracle/precomp/public /oracle/lib/libclntsh.so –osample10 sample10.c sales% sample10
Matthew P. Johnson, OCL1, CISDD CUNY, F Next topic: SQL/CLI Pro*C converts EXEC SQL code --into what? If we know the API (“Call-Level Interface”), can call library routines by hand Is this better or worse? Pros & cons Won’t cover in depth
Matthew P. Johnson, OCL1, CISDD CUNY, F CLI: Java Host language + Embedded SQL Preprocessor Host Language + function calls Host language compiler Executable Preprocessor Host language compiler Oracle’s Pro*C javac + jar prog.pc Prog.java Proj.class
Matthew P. Johnson, OCL1, CISDD CUNY, F CLI - Overview Similar to what really happens in embedded SQL implementations. Major approaches: SQL/CLI - standard of ODBC JDBC - Java database connectivity See for many optionshttp://cbbrowne.com/info/middleware.html Advantages over embedded SQL: Avoid preprocessor-stage, easier to debug In th., use same program with several DBMS Disadvantages: Must keep up to date with API changes DBMS may have conflicting APIs
Matthew P. Johnson, OCL1, CISDD CUNY, F Next topic: JDBC (Java’s CLI) As expected: Java too can talk to SQL In some ways: much nicer JDBC is an interface Changes very little Each vendor writes own plug-in Dev. Strategy: write to API, compile with jar See for 219 (!) JDBC drivershttp://servlet.java.sun.com/products/jdbc/drivers
Matthew P. Johnson, OCL1, CISDD CUNY, F JDBC 1. Load JDBC driver for DBMS: 2. Obtain a connection: Class.forName("oracle.jdbc.driver.OracleDriver") Connection con = DriverManager.getConnection( username, passwd); Connection con = DriverManager.getConnection( username, passwd);
Matthew P. Johnson, OCL1, CISDD CUNY, F JDBC 3. Obtain a statement object: 4. Run a query: Or an update: Statement stmt = con.createStatement(); stmt.executeQuery(“SELECT * FROM table”); stmt.executeUpdate(“INSERT INTO tables” + “VALUES(‘abc’, ‘def’)”); stmt.executeUpdate(“INSERT INTO tables” + “VALUES(‘abc’, ‘def’)”);
Matthew P. Johnson, OCL1, CISDD CUNY, F Prepared Statements in JDBC JDBC also supports prepared statements 3. Obtain a PreparedStatement object: 4. Now execute: PreparedStatement ps = con.createStatement( “SELECT * FROM table”); PreparedStatement ps = con.createStatement( “SELECT * FROM table”); ps.executeQuery();
Matthew P. Johnson, OCL1, CISDD CUNY, F Obtaining query results “Cursor” not used, but same idea executeQuery() returns a ResultSet: rs.next() advances to new row, returns false if EOF getInt(i) returns ith column (if an int!) from current row ResultSet rs = ps.executeQuery(); while (rs.next()) { String val1 = rs.getString(1); int val2 = rs.getInt(2); … } while (rs.next()) { String val1 = rs.getString(1); int val2 = rs.getInt(2); … }
Matthew P. Johnson, OCL1, CISDD CUNY, F Java/JDBC/Oracle example Example program: SQLRunner.java Goal: run the SQL query passed Copy-able commands: nstr.txt C:\proc>c:\j2sdk1.4.2_04\bin\javac SQLRunner.java C:\proc>c:\j2sdk1.4.2_04\bin\java -cp.;C:\OraHome_1\jdbc\lib\ojdbc14.jar SQLRunner "select * from emp" C:\proc>c:\j2sdk1.4.2_04\bin\javac SQLRunner.java C:\proc>c:\j2sdk1.4.2_04\bin\java -cp.;C:\OraHome_1\jdbc\lib\ojdbc14.jar SQLRunner "select * from emp"
Matthew P. Johnson, OCL1, CISDD CUNY, F Java/JDBC/MySQL example Example program: MySQLRunner.java Goal: run the SQL query passed (Nearly) the same as before! just using different DB/tables/login mysql.jar is the MySQL J/Connector jar C:\proc>c:\j2sdk1.4.2_04\bin\javac MySQLRunner.java C:\proc>c:\j2sdk1.4.2_04\bin\java –cp.;mysql.jar MySQLRunner "select * from emp" C:\proc>c:\j2sdk1.4.2_04\bin\javac MySQLRunner.java C:\proc>c:\j2sdk1.4.2_04\bin\java –cp.;mysql.jar MySQLRunner "select * from emp"
Matthew P. Johnson, OCL1, CISDD CUNY, F Recap: JDBC Host language + Embedded SQL Preprocessor Host Language + function calls Host language compiler Executable Preprocessor Host language compiler Oracle’s Pro*C javac + jar prog.pc Prog.java Proj.class
Matthew P. Johnson, OCL1, CISDD CUNY, F Java & parameter-based SQL Like SQL/CLI in C, Java also supports parameterized queries (why?) 1. Prepare structure of query 2. Then can set values PreparedStatement ps = conn.prepareStatement( "SELECT * FROM table WHERE f1 = ? and f2 = ?"); ps.setString(1 “abc"); ps.setString(2, “def"); ResultSet rs = ps.executeQuery();... PreparedStatement ps = conn.prepareStatement( "SELECT * FROM table WHERE f1 = ? and f2 = ?"); ps.setString(1 “abc"); ps.setString(2, “def"); ResultSet rs = ps.executeQuery();...
Matthew P. Johnson, OCL1, CISDD CUNY, F Also: ODBC Used by Microsoft platforms/tools, others Access: Start | Control Panel | Administrative Tools | Data Sources (ODBC) Similar to JDBC Won’t cover
Matthew P. Johnson, OCL1, CISDD CUNY, F Other combinations So far: C/Pro*C, Java/JDBC Q: Only choices? A: No “Call-level interface” for C: SQL/CLI ODBC Embedded Java: SQL/J CLI for Perl, PHP, etc. Stored Procedures (next) {langs} x {dyn/not} x {SPs/not} x {DBMSs}
Matthew P. Johnson, OCL1, CISDD CUNY, F Step back Recall basic problem: need SQL plus stronger programming lang need to connect the two langs In all these cases (and in the web app case), idea is: put SQL in (traditional-lang) programs Another way: put programs in SQL i.e., store programs on the DBMS “stored procedures”
Matthew P. Johnson, OCL1, CISDD CUNY, F Next topic: SPs “Persistent, Stored Modules” / “Stored Procedures / “PL/SQL programs” (in Oracle) Added to MySQL in 5.0 Another way to connect application programming language and SQL Supports usual things: Declare, set vars to vals of expressions Print output Define (optional) procedures, functions Cursors PL/SQL can compute n!
Matthew P. Johnson, OCL1, CISDD CUNY, F PL/SQL “Procedural Language/SQL” Oracle’s language for stored procedures Simple, interpreted, procedural language But Pascal-like: BEGIN END, not { } AND OR, not && || vars defined at top of procedre how return works
Matthew P. Johnson, OCL1, CISDD CUNY, F PL/SQL Generally speaking can be used wherever SQL can be sqlplus embeded SQL Can store programs in files (.sql), run later runs code in myprog.sql
Matthew P. Johnson, OCL1, CISDD CUNY, F Scripting languages Big problems v. small problems Big solutions v. small solutions Programming languages: C/C++, Java, etc. Scripting languages: PL/SQL, Perl, PHP, Unix shell, DOS batch files, Python, Excel macros, VBA, JavaScript Usual properties of scripting languages: Interpreted Don’t require functions/procedures Weakly typed
Matthew P. Johnson, OCL1, CISDD CUNY, F PL/SQL Structure of procedure body: As in Pascal, var declars precede body DECLARE --Optional --var declarations BEGIN --executable statements --queries/updates, etc. END; /--to execute DECLARE --Optional --var declarations BEGIN --executable statements --queries/updates, etc. END; /--to execute
Matthew P. Johnson, OCL1, CISDD CUNY, F PL/SQL: Hello, World SET SERVEROUTPUT ON; BEGIN -- print out message DBMS_OUTPUT.PUT_LINE('Hello World, from PL/SQL'); END; / SET SERVEROUTPUT ON; BEGIN -- print out message DBMS_OUTPUT.PUT_LINE('Hello World, from PL/SQL'); END; /
Matthew P. Johnson, OCL1, CISDD CUNY, F PL/SQL code examples One example: Likes(drinker, beverage) Another example: BEGIN INSERT INTO Likes VALUES(‘Izzy', ‘milk'); DELETE FROM Likes WHERE drinker = ‘Izzy' AND beverage = ‘Beaujolais Nouveau '; COMMIT; END; / BEGIN INSERT INTO Likes VALUES(‘Izzy', ‘milk'); DELETE FROM Likes WHERE drinker = ‘Izzy' AND beverage = ‘Beaujolais Nouveau '; COMMIT; END; /
Matthew P. Johnson, OCL1, CISDD CUNY, F Procedures Stored database objects that use a PL/SQL statement(s) in their body Create/drop similar to other SQL objects: ALTER PROCEDURE… in MySQL CREATE PROCEDURE ( ) ; CREATE PROCEDURE ( ) ; DROP PROCEDURE ; CREATE OR REPLACE PROCEDURE ( ) ; CREATE OR REPLACE PROCEDURE ( ) ;
Matthew P. Johnson, OCL1, CISDD CUNY, F Example procedure Define the procedure: Now we can call it: CREATE PROCEDURE testProcedure BEGIN INSERT INTO Student VALUES (5, ‘Joe’); COMMIT; END; CREATE PROCEDURE testProcedure BEGIN INSERT INTO Student VALUES (5, ‘Joe’); COMMIT; END; EXEC testProcedure;
Matthew P. Johnson, OCL1, CISDD CUNY, F More details on procedures Parameter list has name-mode-type triples: Modes: IN, OUT, or IN OUT Fulfills role similar to pass-by-value v. pass-by- reference Default is IN Types must match, so can get exact field type: relation.attribute%TYPE
Matthew P. Johnson, OCL1, CISDD CUNY, F Procedure example A procedure to take a beer and price and add it to Joe's menu: Sells(bar, beer, price) CREATE PROCEDURE joeMenu( b IN Sells.beer%TYPE, p IN Sells.price%TYPE) AS BEGIN INSERT INTO Sells VALUES('Joe''s Bar', b, p); END; / CREATE PROCEDURE joeMenu( b IN Sells.beer%TYPE, p IN Sells.price%TYPE) AS BEGIN INSERT INTO Sells VALUES('Joe''s Bar', b, p); END; /
Matthew P. Johnson, OCL1, CISDD CUNY, F Branching IF–THEN statements use THEN Must end with END IF Use ELSIF in place of ELSE IF Example: IF THEN ELSIF END IF; IF THEN ELSIF END IF;
Matthew P. Johnson, OCL1, CISDD CUNY, F Loop example DECLARE i NUMBER := 1; BEGIN LOOP INSERT INTO T1 VALUES(i,i); i := i+1; EXIT WHEN i>100; END LOOP; END; / DECLARE i NUMBER := 1; BEGIN LOOP INSERT INTO T1 VALUES(i,i); i := i+1; EXIT WHEN i>100; END LOOP; END; /
Matthew P. Johnson, OCL1, CISDD CUNY, F Cursors in PL/SQL As expected, PL/SQL has syntax to do the usual things: Declare cursors Open and close Fetch and eventually leave Each can be done manually Also has elegant for/cursor loop Declare, open, close, fetch all automatic: Example: FOR my-rec IN my-cursor LOOP … END LOOP; FOR my-rec IN my-cursor LOOP … END LOOP;
Matthew P. Johnson, OCL1, CISDD CUNY, F Functions Like procedures but with return values Big strength: can be called from SQL CREATE FUNCTION ( ) RETURNS type AS BEGIN END; CREATE FUNCTION ( ) RETURNS type AS BEGIN END; DROP FUNCTION ;
Matthew P. Johnson, OCL1, CISDD CUNY, F Function example Like procedures but with return values drop in same way Big strength: can be called from SQL CREATE OR REPLACE FUNCTION maxval(a IN int, b IN int) RETURN int AS BEGIN IF a > b THEN RETURN a; ELSE RETURN b; END IF; END maxval; / CREATE OR REPLACE FUNCTION maxval(a IN int, b IN int) RETURN int AS BEGIN IF a > b THEN RETURN a; ELSE RETURN b; END IF; END maxval; / INSERT INTO R VALUES(“abc”, maxval(5,10));
Matthew P. Johnson, OCL1, CISDD CUNY, F How to run scripts Don’t want to type ftns into sqlplus by hand Define them in a.sql file In sqlplus, execute.sql file Runs commands in file Here, defines function Now, we can call functions See SQL> exec DBMS_OUTPUT.PUT_LINE (maxval(5,10))
Matthew P. Johnson, OCL1, CISDD CUNY, F Triggers in Oracle Oracle triggers are written in PL/SQL Trigger body is like regular procedure body, but following trigger syntax: CREATE OR REPLACE TRIGGER MYTRIG1 BEFORE DELETE ON mytable BEGIN --code END; CREATE OR REPLACE TRIGGER MYTRIG1 BEFORE DELETE ON mytable BEGIN --code END;
Matthew P. Johnson, OCL1, CISDD CUNY, F Look up procedures, functions In Oracle, functions & procedures in user_procedures: SELECT object_name from user_procedures;
Matthew P. Johnson, OCL1, CISDD CUNY, F More on PL/SQL O’Reilly’s Oracle PL/SQL Programming: PL/SQL Reference & Tutorial: Introduction to PL/SQL: ml ml Oracle FAQ's Script and Code Exchange:
Matthew P. Johnson, OCL1, CISDD CUNY, F Triggers Constraints state what must remain true DBMS decides when to check Triggers are instructions to perform at explicitly specified times Three aspects: An event (e.g., update to an attribute) A condition (e.g., a query to check) An action (the trigger’s effect) (deletion, update, insertion) When the event occurs, DBMS checks the constraint, and if it is satisfied, performs the action
Matthew P. Johnson, OCL1, CISDD CUNY, F Triggers – important points Can replace old row (result of event) with new row Action may be performed before or after event Can refer to old row and new row WHEN clauses tests whether to continue Action may be performed either For each row involved in event Once per event Oracle does triggers as PL/SQL programs
Matthew P. Johnson, OCL1, CISDD CUNY, F Elements of Triggers Timing of action execution: before, after or instead of triggering event The action can refer to both the old and new state of the database Update events may specify a particular column or set of columns A condition is specified with an optional WHEN clause The action can be performed either for once for every tuple or once for all the tuples that are changed by the database operation
Matthew P. Johnson, OCL1, CISDD CUNY, F Simple trigger example R(id, data, last-modified) data is a large string Last-modified is a newly added date field Goal: whenever data is modified, update last- modified date Could modify all scripts/programs that touch this table Bad idea Better: user a trigger CREATE TRIGGER UpdateDateTrigger BEFORE UPDATE OF data ON R REFERENCING NEW ROW AS NewTuple FOR EACH STATEMENT BEGIN NewTuple.last-modified = sysdate; END; CREATE TRIGGER UpdateDateTrigger BEFORE UPDATE OF data ON R REFERENCING NEW ROW AS NewTuple FOR EACH STATEMENT BEGIN NewTuple.last-modified = sysdate; END;
Matthew P. Johnson, OCL1, CISDD CUNY, F Triggers: Row-level example MovieExec(name, address, cert#, netWorth) “If someone decreases a movie executive’s net worth, I want the database to reset itself to the previous net worth.” CREATE TRIGGER NetWorthTrigger AFTER UPDATE OF netWorth ON MovieExec REFERENCING NEW ROW AS NewTuple OLD ROW AS OldTuple FOR EACH ROW WHEN (OldTuple.netWorth>NewTuple.netWorth) UPDATE MovieExec SET netWorth = oldTuple.netWorth WHERE cert# = newTuple.cert#) CREATE TRIGGER NetWorthTrigger AFTER UPDATE OF netWorth ON MovieExec REFERENCING NEW ROW AS NewTuple OLD ROW AS OldTuple FOR EACH ROW WHEN (OldTuple.netWorth>NewTuple.netWorth) UPDATE MovieExec SET netWorth = oldTuple.netWorth WHERE cert# = newTuple.cert#)
Matthew P. Johnson, OCL1, CISDD CUNY, F Triggers: Table-level example MovieExec(name, address, cert#, netWorth) “If someone updates the net worth of one movie exec so that the average net worth of all movie execs becomes less than $50,000, I want the database to reset itself.” CREATE TRIGGER AvgNetWorthTrigger AFTER UPDATE OF netWorth ON MovieExec REFERENCING OLD TABLE AS OldStuff, NEW TABLE AS NewStuff FOR EACH STATEMENT WHEN (50000 > (SELECT AVG(netWorth) FROM MovieExec)) BEGIN DELETE FROM MovieExec WHERE (Name, address, cert#, netWorth) IN NewStuff; INSERT INTO MovieExec (SELECT * FROM OldStuff); END; CREATE TRIGGER AvgNetWorthTrigger AFTER UPDATE OF netWorth ON MovieExec REFERENCING OLD TABLE AS OldStuff, NEW TABLE AS NewStuff FOR EACH STATEMENT WHEN (50000 > (SELECT AVG(netWorth) FROM MovieExec)) BEGIN DELETE FROM MovieExec WHERE (Name, address, cert#, netWorth) IN NewStuff; INSERT INTO MovieExec (SELECT * FROM OldStuff); END;
Matthew P. Johnson, OCL1, CISDD CUNY, F Perl and databases DB connectivity is done through DBI Database Interface Analogous to Java’s JDBC Think of DBI as a Java class with static methods Use these to obtain a connection, prepare and execute queries, etc.
Matthew P. Johnson, OCL1, CISDD CUNY, F Perl DBI 1. Open a connection: 2. Prepare and execute query: my $dbh = DBI-> connect("dbi:mysql:database=test;mysql2.st ern.nyu.edu;port=3306", user, pass); my $sth = $dbh->prepare($query); $sth->execute; my $sth = $dbh->prepare($query); $sth->execute;
Matthew P. Johnson, OCL1, CISDD CUNY, F Perl DBI 3. Extract next row of data from statement results, if available: What this means: row has two fields, whose values are put in $a and $b, in order Other options, but this should suffice In general, want to scroll through results: Braces { } are required! my ($a, $b) = $sth->fetchrow_array() while (my ($a, $b) = $sth->fetchrow_array()) { # print out $a and $b } while (my ($a, $b) = $sth->fetchrow_array()) { # print out $a and $b }
Matthew P. Johnson, OCL1, CISDD CUNY, F Limit: Perl webpages that do something Semi-interesting Perl script: Non-trivial but not huge: ~40 lines Works with two-column (a,b) table Takes input from user Returns rows whose a field contains value If no/empty input, returns all rows Bad idea in general!
Matthew P. Johnson, OCL1, CISDD CUNY, F lookup.cgi Two possible situations for running script: 1. Page opened for the first time 2. User entered parameter and pressed button Structure of file: 1. Print input box and button for next search On button click, parameter is sent to this page’s url 2. (Try to) read input parameter 3. Open MySQL connection 4. Run query 5. Print results in a table 6. Disconnect from MySQL
Matthew P. Johnson, OCL1, CISDD CUNY, F Higher-level structure As one page: If we have params, display data based on them Otherwise, prompt user for params, call self Could be: Page 1: prompt for params, call page 2 Page 2: display data based on params In e.g.: always display data for convenience
Matthew P. Johnson, OCL1, CISDD CUNY, F Tutorials on Perl Some material drawn from the following good tutorials: CGI backend programming using perl: Perl Basics: CGI Basics: MySQL/Perl/CGI example:
Matthew P. Johnson, OCL1, CISDD CUNY, F PHP & MySQL 1. Open a connection and open our DB: 2. Run query: $db = mysql_connect("mysql2.stern.nyu.edu:3306", user, pass); mysql_select_db("test", $db); $db = mysql_connect("mysql2.stern.nyu.edu:3306", user, pass); mysql_select_db("test", $db); $result = mysql_query($query,$db);
Matthew P. Johnson, OCL1, CISDD CUNY, F PHP & MySQL 3. Extract next row of data from statement, if available: What this means: myrow is an array that can then be accessed Other options, but this should suffice In general, want to scroll through results: $myrow = mysql_fetch_row($result) while ($myrow = mysql_fetch_row($result)) # print row’s data while ($myrow = mysql_fetch_row($result)) # print row’s data
Matthew P. Johnson, OCL1, CISDD CUNY, F Limit: PHP webpages that do something Semi-interesting Perl script: Non-trivial but not huge: ~60 lines, but much plain html Works with two-column (a,b) table Takes input from user Returns rows whose a field contains value If no/empty input, returns all rows Bad idea in general!
Matthew P. Johnson, OCL1, CISDD CUNY, F lookup.php: port of lookup.cgi Two possible situations for running script: 1. Page opened for the first time 2. User entered parameter and pressed button Structure of file: 1. Print input box and button for next search On button click, parameter is sent to this page’s url 2. (Try to) read input parameter 3. Open MySQL connection 4. Run query 5. Print results in a table 6. Disconnect from MySQL
Matthew P. Johnson, OCL1, CISDD CUNY, F Insert/delete Perl/PHP example Similar to search example NB: form has two buttons t t
Matthew P. Johnson, OCL1, CISDD CUNY, F Master-detail Perl/PHP example Idea: display list of regions; When region clicked on, display its countries Mechanism: pass GET param in link, not with a FORM s/cia.pl s/cia.pl s/cia.php.txt s/cia.php.txt
Matthew P. Johnson, OCL1, CISDD CUNY, F Tutorials on PHP Some material drawn from the following good tutorials: PHP introduction and examples: Interactive PHP with database access: Longer PHP/MySQL Tutorial from webmonkey: Nice insert/update/delete example from webmonkey: MySQL/Perl/PHP page from U-Wash: