/**************************************************************************************/
/* File:        demo28                                                                */
/* Project:     ATSQL2 : Quick Tour in 'SQL3 Change Proposal - Adding Transaction     */
/*                                      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:                                                                           */
/**************************************************************************************/

/**************************************************************************************/ 
/*   Note: - level 1 to 4 correspond to demo27 and appeared in quick tour of          */
/*           'SQL3: Change Proposal - Adding Valid Time to SQL/Temporal'              */
/*         - new section 'Transaction Time' appeared in quick tour of                 */
/*           'SQL3: Change Proposal - Adding Transaction Time to SQL/Temporal '       */
/**************************************************************************************/

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
*/


/********************/
/* Transaction time */
/********************/

SET CALENDRIC SYSTEM SQL3;

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|
*/


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
*/


/* We can alter the employee table to be a table with both valid-time and
   transaction-time support, by adding transaction-time support. Assume that the
   current date is July 1, 1995. */

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

ALTER TABLE employee ADD TRANSACTIONTIME;
COMMIT;

VALIDTIME AND TRANSACTIONTIME SELECT * FROM employee;

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


/* Temporal upward compatibility guarantees that conventional, nontemporal 
   queries, updates, etc. work as before, with the same semantics. We can list 
   those for which (currently, as best known) no one makes a higher salary in 
   a different city. */

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);

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


/* We can also ask for the valid times that this is true, by simply
   prepending ``VALIDTIME''. */

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);

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



/* Temporally upward compatible modifications also work as before. Assume
   it is now August 1, 1995. Franziska just moved. */

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

UPDATE employee
SET street = 'Niederdorfstrasse 2'
WHERE ename = 'Franziska';

COMMIT;


VALIDTIME AND TRANSACTIONTIME SELECT * FROM employee;

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


/* The query, ``When was the street changed?'', combines nonsequenced 
   transaction with temporal upward compatibility in valid time, since 
   we are interested in the current street. */

NONSEQUENCED TRANSACTIONTIME AND VALIDTIME
SELECT e1.ename, e1.street AS old_street, e2.street AS new_street,
       BEGIN(TRANSACTIONTIME(e2)) AS trans_time
FROM   employee AS e1, employee AS e2
WHERE  e1.eno = e2.eno 
AND    TRANSACTIONTIME(e1) MEETS TRANSACTIONTIME(e2);

/*
                VALID  e1.ename  old_street          new_street   trans_time
 -------------------- --------- ----------- ------------------- ------------
 [1996-01-01-forever) Franziska Rennweg 683 Niederdorfstrasse 2 |1995-08-01|
*/


/* To extract all the information from the employee table, we can use a
   sequenced valid/sequenced transaction query.  Such queries can have
   arbitrarily complex predicates. ``When did we think that someone lived
   somewhere for more than six months?''. */

VALIDTIME AND TRANSACTIONTIME 
  SELECT ename, street
  FROM   employee
  WHERE  END(VALIDTIME(employee)) - BEGIN(VALIDTIME(employee)) > INTERVAL '6' MONTH;

/*
             TRANSACTION                VALID     ename              street
 ----------------------- -------------------- --------- -------------------
    [1995-07-01-forever) [1995-02-02-forever)    Lilian         46 Speedway
    [1995-08-01-forever) [1996-01-01-forever) Franziska Niederdorfstrasse 2
 [1995-07-01-1995-08-01) [1996-01-01-forever) Franziska         Rennweg 683
*/


/* Assume it is now October 1, 1995. Lilian moved last June 1. */

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

VALIDTIME PERIOD '[1995-06-01-forever]'
  UPDATE employee
  SET street = '124 Alberca'
  WHERE ename = 'Lilian';


COMMIT;

VALIDTIME AND TRANSACTIONTIME SELECT * FROM employee;

/*
             TRANSACTION                   VALID     ename  eno              street   city     birthday
 ----------------------- ----------------------- --------- ---- ------------------- ------ ------------
    [1995-07-01-forever) [1995-02-01-1995-07-01) Franziska 6542         Rennweg 683 Zurich |1963-07-04|
 [1995-07-01-1995-08-01)    [1996-01-01-forever) Franziska 6542         Rennweg 683 Zurich |1963-07-04|
    [1995-08-01-forever)    [1996-01-01-forever) Franziska 6542 Niederdorfstrasse 2 Zurich |1963-07-04|

 [1995-07-01-1995-10-01)    [1995-02-02-forever)    Lilian 3463         46 Speedway Tucson |1970-03-09|
    [1995-10-01-forever) [1995-02-02-1995-06-01)    Lilian 3463         46 Speedway Tucson |1970-03-09|
    [1995-10-01-forever)    [1995-06-01-forever)    Lilian 3463         124 Alberca Tucson |1970-03-09|
*/



/* The query, ``When was an employee's address for 1995 corrected?'', 
   involves nonsequenced transaction semantics and sequenced valid semantics,
   with a temporal scope of 1995. */

NONSEQUENCED TRANSACTIONTIME AND VALIDTIME PERIOD '[1995-01-01-1996-01-01)'
  SELECT e1.ename, e1.street AS old_street, e2.street AS new_street,
         BEGIN(TRANSACTIONTIME(e2)) AS trans_time
  FROM   employee AS e1, employee AS e2
  WHERE  e1.eno = e2.eno AND TRANSACTIONTIME(e1) MEETS TRANSACTIONTIME(e2)
  AND    e1.street <> e2.street;

/*
                   VALID e1.ename  old_street  new_street   trans_time
 ----------------------- -------- ----------- ----------- ------------
 [1995-06-01-1996-01-01)   Lilian 46 Speedway 124 Alberca |1995-10-01|
*/


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;

