Please send questions to st10@humboldt.edu .
-----
-- CIS 180 - Intro to SQL
-- Class 8 - 3-28-03
-----

-- last modified: 3-28-03, post-class

spool 180class8_results.txt

-- setting up a clean set of empl, dept, customer tables

@ set_up_ex_tbls

-----
-- UNION, INTERSECT, MINUS
-----

-- if you think of a table as a SET of ROWS,
-- then you can do these SET operations on any two
-- UNION-COMPATIBLE tables...

-- (union-compatible: same number of columns, columns
-- are respectively of compatible types)

-- (sub-select)
-- union
-- (sub-select);

-- (sub-select)
-- intersect
-- (sub-select);

-- (sub-select)
-- minus
-- (sub-select);

-- union gives you all rows in either table...
-- ("true" union --- no duplicate rows in result)

-- what if I want the names of departments in Dallas
-- or names of departments with Clerks in them?

(select	dept_name
 from	dept
 where  dept_loc = 'Dallas')
union
(select dept_name
 from   dept d, empl e
 where  d.dept_num = e.dept_num
 and    job_title = 'Clerk');

-- how about union-ing the employees involved?

(select empl_last_name
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
union
(select empl_last_name
 from   empl e
 where  job_title = 'Clerk');

-- to get two rows for those in BOTH tables being
-- union'd, you can use UNION ALL

(select empl_last_name
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
union all
(select empl_last_name
 from   empl e
 where  job_title = 'Clerk');

-- INTERSECT gives you only rows in BOTH sets...

(select empl_last_name
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
intersect
(select empl_last_name
 from   empl e
 where  job_title = 'Clerk');

-- MINUS gives you rows of the first set NOT in the second
-- (CAN carefully use as a NOT EXISTS alternative, sometimes!)

(select empl_last_name
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
minus
(select empl_last_name
 from   empl e
 where  job_title = 'Clerk');

-- notice that the "pair", if you will, can serve as an outer select;
-- an ORDER BY can be on the end!

(select empl_last_name, salary
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
union
(select empl_last_name, salary
 from   empl e
 where  job_title = 'Clerk')
order by salary desc;

prompt FAILS --- don't put order by INSIDE one of the sub-selects!

(select empl_last_name, salary
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
union
(select empl_last_name, salary
 from   empl e
 where  job_title = 'Clerk'
 order by salary desc);

prompt FAILS -- not union compatible!

(select empl_last_name, salary
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
union
(select empl_last_name
 from   empl e
 where  job_title = 'Clerk')
order by salary desc;

prompt FAILS -- not union compatible!

(select salary, empl_last_name
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
union
(select empl_last_name, salary
 from   empl e
 where  job_title = 'Clerk')
order by salary desc;

-- BUT --- there's a limit to what SQL*Plus can check:
-- USER must make sure the results will be "meaningful"...

(select empl_last_name, job_title
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
union
(select job_title, empl_last_name
 from   empl e
 where  job_title = 'Clerk')
order by job_title desc;

-- caveat: if rename columns in sub-selects, order-by
-- needs to use the NEW names:

prompt FAILS --- order by expects 2nd column to be called payrate

(select empl_last_name, salary payrate
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
union
(select empl_last_name, salary
 from   empl e
 where  job_title = 'Clerk')
order by salary desc;

(select empl_last_name, salary payrate
 from   dept d, empl e
 where  dept_loc = 'Dallas'
 and    d.dept_num = e.dept_num)
union
(select empl_last_name, salary
 from   empl e
 where  job_title = 'Clerk')
order by payrate desc;

-- union is nice for including results that an equi-join
-- will miss...

-- consider a new department:

insert into dept
values
('600', 'Database', 'Arcata');

-- here's how I can get a count of employees in ALL
-- departments, even those with NO employees:

(select dept_name, count(*) "# Empls"
 from	dept d, empl e
 where  d.dept_num = e.dept_num
 group by dept_name)
union
((select dept_name, 0
  from   dept)
  minus
  (select distinct dept_name, 0 
   from   dept d, empl e
   where  d.dept_num = e.dept_num));

rpompt ODDNESS: cannot get order-by to work here! WHY?

(select dept_name, count(*) "# Empls"
 from   dept d, empl e
 where  d.dept_num = e.dept_num
 group by dept_name)
union
((select dept_name, 0
  from   dept)
  minus
  (select distinct dept_name, 0
   from   dept d, empl e
   where  d.dept_num = e.dept_num))
order by "# Empls";

(select dept_name, count(*) "# Empls"
 from   dept d, empl e
 where  d.dept_num = e.dept_num
 group by dept_name)
union
(select dept_name, 0
 from   dept d
 where  not exists
        (select 'a'
         from   empl e
         where  d.dept_num = e.dept_num))
order by "# Empls";

-----
-- VIEWS  
-----

-- to get of a view...
--
-- drop view viewname;
-- 
-- to create a view:
--
-- create view viewname(col1, col2, ...) AS
-- <select sttmt>;
--
-- creates a view with the columns col1, col2, ...

-- viewname can then be sued like a table in any select!
-- (can SOMETIMES make updates using a view, and sometimes not...)

-- what if I want a view that gives "public" employee info?

drop view public_empl;

create view public_empl(name, title, dept, loc) as
select empl_last_name, job_title, dept_name, dept_loc
from   dept d, empl e
where  e.dept_num = d.dept_num;

-- use it like a table!

select *
from   public_empl;

select name, loc
from   public_empl
order by loc;

prompt FAILS -- gotta use VIEW's name!

select empl_last_name
from   public_empl;

update empl
set    dept_num = '300'
where  empl_num = '7934';

-- see Miller's new location?
select *
from   public_empl;

-- CAN use query's column names, if you wish...

drop view empl_dept;

create view empl_dept as
select empl_last_name, dept_name
from   empl e, dept d
where  d.dept_num = e.dept_num;

select dept_name, empl_last_name
from   empl_dept;

-- MUST rename a computation result!

prompt FAILS --- need to rename function result!

drop view dept_pop;

create view dept_pop as
select dept_name, count(*)
from   dept d, empl e
where  e.dept_num = d.dept_num
group  by dept_name;

create view dept_pop(dept_name, num_empls) as
select dept_name, count(*)
from   dept d, empl e
where  e.dept_num = d.dept_num
group  by dept_name;

select	dept_name, num_empls
from	dept_pop
order by num_empls;

drop view dept_pop;

-- this works, too:

create view dept_pop as
select dept_name, count(*) num_empls
from   dept d, empl e
where  e.dept_num = d.dept_num
group  by dept_name;


select  dept_name, num_empls
from    dept_pop
order by num_empls;


-----
-- SEQUENCES
-----

-- first, tables to help us play with sequences...

drop table painter cascade constraints;

create table painter
(ptr_num        char(3),
 ptr_lname      varchar2(30) not null,
 ptr_fname      varchar2(15),
 primary key    (ptr_num)
);

drop table painting cascade constraints;

create table painting
(ptg_title      varchar2(30),
 ptr_num        char(3),
 primary key    (ptg_title),
 foreign key    (ptr_num) references painter
);

-- create a sequence to help me set good primary keys
-- over time for table painter

drop sequence painter_seq;

create sequence painter_seq
increment by    2
start with      100;

-- seqname.nextval gets the NEXT values in the sequence, and
-- resets the sequence accordingly...
-- 
-- seqname.currval gets the CURRENT value of the sequence,
-- without changing it;

-- let's use the sequence to help set primary keys...

-- this first one is weird, BUT it shows that you could
-- concatenate sequences with alpha characters to build
-- table attribute values...

insert into painter
values
(painter_seq.nextval, 'Van Gogh', 'a' || painter_seq.currval);

insert into painter
values
(painter_seq.nextval, 'Monet', 'Claude');

insert into painter
values
(painter_seq.nextval, 'Da Vinci', 'Leonardo');

select  *
from    painter;

-- [sequence_name].currval should get you the sequence's CURRENT value;
-- if I know that the painting I'm adding is by the
-- 'latest' painter added, can I do this? Yes;

insert into painting
values
('Mona Lisa', painter_seq.currval);

select  *
from    painting;

-- sequences can be used in queries, too... maybe???
-- (OK, these didn't work...)

prompt FAILS --- not sure why!
select  *
from    painter
where   ptr_num = painter_seq.currval;

select  *
from    painter
where   ptr_num = (select painter_seq.currval
                   from   painter);             

select  *
from    painter
where   ptr_num = (select painter_seq.currval
                   from   painter_seq);
                 

-- stop writing results to file 180class8_results.txt
spool off