4. Embedded SQL for Fortran : Sample Applications : The Department-Employee Master/Detail Application
 
Share this page                  
The Department-Employee Master/Detail Application
This application uses two database tables joined on a specific column. This typical example of a department and its employees demonstrates how to process two tables as a master and a detail.
The program scans through all the departments in a database table, in order to reduce expenses. Based on certain criteria, the program updates department and employee records. The conditions for updating the data are the following:
Departments:
If a department has made less than $50,000 in sales, the department is dissolved.
Employees:
If an employee was hired since the start of 1985, the employee is terminated.
If the employee's yearly salary is more than the minimum company wage of $14,000 and the employee is not nearing retirement (over 58 years of age), the employee takes a 5% pay cut.
If the employee's department is dissolved and the employee is not terminated, the employee is moved into a state of limbo to be resolved by a supervisor.
This program uses two cursors in a master/detail fashion. The first cursor is for the "department" table, and the second cursor is for the "employee" table. Both tables are described in declare table statements at the start of the program. The cursors retrieve all the information in the tables, some of which is updated. The cursor for the "employee" table also retrieves an integer date interval whose value is positive if the employee was hired after January 1, 1985.
Each row that is scanned, from both the "department" table and the "employee" table, is recorded in an output file. This file serves both as a log of the session and as a simplified report of the updates that were made.
Each section of code is commented for the purpose of the application and also to clarify some of the uses of the Embedded SQL statements. The program illustrates table creation, multi-statement transactions, all cursor statements, direct updates and error handling.
If your application requires the use of structures, see Fortran Variables and Data Types in this chapter for more information.
This application runs in UNIX, VMS, and Windows environments.
C
C  Program: ProcessExpenses
C  Purpose: Main entry point to process department and employee expenses
C
      program ProcessExpenses
      exec sql include sqlca
      exec sql declare dept table
     1 (name          char(12) not null,
     2 totsales       decimal(14,2) not null,
     3 employees      integer2 not null) 
      exec sql declare employee table
     1 (name          char(20) not null, 
     2 age            integer1 not null, 
     3 idno           integer4 not null,
     4 hired          date not null, 
     5 dept           char(12) not null, 
     6 salary         decimal(14,2)  not null)
C "State-of-Limbo" for employees who lose their departments 
      exec sql declare toberesolved table
     1 (name        char(20) not null, 
     2 age          integer1 not null,
     3 idno         integer4 not null,
     4 hired        date not null, 
     5 dept         char(12) not null, 
     6 salary       decimal(14,2) not null)
      print *, 'Entering application to process expenses.' 
      open(unit = 1, file = 'expenses.log', status = 'new')
      call InitDb
      call ProcessDepts
      call EndDb
      close(unit = 1, status = 'keep')
      print *, 'Successful completion of application.'
      end
C
C Subroutine: InitDb
C Purpose:    Initialize the database. Connect to the database and 
C             abort if an error. Before processing employees, 
C             confirm that the table for employees who lose 
C             their departments,"toberesolved," 
C             exists. Initiate multi-statement transaction.
C Parameters: None.
C
      subroutine InitDb
      exec sql include sqlca
      exec sql whenever sqlerror stop
      exec sql connect personnel
      write (1, 10) 
10    format ('Creating "To_Be_Resolved" table.')
      exec sql create table toberesolved
     1     (name       char(20) not null,  
     2      age        integer1 not null,
     3      idno       integer4 not null,
     4      hired      date not null,
     5      dept       char(10) not null,
     6      salary     decimal(14,2) not null)

      end
 
C
C Subroutine:     EndDb
C Purpose:        End the multi-statement transaction and access
C                 to the database.
C Parameters:     None.
C
      subroutine EndDb
      exec sql include sqlca
      exec sql commit
      exec sql disconnect
      end
C
C Subroutine: ProcessDepts
C Purpose:    Scan through all the departments, processing each
C             one. If the department has made less than $50,000 
C             in sales,then the department is 
C             dissolved. For each department, process all the
C             employees (they may even be moved to
C             another table.) If an employee was terminated,
C             update the department's employee counter.
C Parameters: None
C
      subroutine ProcessDepts
      exec sql include sqlca
      exec sql begin declare section
           character*12        dname
           double precision    dsales
           integer*2           demps
C Employees terminated
           integer*2           dterm
      exec sql end declare section
C Minimum sales of department
      parameter (mindeptsales = 50000.00)
C Was the dept deleted?
      logical deldept 
C Formatting value
      character*20 deptformat
      exec sql declare deptcsr cursor for
     1    select name, totsales, employees
     2    from dept
     3    for direct update of name, employees
C All errors from this point on close down the application 
      exec sql whenever sqlerror call closedown
C Close deptcsr
      exec sql whenever not found go to 100
      exec sql open deptcsr
      dterm = 0
55    if (sqlcod .ne. 0) go to 555
      exec sql fetch deptcsr into :dname, :dsales, :demps
