/**************************************************************************************/
/* File:        demo27                                                                */
/* Project:     ATSQL2 : Quick Tour in 'Change Proposal - Adding Valid Time to        */
/*                       SQL/Temporal'                                                */
/*                       Note that in the change proposal we use the keywords         */
/*                       VALIDTIME instead of VALID and                               */
/*                       TRANSACTIONTIME instead of TRANASCTION                       */
/* Author:      Richard Snodgrass, University of Arizona, Tucson, AZ                  */
/* Date:        25.10.1996                                                            */
/* Results:                                                                           */
/**************************************************************************************/


SET CALENDRIC SYSTEM SQL3;


/*************/
/*  LEVEL 1  */
/*************/

/* The following statements are executed on January 1, 1995: */

SET CLOCK TO TIMESTAMP '1995-01-01';


CREATE TABLE employee(ename VARCHAR(12), eno INTEGER PRIMARY KEY,
                        street VARCHAR(22), city VARCHAR(10), birthday DATE);

CREATE TABLE salary(eno INTEGER REFERENCES employee(eno), amount INTEGER);

CREATE ASSERTION emp_has_sal CHECK
  (NOT EXISTS ( SELECT *
                FROM employee AS e
                WHERE NOT EXISTS ( SELECT *
                                   FROM salary AS s
                                   WHERE e.eno = s.eno)));


INSERT INTO employee 
        VALUES ('Therese', 5873, 'Bahnhofstrasse 121', 'Zurich', DATE '1961-03-21');
INSERT INTO employee 
        VALUES ('Franziska', 6542, 'Rennweg 683', 'Zurich', DATE '1963-07-04');

INSERT INTO salary VALUES (6542, 3200);
INSERT INTO salary VALUES (5873, 3300);


CREATE VIEW high_salary AS SELECT * FROM salary WHERE amount > 3500;


UPDATE salary s
        SET    (amount) = (SELECT 1.1 * s.amount
                           FROM   salary s, employee e
                           WHERE  e.ename = 'Therese'
                                 AND    s.eno = e.eno)
  WHERE s.eno = (SELECT e.eno FROM employee e WHERE e.ename = 'Therese');

COMMIT;



SELECT * FROM employee;

/*
     ename  eno             street   city     birthday
 --------- ---- ------------------ ------ ------------
   Therese 5873 Bahnhofstrasse 121 Zurich |1961-03-21|
 Franziska 6542        Rennweg 683 Zurich |1963-01-01|
*/


SELECT * FROM salary;

/*
  eno amount
 ---- ------
 6542   3200
 5873   3630
*/


/*************/
/*  LEVEL 2  */
/*************/

/* The following statements are executed on February 1, 1995: */

SET CLOCK TO TIMESTAMP '1995-02-01';


/* Granularity SECOND is used because DAY is not yet supported. */

ALTER TABLE salary ADD VALIDTIME SECOND;

ALTER TABLE employee ADD VALIDTIME SECOND;


/* The following statements are typed in the next day: */

SET CLOCK TO TIMESTAMP '1995-02-02';


INSERT INTO employee 
        VALUES('Lilian', 3463, '46 Speedway', 'Tuscon', DATE '1970-03-09');
INSERT INTO salary VALUES(3463, 3400);


COMMIT;


/* The employee table contains the following rows: */

VALIDTIME SELECT * FROM employee;

/*
                VALID     ename  eno             street   city     birthday
 -------------------- --------- ---- ------------------ ------ ------------
 [1995-02-01-forever)   Therese 5873 Bahnhofstrasse 121 Zurich |1961-03-21|
 [1995-02-01-forever) Franziska 6542        Rennweg 683 Zurich |1963-01-01|
 [1995-02-02-forever)    Lilian 3463        46 Speedway Tucson |1970-03-09|
*/

/* The salary table contains following rows: */


VALIDTIME SELECT * FROM salary;

