CIS 318 - Week 2 Lab - 1-28-13

********
PL/SQL -- adds procedural programming features to Oracle SQL

*   you saw an example of a trigger written in PL/SQL in CIS 315;
    now we are adding PL/SQL stored procedures and stored
    functions

*   usually, (in theory? 8-) ) the primary goal of a trigger
    is to ENHANCE DATABASE INTEGRITY

    in contrast, usually the primary goal of stored procedures
    and stored functions is to IMPLEMENT BUSINESS RULES

    *   wait a minute -- aren't we, then, kind of violating the 
        idea of our n-tiers, putting business rules in the 
        data-server tier? 

	I think so! -- philosophical to consider: when, then, 
        are the costs of this worth it?

	...we'll see, for example, that there can be SECURITY 
	advantages to stored procedures and stored functions
	(harder to SQL-inject)

    *   I don't have a good answer for how "bad" this violation 
        is yet -- 
        but it is something to consider/keep in mind;

*   as you know -- PL/SQL is block-structured;
    
    *   basic PL/SQL block: ([] means optional piece within!)

    [DECLARE                   -- but you don't use DECLARE before a
        chosen_name  type;      -- PL/SQL subroutine's local 
        another_name type;      -- declarations
    ]
    BEGIN
        [statements;]
    [EXCEPTION
        exception_handlers_here;]
    END;
    /     -- NEED / after the END; of an OUTERMOST block, or 
          --     PL/SQL subroutine will NOT be compiled!

*   also nice to put:

    show errors

    ...after a subroutine's final END; (an outermost block), so that
    you will be shown any compiler errors;

*   procedure header syntax:

    -- opening comment (at least procedure name, description of what
    --     it expects and does)

    create or replace procedure procedure_name(param_list) as
        local_decls;
    begin
        statements;
    end;
    /
    show errors

    *   to execute a stored procedure from the SQLPLUS prompt,
        type 

        exec proc_call(argument, argument, ... argument)

    *   to execute a stored procedure from PL/SQL subroutines,
        type

        proc_call(argument, argument, ... argument)

    *   (but a zero-argument procedure doesn't have to have () in
        its call, although it can!)

*   let's review/intro a few more PL/SQL features before our
    first stored procedure example:

*   PL/SQL identifiers:

    *   begins with a letter;
    *   followed by 0 or more letters, digits, underscores, $, #

    *   like SQL -- case is NOT significant for identifiers;
    *   cannot be a reserved word

*   variable types

    *   any SQL type can be the type of the PL/SQL variable;
    *   BUT PL/SQL variables have a few ADDITIONAL possible
        types as well -- for example,
        *   boolean

    *   in PL/SQL declaration, you put the name, THEN its type!

    DECLARE   
       found_name boolean;
       name       varchar2(27);
       id_code    char(5);
       quantity   int;
       cost       number(6, 2);
       date_done  date := sysdate;
       max_salary number;

*   PL/SQL statements

    *   any SQL statement is a PL/SQL statement;
    
    *   assignment statements

        variable := expression;
        cost := 3.95;

    *   to "grab" the result from a select into a variable,

        put an INTO clause between the SELECT and FROM clauses
        of a select statement;

        select blah
        into desired_variable
	where blah;

        select max(salary)
        into max_salary
        from empl;
    
        ...and INTO clause can have a comma-separated
	   list of variables:

        select book_no, pub_no
        into bnum, pnum
        from books
        where book_no = '100';

        *   NOTE: number and types of values projected have to
	    be suitable for the number and types of variables in
	        the INTO clause!

        *   STYLE: avoid having variable names
	    that are the SAME as attribute names
	        in the tables being used!!

    *   example of a PL/SQL procedure: see count_empls in
        count-empls-proc.sql

*   function header syntax:

    -- opening comment (at least procedure name, description of what
    --     it expects and returns)

    create or replace function funct_name(param_list) 
                      return ret_type is
        local_decls;
    begin
        statements;
        ...
        return expr;
    end;
    /
    show errors

    *   I thought for a long time you couldn't directly call
        a PL/SQL function from the SQL*Plus command line --

	then I thought maybe you could, but with very arcane
	syntax;

	now I found out -- can, with syntax that ISN'T so bad!!

        -- this worked 1-28-13!

        -- here's how you can declare a local variable at 
        -- the SQL*Plus level

        var num_empls number

        -- here's how you can execute a function, storing what that
        --     function returns in that local variable 
        --     (notice the : and := )

        exec :num_empls := empl_count();

        -- and here's how you can output the value of a local 
        --     SQL*Plus variable

        print num_empls

    *   can ALSO project a function's result by using a select
        from special table dual:    

        select my_funct(arguments)
        from dual;
   
    *   and, if you prefer, can also include a little 
        testing *procedure* as a convenient tester for a function...
        (how I USED to do it...!)

    *   example of a PL/SQL function: see empl_count in
        empl_count_funct.sql

********
*   IF statements:

    IF <condition> THEN
        statements;
    [ELSE
        statements;]
    END IF;

    *   we'll see examples of this on Wednesday;

********
*   repetition in PL/SQL --

    LOOP statement, that ends with
    END LOOP;

    *   here are two of the common variants:

    WHILE condition
    LOOP
       repeated_statements;
    END LOOP;

    FOR loop_index IN lowest_number .. highest_number
    LOOP
       repeated_statements;
    END LOOP;

    *   we'll also see examples of these on Wednesday;

********
*   simple version of a cursor

    for row in (select_of_your_choice)
    loop
        ... row.col_name ...
    end loop;

    *    ...and this as well;

...we'll talk about adding parameters to these,
   and exception handling, on Wednesday as well, hopefully;