C Did the department reach minimum sales?
      if (dsales .lt. mindeptsales) then
          exec sql delete from dept
     1          where current of deptcsr
          deldept = .true.
          deptformat = ' -- DISSOLVED --'
      else
          deldept = .false.
          deptformat = ' '
      endif
 
C Log what we have just done
       write (1, 11) dname, dsales, deptformat
11     format ('Department: ', a14, ', Total Sales: ', f12.3, a)
C Now process each employee in the department
      call ProcessEmployees(dname, deldept, dterm)

C If some employees were terminated, record this fact 
      if (dterm .gt. 0 .and. .not. deldept) then
          exec sql update dept
     1         set employees = :demps - :dterm
     2         where current of deptcsr
      endif
      go to 55
 
555   continue
      exec sql whenever not found continue

100   exec sql close deptcsr
      end
C
C Subroutine:  ProcessEmployees
C Purpose:     Scan through all the employees for a particular 
C              department.Based on given conditions, the employee
C              may be terminated or given a salary reduction.
C            1. If an employee was hired since 1985, the employee
C               is terminated.
C            2. If the employee's yearly salary is more than the
C               minimum company wage of $14,000 and the employee
C               is not close to retirement (over 58 years of age),
C               the employee takes a 5% salary reduction.
C            3. If the employee's department is dissolved and the
C               employee is not terminated, then the employee is
C               moved into the "toberesolved" table.
C
C Parameters: sname      - Name of current department
C             sdel       - Is current department being dissolved?
C             sterm      - Set locally to record how many employees
C                           were terminated for the current 
C                           department.
C
      subroutine ProcessEmployees(sname, sdel, sterm)
      exec sql include sqlca
      exec sql begin declare section
           character*12     sname
           character*20     name
           integer*2        age
           integer*4        idno
           character*25     chired
           real             salary
           integer*4        ihired
           parameter (salreduc = 0.95)
      exec sql end declare section
C Minimum employee salary
      parameter        (minsal = 14000.00)
      parameter        (nearlyretired = 58)
C Formatting values
      character*12 title
      character*25 description
C Subroutine arguments
      logical        sdel
      integer*2      sterm
C Note the use of the Ingres function to find out who
C has been hired since 1985.
      exec sql declare empcsr cursor for
     1   select name, age, idno, hired, salary,
     2     int4(interval('days', hired-date('01-jan-1985')))
     3   from employee
     4   where dept = :sname
     5   for direct update of name, salary
C  All errors from this point on close down the application 
      exec sql whenever sqlerror call closedown
C Close empcsr
      exec sql whenever not found go to 200
      exec sql open empcsr
      sterm = 0 
66    if (sqlcod .ne. 0) go to 666
      exec sql fetch empcsr into :name, :age, :idno,  
     1     :chired, :salary, :ihired
      if (ihired .gt. 0) then
           exec sql delete from employee
     1          where current of empcsr
           title = 'Terminated:'
           description = 'Reason: Hired since 1985.'
           sterm = sterm + 1
      else if (salary .gt. minsal) then
C Reduce salary if not nearly retired
           if (age .lt. nearlyretired) then
               exec sql update employee
    1                 set salary = salary * :salreduc  
    2                 where current of empcsr
               title = 'Reduction:'
               description = 'Reason: Salary.'  
           else
C  Do not reduce salary
               title = 'No Changes:'
               description = 'Reason: Retiring.'
           endif
      else 
C  Leave employee alone
           title = 'No Changes:'
           description = 'Reason: Salary.'
      endif
C  Was employee's department dissolved?
      if (deldept) then
          exec sql insert into toberesolved
    1          select *
    2          from employee
    3          where idno = :idno
          exec sql delete from employee
    1          where current OF empcsr
      endif
C  Log the employee's information
      write (1, 12) title, idno, name, age, salary, description
12    format (' ', a, ' ', i6, ', ', a, ', ', i2, ', ', f8.2, ';', 
     1        ' ' a)
      go to 66
666   continue
      exec sql whenever not found continue
200   exec sql close empcsr
      end
C
C Subroutine:   CloseDown
C Purpose:      Error handler called any time after InitDb has been
C               successfully completed. In all cases, print the
C               cause of the error and abort the transaction, 
C               backing out 
C               change Note that disconnecting from the database
C               will implicitly close any open cursors.
C Parameters:   None
C
      subroutine CloseDown
      exec sql include sqlca
      exec sql begin declare section
          character*100 errbuf
      exec sql end declare section
C  Turn off error handling
      exec sql whenever sqlerror continue
      exec sql copy sqlerror into :errbuf with 100
      write (1, 13)
13    format ('Closed down because of database error:')
      write (1, 14) errbuf
14    format (a)
      close(unit = 1, status = 'keep')
      exec sql rollback
      exec sql disconnect
      print *, stop 'An SQL error occurred - Check the log file.'
      stop
      end