/*
                VALID  eno amount
 -------------------- ---- ------
 [1995-02-01-forever) 6542   3200
 [1995-02-01-forever) 5873   3630
 [1995-02-02-forever) 3463   3400
*/


/* We continue, still on February 2. Tables, views, and queries act like
   before, due to temporal upward compatibility. */

SELECT ename, city
FROM   high_salary AS s, employee AS e
WHERE  s.eno = e.eno;

/* This returns the employee Therese, in Zuerich: 

   ename   city
 ------- ------
 Therese Zurich
*/


/* Assertions and referential integrity act like before, applying to the 
   current state. The following transaction will abort due to (1) a violation 
   of the  PRIMARY KEY constraint, (2) a violation of the emp_has_sal assertion
   and (3) a referential integrity violation, respectively. */


INSERT INTO employee
        VALUES ('Eric', 3463, '701 Broadway', 'Tucson', DATE '1988-01-06');
INSERT INTO employee
        VALUES ('Melanie', 1234, '701 Broadway', 'Tucson', DATE '1991-03-08');
INSERT INTO salary VALUES(9999, 4900);


COMMIT;



/*************/
/*  LEVEL 3  */
/*************/

/* We evaluate the following statements on March 1, 1995: */

SET CLOCK TO TIMESTAMP '1995-03-01';


VALIDTIME
     SELECT ename, amount
     FROM   salary AS s, employee AS e
     WHERE  s.eno = e.eno;

/*
This evaluates to the following:

                VALID     ename amount
 -------------------- --------- ------
 [1995-02-01-forever) Franziska   3200
 [1995-02-01-forever)   Therese   3630
 [1995-02-02-forever)    Lilian   3400
*/


/* List those for which no one makes a higher salary in a different
   city, over all time. */

VALIDTIME 
     SELECT ename
     FROM   employee AS e1, salary AS s1
     WHERE  e1.eno = s1.eno
     AND NOT EXISTS (SELECT ename
                     FROM   employee AS e2, salary AS s2
                     WHERE  e2.eno = s2.eno
                     AND    s2.amount > s1.amount
                     AND    e1.city <> e2.city);

/* This gives the following result: 

                   VALID     ename
 ----------------------- ---------
    [1995-02-01-forever)   Therese
 [1995-02-01-1995-02-02) Franziska
*/


/* We then create a temporal view, similar to the non-temporal view defined
   earlier. In fact, the only difference is the use of the reserved word VALIDTIME. */


CREATE VIEW high_salary_history AS
      VALIDTIME SELECT * FROM salary s WHERE s.amount > 3500;


/* Finally, we define a temporal column constraint: */

ALTER TABLE salary ADD VALIDTIME CHECK (amount > 1000 AND amount < 12000);

COMMIT;


/* Snapshot reducible modifications are similarly handled. To remove employee
   #5873 for all states of the database, we use the following statements. */


VALIDTIME DELETE FROM employee
      WHERE eno = 5873;

VALIDTIME DELETE FROM salary
      WHERE eno = 5873;

COMMIT;


/* To correct the common mispelling of Tucson, we use the following statement. */

VALIDTIME UPDATE employee
       SET city = 'Tucson'
       WHERE city = 'Tuscon';

COMMIT;



/*************/
/*  LEVEL 4  */
/*************/

SET CALENDRIC SYSTEM SQL3;

VALIDTIME SELECT * FROM employee;

/*
                VALID     ename  eno      street   city     birthday
 -------------------- --------- ---- ----------- ------ ------------
 [1995-02-01-forever) Franziska 6542 Rennweg 683 Zurich |1963-07-04|
 [1995-02-02-forever)    Lilian 3463 46 Speedway Tucson |1970-03-09|
*/


VALIDTIME SELECT * FROM salary;

/*
                VALID  eno amount
 -------------------- ---- ------
 [1995-02-01-forever) 6542   3200
 [1995-02-02-forever) 3463   3400
*/


/* List those who were employed sometime during the first six months. */

VALIDTIME PERIOD '[1995-01-01 - 1995-07-01)' SELECT ename FROM employee;

/*
                   VALID     ename
 ----------------------- ---------
 [1995-02-01-1995-07-01) Franziska
 [1995-02-02-1995-07-01)    Lilian
*/



/* On April 1, 1995, we give Lilian a 5% raise, starting immediately.
   This is a temporally upward compatible modification, and so is already 
   expressible in SQL. */

SET CLOCK TO TIMESTAMP '1995-04-01';

UPDATE salary s
  SET (amount) = (SELECT 1.05 * s.amount
                  FROM   salary s, employee e
                  WHERE  e.ename = 'Lilian'
                  AND    s.eno = e.eno)
WHERE s.eno = (SELECT e.eno FROM employee e WHERE e.ename = 'Lilian');


COMMIT;


/* This results in the following salary table. */

VALIDTIME SELECT * FROM salary;

/*
                   VALID  eno amount
 ----------------------- ---- ------
    [1995-02-01-forever) 6542   3200
 [1995-02-02-1995-04-01) 3463   3400
    [1995-04-01-forever) 3463   3570
*/



/* To determine who was given salary raises, we must simultaneously
   consider two consecutive states of the salary table, before and
  after the raise. This requires a nonsequenced query. */

NONSEQUENCED VALIDTIME 
  SELECT ename
  FROM employee AS E, salary AS S1, salary AS S2
  WHERE E.eno = S1.eno AND E.eno = S2.eno
  AND S1.amount < S2.amount AND VALIDTIME(S1) MEETS VALIDTIME(S2);

/*
  ename
 ------
 Lilian
*/



/* If we instead wish to get back a valid-time table, i.e., ``Who was
   given salary raises, and when did they receive the higher salary?'',
   we place a value expression after VALIDTIME to specify when
   each resulting row is valid. */

NONSEQUENCED VALIDTIME VALIDTIME(S2) 
  SELECT ename
  FROM employee AS E, salary AS S1, salary AS S2
  WHERE E.eno = S1.eno AND E.eno = S2.eno
  AND S1.amount < S2.amount AND VALIDTIME(S1) MEETS VALIDTIME(S2);


/* This query has the following result. */

/*
                VALID  ename
 -------------------- ------
 [1995-04-01-forever) Lilian
*/



/* If we had desired the time when the person had received the
   lower salary, we would simply specify VALIDTIME(S1) instead. */

NONSEQUENCED VALIDTIME VALIDTIME(S1) 
  SELECT ename
  FROM employee AS E, salary AS S1, salary AS S2
  WHERE E.eno = S1.eno AND E.eno = S2.eno
  AND S1.amount < S2.amount AND VALIDTIME(S1) MEETS VALIDTIME(S2);

/*
                   VALID  ename
 ----------------------- ------
 [1995-02-02-1995-04-01) Lilian
*/



/* Following VALIDTIME with a period expression in a modification
   (whether sequenced or not) specifies the temporal scope of the modification. 
   Two applications of this are retroactive and future changes. Assume it is now 
   May 1, 1995. Franziska, employee 6542, will be taking a leave of absence the
   last half of the year. */

SET CLOCK TO TIMESTAMP '1995-05-01';

VALIDTIME PERIOD '[1995-07-01 - 1996-01-01)'
DELETE FROM salary
WHERE eno = 6542;


VALIDTIME PERIOD '[1995-07-01 - 1996-01-01)'
DELETE FROM employee
WHERE eno = 6542;

COMMIT;

/* The salary table now has the following contents. */

VALIDTIME SELECT * FROM salary;

/*
                   VALID  eno amount
 ----------------------- ---- ------
 [1995-02-02-1995-04-01) 3463   3400
    [1995-04-01-forever) 3463   3570
 [1995-02-01-1995-07-01) 6542   3200
    [1996-01-01-forever) 6542   3200
*/


/* The employee table has the following contents. */

VALIDTIME SELECT * FROM employee;

/*
                   VALID     ename  eno      street   city     birthday
 ----------------------- --------- ---- ----------- ------ ------------
    [1995-02-02-forever)    Lilian 3463 46 Speedway Tucson |1970-03-09|
 [1995-02-01-1995-07-01) Franziska 6542 Rennweg 683 Zurich |1963-07-04|
    [1996-01-01-forever) Franziska 6542 Rennweg 683 Zurich |1963-07-04|
*/




/* The period expression following VALIDTIME is also allowed for
   assertions and constraints. Assume that no employee may make less
   than 3400 during 1996. */


CREATE ASSERTION salary_check
  VALIDTIME PERIOD '[1996-01-01 - 1997-01-01)'
  CHECK (NOT EXISTS ( 
	   SELECT * FROM salary WHERE amount < 3000 ));



/* We can augment this statement to use a non-sequenced query in the from clause
   to look for raises in the past. */

SET CLOCK TO TIMESTAMP '1995-06-01';

UPDATE salary s
SET (amount) = (SELECT 1.05 * amount 
                FROM   salary s
                WHERE  NOT EXISTS (SELECT *
                                   FROM   (NONSEQUENCED VALIDTIME 
                                           SELECT S1.eno
                                           FROM   salary AS S1, salary AS S2
                                           WHERE  S1.amount < S2.amount
                                           AND    VALIDTIME(S1) MEETS VALIDTIME(S2)
                                           AND    S1.eno = S2.eno) AS S3
                                   WHERE S.eno = S3.eno))
WHERE NOT EXISTS (SELECT *
                  FROM   (NONSEQUENCED VALIDTIME 
                          SELECT S1.eno
                          FROM   salary AS S1, salary AS S2
                          WHERE  S1.amount < S2.amount
                          AND    VALIDTIME(S1) MEETS VALIDTIME(S2)
                          AND    S1.eno = S2.eno) AS S3
                  WHERE S.eno = S3.eno);

VALIDTIME SELECT * FROM salary;

/*
                   VALID  eno amount
 ----------------------- ---- ------
 [1995-02-02-1995-04-01) 3463   3400
    [1995-04-01-forever) 3463   3570
 [1995-02-01-1995-06-01) 6542   3200
 [1995-06-01-1995-07-01) 6542   3360
    [1996-01-01-forever) 6542   3360
*/

COMMIT;


/* Finally, we wish to define a snapshot view of the salary table in which
   the row timestamp appears as an explicit column. */

CREATE VIEW snapshot_salary (eno, amount, when) AS
  NONSEQUENCED VALIDTIME 
    SELECT S.*, VALIDTIME(S) 
    FROM   salary AS S;


SELECT * FROM snapshot_salary;

/*
  eno amount                    when
 ---- ------ -----------------------
 3463   3400 [1995-02-02-1995-04-01)
 3463   3570    [1995-04-01-forever)
 6542   3200 [1995-02-01-1995-06-01)
 6542   3360 [1995-06-01-1995-07-01)
 6542   3360    [1996-01-01-forever)
*/


/* Coming around full circle, we can define a valid-time view on
   snapshot_salary that uses the explicit column valid as an
   implicit timestamp. */

CREATE VIEW temporal_salary (eno, amount) AS
  NONSEQUENCED VALIDTIME when
  SELECT eno, amount 
  FROM   snapshot_salary AS S;


VALIDTIME SELECT * FROM temporal_salary;

/*
                   VALID  eno amount
 ----------------------- ---- ------
 [1995-02-02-1995-04-01) 3463   3400
    [1995-04-01-forever) 3463   3570
 [1995-02-01-1995-06-01) 6542   3200
 [1995-06-01-1995-07-01) 6542   3360
    [1996-01-01-forever) 6542   3360
*/


DROP TABLE salary;
DROP TABLE employee;

DROP ASSERTION emp_has_sal;
DROP ASSERTION salary_check;

DROP VIEW high_salary;
DROP VIEW high_salary_history;

DROP VIEW snapshot_salary;
DROP VIEW temporal_salary;

