Současné databázové modely

Temporální databáze

Řešitelé (abecedně): Jaromír Ocelka, Šárka Procházková, Jaroslav Ráček

Obsah:

  1. Co jsou Temporální databáze
  2. Instalace TimeDB
  3. Dotazovací jazyk TemporalSQL
  4. Časové údaje v databázi
  5. Časová období, intervaly a události - aritmetika
  6. Časová období, intervaly a události - porovnávání
  7. Aritmetické výrazy
 

1. Co jsou Temporální databáze

Temporální databáze jsou takové databáze, které jsou schopny uchovávat časový údaj. Podrobnější popis, včetně rozdílu mezi temporálními a netemporálními databázemi, je možné najít na http://www.timeconsult.com/TemporalData/TemporalDB.html.

 

2. Instalace TimeDB

TimeDB je temporalní nastavba nad klasické relační databaze. Toto rozhraní bylo původně napsáno v jazyce Prolog včetně simulace relační databaze. V první verzi pro instalaci postačovala funkčí verze prologu - nebylo potřeba žádného dalšího softwaru. Vykon tohoto produktu byl zanedbatelný neboť simulace databaze programem, jenž je interpretován je nesrovnatelná s databázovými porodukty jako je Oracle, Informix, ... . Druhá verze je již pouze rozhraní mezi příkazy TimeSQL a relační databázi. V této verzi je zahrnuto i jednoduchý demostrační klient, který je zároveň ukázkou komunikace s rozhraním TimeDB.

Demostrační verzi je možné si zkopirovat na stránkách autora - firma TimeConsult (www.timeconsult.com). Tento systém je určen pro relační databaze Oracle, Sybase a planuje se podpora pro MS Access a Informix a další. Jelikož je TimeDB napsána v platformově nezávislém jazyce java a zároveň přistupuje do databáze jako obyčejný klient je možné jej provozovat na jakekoliv platformě (i rozdílnné od platformy relační databáze), na které existuje kompilace Java Virtual Machine. Komunikaci mezi relační databází a javou zajišťuje JDBC Driver.

Java Virtual Machine lze nalézt na serveru firmy Sun microsystems (www.javasoft.com). Zde je zdarma nabízen program Java Runtime Environment (JRE 1.2, JRE 1.1) jenž je potřebný pro instalaci provoz TimeDB2.

Potřebný JDBC Driver je největší problém. Pro každou relační databázi je většinou jiný ovladač. Jelikož jsme TimeDB2 testovali na databazi Oracle obrátili jsme se na server www.oracle.com. Serie odkazů Free Download/Free Software/ Oracle 7 JDBC OCI and JDBC Thin Drivers nás zavedla na stranku s JDBC ovladači pro databázi Oracle verze 7. Ovladače pro Windowns NT 4.0/Win95 jsme testovali jako první a funguji docela spolehlivě, ale obsahují binární knihovny určené pouze pro MS Windows na procesoru Intel. Druhý testovaný ovladač je označen názvem All Unix Platforms. Tento ovladač je napsán v čisté javě a jej možné jej použít nejenom na Unix platformě, ale i v MS Windows. Po zvolení ovladače je nutné provést zaregistrování a odsouhlasení s licencí. Po té se objeví stránka s odkazem na soubor. V případě ovladače pro All Unix Platforms je to odkaz na ftp serveru Oracle ftp://ftp.oracle.com/pub/www/jdbc/v7/jdbcthin.tar.

Produkt jsme tedy testovali na relační databázi Oracle v.7 na počítači aisa.fi.muni.cz. Jako klient nám sloužili nejprve pracovní stanice MS Windows NT 4.0 s JDBC ovladačem určeným pouze pro MS Windows. Pro provoz javy jsme použili JRE 1.2. Dále jsme produkt vyzkoušeli na na počítačích s operačními systémy UNIX na FI MU. Zde nám posloužil ovladač JDBC pro All Unix Platforms. A na závěr ovladač pro All Unix Platforms ukázal svou bezproblémovou funkčnost i na MS Windows.

Pro případné zájemce jsme zřidili dočasné zrcadlo s TimeDB2.0D a JDBC ovladačem All Unix Platforms v adresáři /home/ocelka/disk2/TimeDB na počítači aisa. Nyní popíšeme instalační proces pro UNIXové platformy na FI MU:

# provedeme rozbaleni z archivu do adresare /tmp/timedb

export ZDROJ=~ocelka/disk2/TimeDB
mkdir /tmp/timedb
cd /tmp/timedb
cp  $ZDROJ/*.gz /tmp/timedb
cp  $ZDROJ/*.tar /tmp/timedb

# rozbaleni TimeDB2
gzip -d TimeDB2.0D.tar.gz
tar -xvf TimeDB2.0D.tar

# rozbaleni JDBC ovladace
tar -xvf jdbcthin.tar

# pro beh programu nam postacuje knihovna s tridami
cp lib/classes111.zip TimeDB2.0D/oracle_jdbc.zip


# potrebne soubory s programem si prekopirujeme do
# domovskeho adresare do adresare TimeDB2.0D
cp -r TimeDB2.0D ~

# pomocny adresar smazeme
yes | rm -r /tmp/timedb

cd ~/TimeDB2.0D

# a pokud se nam nechce nastavovat parametry
# JDBC ovladace pomoci dialogoveho okna
# pouzije preddefinovany konfiguracni soubor
cp $ZDROJ/prefs ~/TimeDB2.0D
Ještě si v adresáři ~/TimeDB2.0D vytvoříme startovací skript se jménem start_time_db:
#!/bin/bash
case `uname -n` in
  anxur | oreias* | artemis )
        module add java1.2  # pro SunOs
        ;;
  * )
        module add java1.1  # pro ostatni
        ;;
esac
cd ~/TimeDB2.0D
java -classpath oracle_jdbc.zip:classes TimeDB
a nastavíme práva na spuštění:
chmod a+x ~/TimeDB2.0D/start_time_db
Spuštění TimeDB je nyní jednoduché: ~/TimeDB2.0D/start_time_db

Po spuštění si ještě zkontrolujeme nastavení programu. V menu TimeDB/Preferences by mělo být

Application dir:/tmp/
JDBC Driver: oracle.jdbc.driver.OracleDriver
JDBC URL: jdbc:oracle:thin:@aisa.fi.muni.cz:1521:prod
DBMS:Oracle
Položku Application dir změníme na adresář s vlastní instalací TimeDB. Například /home/ocelka/TimeDB2.0D/. Konfiguraci potvrdíme pomocí Save a uložíme ukončením programu.

Před vlastním započetím práce je nutné nejprve provést připojení k databázi Oracle pomocí TimeDB/Open DB. Po prvním připojení je nutné vytvořit databázi (sada tabulek) pro TimeDB pomocí TimeDB/Create DB. Po vytvoření databáze mužeme příkazy zadávat do textové oblasti a spouštět tlačitkem execute, nebo napsat dávku příkazů do souboru a provést jejich spuštění pomocí menu File/Execute File. Veškeré textové výstupy se provádějí do terminálu z nějž jsme spustili TimeDB.

 

3. Dotazovací jazyk TemporalSQL

Databáze TimeDB 2.0 používá vlastní dotazovací jazyk Temporal SQL. Následuje jeho popis:

statement ::= (query | ddl | dml | control) ';'

Query
-----

timeFlag ::= [ 'nonsequenced' ] 'validtime' [ scalarExp ]
coal ::= '(' 'period' ')'

query ::= [ timeFlag ] queryExp
queryExp ::= queryTerm { ('union' | 'except') queryTerm }
queryTerm ::= queryFactor { 'intersect' queryFactor }
queryFactor ::= '(' query ')' [ coal ] | sfw

sfw ::= 'select' selectItemList
'from' tableRefList
[ 'where' condExp ]

selectItemList ::= '*' | selectItem { ',' selectItem }
selectItem ::= scalarExp [ alias ]

tableRefList ::= tableRef { ',' tableRef }
tableRef ::= '(' query ')' [ coal ] alias [ colList ] |
identifier [ coal ] [ alias ]

alias ::= ['as'] identifier

condExp ::= condTerm { 'or' condTerm }
condTerm ::= condFactor { 'and' condFactor }
condFactor ::= [ 'not' ] simpleCondFactor
simpleCondFactor ::= '(' condExp ')' |
scalarExp condOp scalarExp

condOp ::= '<' | '>' | '<=' | '>=' | '<>' | '=' |
'precedes' | 'overlaps' | 'meets' | 'contains'


scalarExp ::= term { ('+' | '-') term }
term ::= factor { ('*' | '/') factor }
factor ::= [ ('+' | '-') ] simpleFactor
simpleFactor ::= colRef |
const |
'(' scalarExp ')' |
'abs' '(' scalarExp ')'

colRef ::= identifier [ '.' identifier ]

const ::= integer |
float |
''' string ''' |
interval |
event |
span

interval ::= 'validtime' '(' identifier ')' |
'period' intervalExp |
'period' '(' scalarExp ',' scalarExp ')'
intervalExp ::= '[' time '-' time ')'
time ::= timeDBDate | eventExp

event ::= ( 'begin' | 'end' ) '(' scalarExp ')' |
( 'first' | 'last' ) '(' scalarExp ',' scalarExp ')' |
eventExp

eventExp ::= 'now' |
'beginning' |
'forever' |
'date' dateString |
'date' timeDBDate |
'timestamp' timestampString

dateString ::= ''' YYYY '-' MM '-' DD '''
timestampString ::= ''' YYYY '-' MM '-' DD ' ' HH ':' MM ':' SS '''
timeDBDate ::= YYYY [ '/' MM [ '/' DD
[ '~' HH [ ':' MM [ ':' SS ]]]]]

span ::= 'interval' spanExp
spanExp ::= integer qualifier { integer qualifier }
qualifier ::= 'year' |
'month' |
'day' |
'hour' |
'minute' |
'second'

Data Definition
---------------

ddl ::= ddlTable | 'drop' 'table'

ddlTable ::= 'create' 'table' identifier '(' colDefList ')' [ 'as' 'validtime' ]

colDefList ::= colDef { ',' colDef }
colDef ::= identifier dataType

colList ::= col { ',' col }
col ::= identifier

dataType ::= 'number' [ typeLength ] | /* Oracle */
'numeric' [ typeLength ] | /* Sybase */
'smallint' | /* Cloudscape's JBMS */
'longint' | /* Cloudscape's JBMS */
'integer' |
'real' |
'float' |
'interval' |
'date' |
'period' |
'char' [ typeLength ] |
'varchar' [ typeLength ]

typeLength ::= '(' integer ')'

Data Manipulation
-----------------

dml ::= [ timeFlag ] insert

insert ::= 'insert' 'into' identifier valExp
valExp ::= 'values' '(' valList ')'

Control
-------

control ::= 'commit' | 'rollback'

 

4. Časové údaje v databázi

Při pokusech s databází jsme se především zaměřili na oblast týkající se časových údajů, které databáze uchovává.

Příklad 1.

Vtomto příkladu jde o práci nad tabulkou student a tabulkou koleje. Tabulka student obsahuje seznam studentů, kteří studují na škole. Pro náš příklad postačí, když se jako údaje o jednotlivém studentovi bude ukládat jeho jméno, přijmení a obor studia. Tabulka koleje obsahuje seznam osob ubytovaných na kolejích. Ke každé osobe se ukládá její jméno, příjmení a číslo pokoje.

/*Vytvoreni tabulky studentu - jmeno, prijmeni a obor studia*/

CREATE TABLE student(JMENO varchar(15), PRIJMENI varchar(20), studium varchar(10)) AS
VALIDTIME;
/*Vytvoreni tabulky ubytovanych na kolejich - jmeno, prijmeni a cislo pokoje*/
CREATE TABLE koleje(JMENO varchar(15), PRIJMENI varchar(20), pokoj integer) AS VALIDTIME;
/*Ruzne zpusoby vkladani do tabulky student*/
VALIDTIME PERIOD [1992-1997) INSERT INTO student VALUES ('Jan', 'Novak', 'ucitelske');
VALIDTIME PERIOD [now-2005) INSERT INTO student VALUES ('Petr', 'Dvorak', 'ucitelske');
VALIDTIME PERIOD [1992-forever) INSERT INTO student VALUES ('Jiri', 'Novak', 'ucitelske');
VALIDTIME PERIOD [1992-1993) INSERT INTO student VALUES ('Tomas', 'Dolezal', 'odborne');
INSERT INTO student VALUES ('Tomas', 'Dolezal', 'odborne');
VALIDTIME PERIOD [1998/1/1~10:36:14-forever) INSERT INTO student VALUES ('Jan', 'Novak', 'odborne');
VALIDTIME PERIOD [now-forever) INSERT INTO student VALUES ('Libor', 'Tesar', 'odborne');
/*Ruzne zpusoby vkladani do tabulky koleje*/
VALIDTIME PERIOD [1992-1997) INSERT INTO koleje VALUES ('Pavel', 'Bures', 204);
VALIDTIME PERIOD [1999/1/1~10:36:14-forever) INSERT INTO koleje VALUES ('Jan', 'Novak', 814);
INSERT INTO koleje VALUES ('Petr', 'Dvorak', 204);
VALIDTIME PERIOD [now-forever) INSERT INTO koleje VALUES ('Pavel', 'Svoboda', 206);
/*Vypis tabulek bez casovych udaju*/
NONSEQUENCED VALIDTIME
SELECT *
FROM student;

NONSEQUENCED VALIDTIME
SELECT *
FROM koleje;
/*Vypis tabulek s casovymi udaji*/
VALIDTIME
SELECT *
FROM student;

VALIDTIME
SELECT *
FROM koleje;
/*Vypis studenty, kteri ve stejne dobe studovali a bydleli na kolejich, vcetne casoveho intervalu, kdy k tomu doslo*/
VALIDTIME
SELECT *
FROM student, koleje
WHERE student.jmeno = koleje.jmeno AND student.prijmeni = koleje.prijmeni;
/*Vypis vsech dvojic studentu, kteri studovali ve stejne dobe, vcetne casoveho intervalu*/
VALIDTIME SELECT * FROM student (PERIOD) a1, student (PERIOD) a2;
/*Totez bez udani casu*/
NONSEQUENCED VALIDTIME SELECT * FROM student (PERIOD) a1, student (PERIOD) a2;
/*Vypis studentu, kteri studovali v letech 1990-1994*/
VALIDTIME PERIOD [1990-1994)
SELECT * FROM student;
/*zruseni obou tabulek*/
DROP TABLE koleje;

Příklad 2.

Na příkladu různých státních útvarů, které existovaly na území Čech, Polska a Slovenska jsou ukázány rozdíly mezi používáním VALIDTIME a NONSEQUENCED VALIDTIME.

/*Vytvoreni tabulky statu, ktera obsahuje nazev statu a hlavni mesto*/

CREATE TABLE stat (nazev char(20), mesto char(20)) AS VALIDTIME;
/*Naplneni tabulky udaji*/
NONSEQUENCED VALIDTIME PERIOD [1198-1621)
      INSERT INTO stat VALUES ('Ceske kralovstvi', 'Praha');
NONSEQUENCED VALIDTIME PERIOD [1918-1939)
      INSERT INTO stat VALUES ('Ceskoslovensko', 'Praha');
NONSEQUENCED VALIDTIME PERIOD [1945-1993)
      INSERT INTO stat VALUES ('Ceskoslovensko', 'Praha');
NONSEQUENCED VALIDTIME PERIOD [1993-forever)
      INSERT INTO stat VALUES ('Ceska republika', 'Praha');
NONSEQUENCED VALIDTIME PERIOD [1940-1945)
      INSERT INTO stat VALUES ('Slovensko', 'Bratislava');
NONSEQUENCED VALIDTIME PERIOD [1993-forever)
      INSERT INTO stat VALUES ('Slovensko', 'Bratislava');
NONSEQUENCED VALIDTIME PERIOD [1025-1040)
      INSERT INTO stat VALUES ('Polsko', 'Gniezno');
NONSEQUENCED VALIDTIME PERIOD [1040-1596)
      INSERT INTO stat VALUES ('Polsko', 'Krakov');
NONSEQUENCED VALIDTIME PERIOD [1596-1795)
      INSERT INTO stat VALUES ('Polsko', 'Varsava');
NONSEQUENCED VALIDTIME PERIOD [1918-1939)
      INSERT INTO stat VALUES ('Polsko', 'Varsava');
NONSEQUENCED VALIDTIME PERIOD [1945-forever)
      INSERT INTO stat VALUES ('Polsko', 'Varsava');
/*Nasleduje 5 prikazu pro vypis z tabulek:*/
/*Vypise nazvy statu, ktere v soucasnosti existuji*/
SELECT nazev, mesto FROM stat;
/*Vypise nazvy vsech statu, ktere nekdy existovaly*/
NONSEQUENCED VALIDTIME SELECT nazev, mesto FROM stat;
/*Vypise nazvy vsech statu, ktere nekdy existovaly.*/
NONSEQUENCED VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat;
/*Vypise nazvy vsech nazvu statu, ktere nekdy existovaly, a uvede cas*/
VALIDTIME SELECT nazev, mesto FROM stat;
/*Vypise nazvy vsech nazvu statu, ktere existovaly v letech 1400-1700, a uvede cas*/
VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat;
/*Nyni techto pet prikazu budeme ruzne modifikovat:*/
/*To stejne v zavorkach - zadna zmena*/
(SELECT nazev, mesto FROM stat);
(NONSEQUENCED VALIDTIME SELECT nazev, mesto FROM stat);
(NONSEQUENCED VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
(VALIDTIME SELECT nazev, mesto FROM stat);
(VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
/*Pridame-li NONSEQUENCED VALIDTIME, vystup se meni*/
/*Neuvadi se casove intervaly tam, kde se drive uvadely*/
/*Prvni z prikazu vypise vsechny staty*/
NONSEQUENCED VALIDTIME
     (SELECT nazev, mesto FROM stat);
NONSEQUENCED VALIDTIME
     (NONSEQUENCED VALIDTIME SELECT nazev, mesto FROM stat);
NONSEQUENCED VALIDTIME
     (NONSEQUENCED VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
NONSEQUENCED VALIDTIME
     (VALIDTIME SELECT nazev, mesto FROM stat);
NONSEQUENCED VALIDTIME
     (VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
/*Pridame-li PERIOD [1600-1990), vypisi se stejne staty jako v predchozim pripade*/
NONSEQUENCED VALIDTIME PERIOD [1600-1990)
     (SELECT nazev, mesto FROM stat);
NONSEQUENCED VALIDTIME PERIOD [1600-1990)
     (NONSEQUENCED VALIDTIME SELECT nazev, mesto FROM stat);
NONSEQUENCED VALIDTIME PERIOD [1600-1990)
     (NONSEQUENCED VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
NONSEQUENCED VALIDTIME PERIOD [1600-1990)
     (VALIDTIME SELECT nazev, mesto FROM stat);
NONSEQUENCED VALIDTIME PERIOD [1600-1990)
     (VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
/*Pokud vynechame NONSEQUENCED*/
/*Vypise nazvy vsech statu, ktere nekdy existovaly*/
VALIDTIME
     (SELECT nazev, mesto FROM stat);
/*Vypise nazvy vsech statu, ktere nekdy existovaly*/
VALIDTIME
     (NONSEQUENCED VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
/*Vypise nazvy vsech statu, ktere nekdy existovaly*/
VALIDTIME
     (VALIDTIME SELECT nazev, mesto FROM stat);
/*Vypise nazvy vsech statu, existovaly v letech 1400-1700*/
VALIDTIME
     (VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
/*Pridame-li PERIOD [1600-1990)*/
/*Zmeni se polozka valid*/
/*Vypise nazvy vsech statu, ktere existovaly 1600-1900*/
VALIDTIME PERIOD [1600-1990)
     (SELECT nazev, mesto FROM stat);
/*Vypise nazvy vsech statu, ktere nekdy existovaly*/
VALIDTIME PERIOD [1600-1990)
     (NONSEQUENCED VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
/*Vypise nazvy vsech statu, ktere existovaly 1600-1990*/
VALIDTIME PERIOD [1600-1990)
     (VALIDTIME SELECT nazev, mesto FROM stat);
/*Vypise nazvy vsech statu, existovaly v letech 1600-1700*/
VALIDTIME PERIOD [1600-1990)
     (VALIDTIME PERIOD [1400-1700) SELECT nazev, mesto FROM stat);
/*Zruseni tabulky*/
DROP TABLE stat;

 

5. Časová období, intervaly a události - aritmetika

Temporální databáze umí rovněž základní aritmetické operace s časovými atributy, jako jasou intervaly, časové období nebo událost v čase, reprezentovaná datem. Základní aritmetiku ukáže následující příklad.
Nejprve vytvoříme tabulku s těmito atributy:
create table timeTable(s interval, i period, e date);
Následně ji naplníme těmito vzorovými daty:
insert into timeTable
  values (interval 3 year 2 month, period [1989-1991), date '1970-01-01');
insert into timeTable
  values (interval 2 month 3 minute, period [1989-1991), date '1989-06-12');
insert into timeTable
  values (interval 2 second, period [1989-1991), date '1989-06-12');
Pro úplnost je třeba uvést, že první den v roce (tedy např. datum '1970-01-01') je reprezentován pouze rokem (tedy 1970), totéž platí pro první den v měsísi (např. 1973/3/1), který je prazentován rokem a dnem (tedy 1973/3).

Na příkladě nyní ukažme, jak lze časové intervaly sčítat, odčítat, vynásobit nebo vydělit nějakým číslem.

Sčítání a odčítání

Pro sečtení časových intervalů můžeme položit například následující dotaz:
select a1.s + a2.s
from timeTable a1, timeTable a2;
Každý interval z tabulky se zvýší postupně o vše každý z intervalů z téže tabulky.
Výsledek je tedy následující:
a1.s + a2.s
---------------------------
6 year  4 month
3 year  4 month  3 minute
3 year  2 month  2 second
3 year  4 month  3 minute
4 month 6 minute
2 month 3 minute 2 second
3 year  2 month  2 second
2 month 3 minute 2 second
4 second
Pro odčítání bude situace podobná, možné jsou i záporné hodnoty. Položíme například dotaz:
select a1.s - a2.s
from timeTable a1, timeTable a2;
Jeho výsledkem je:
a1.s - a2.s
---------------------------------------------------------
0 second
-2 year -11 month -29 day -23 hour -57 minute
-3 year -1  month -29 day -23 hour -59 minute -58 second
 2 year  11 month  29 day  23 hour  57 minute
0 second
-2 month -2 minute -58 second
 3 year   1 month  29 day 23 hour  59 minute  58 second
 2 month  2 minute 58 second
0 second

Násobení a dělení číslem

Časový interval lze též vynásobit nebo vydělit nějakým číslem.
Ukažme dvojnásobení intervalu:
select 2 * a1.s
from timeTable a1;
Výsledkem je:
2 * a1.s
-------------------
6 year  4 month
4 month 6 minute
4 second
Dále ukažme vydělení dvěmi, tj. požadujeme poloviční interval:
select a1.s / 2
from timeTable a1;
Výsledkem je:
a1.s / 2
----------------------------
1 year  7 month
1 month 1 minute 30 second
1 second

Přičítání a odečítání intervaku k (od) události

K události, která je reprezentována datem, můžeme přičíst (resp. odečíst) časový interval. Výsledkem je datum, zvětšené (resp. zmenšené) o příslušný interval.
Je-li intervalem rok, měsíc nebo den, je výsledné datum ve tvaru rrrr/mm/dd. Měsíc a den je uveden pouze pokud je v intervalu nebo v datu uveden.
Je-li navíc intervalem hodina, minuta nebo sekunda, je výsledné datum ve tvaru rrrr/mm/dd~hh:mm:ss. Minuty a sekundy jsou uvedeny pouze pokud jsou v intervalu nebo v datu.
Vše dostatečně ukazují následující příklady:
Přičtení intervalu k datu:
select a1.e + a2.s
from timeTable a1, timeTable a2;
Výsledek:
a1.e + a2.s
-----------------
1973/3
1992/8/12
1992/8/12
1970/3/1~0:3
1989/8/12~0:3
1989/8/12~0:3
1970/1/1~0:0:2
1989/6/12~0:0:2
1989/6/12~0:0:2
Odečtení intervalu od data:
select a1.e - a2.s
from timeTable a1, timeTable a2;
Výsledek:
a1.e - a2.s
---------------------
1966/11
1986/4/12
1986/4/12
1969/10/30~23:57
1989/4/11~23:57
1989/4/11~23:57
1969/12/30~23:59:58
1989/6/11~23:59:58
1989/6/11~23:59:58

 

6. Časová období, intervaly a události - porovnávání

Časová období, intervaly a události můžeme porovnávat, klást dotazy na to, zda některý předchází nebo je následuje jiný, který je částí druhého nebo se s druhým překrývá.
Pro ukázkové příklady bude použita stejná tabulka jako v předchozím příkladě.

Rovnost časových období nebo událostí

Následujícím dotazem si necháme vypsat takové záznamy, jejichž časová období nebo data jsou si rovna.
Rovnost období:
select *
from   timeTable a1, timeTable a2
where  a1.i = a2.i;
Výsledek dotazu:
s                 i           e         s                 i           e
---------------------------------------------------------------------------------
3 year 2 month    [1989-1991) 1970      3 year 2 month    [1989-1991) 1970
3 year 2 month    [1989-1991) 1970      2 month 3 minute  [1989-1991) 1989/6/12
3 year 2 month    [1989-1991) 1970      2 second          [1989-1991) 1989/6/12
2 month 3 minute  [1989-1991) 1989/6/12 3 year 2 month    [1989-1991) 1970
2 month 3 minute  [1989-1991) 1989/6/12 2 month 3 minute  [1989-1991) 1989/6/12
2 month 3 minute  [1989-1991) 1989/6/12 2 second          [1989-1991) 1989/6/12
2 second          [1989-1991) 1989/6/12 3 year 2 month    [1989-1991) 1970
2 second          [1989-1991) 1989/6/12 2 month 3 minute  [1989-1991) 1989/6/12
2 second          [1989-1991) 1989/6/12 2 second          [1989-1991) 1989/6/12
Rovnost událostí (reprezentovaných datem):
select *
from   timeTable a1, timeTable a2
where  a1.e = a2.e;
Výsledek:
s                 i           e         s                 i           e
---------------------------------------------------------------------------------
3 year 2 month    [1989-1991) 1970      3 year 2 month    [1989-1991) 1970
2 month 3 minute  [1989-1991) 1989/6/12 2 month 3 minute  [1989-1991) 1989/6/12
2 month 3 minute  [1989-1991) 1989/6/12 2 second          [1989-1991) 1989/6/12
2 second          [1989-1991) 1989/6/12 2 month 3 minute  [1989-1991) 1989/6/12
2 second          [1989-1991) 1989/6/12 2 second          [1989-1991) 1989/6/12

Předcházející časové období nebo datum

Pod pojmem předcházející časové období nebo datum rozumíme takové, které skončilo dříve než začalo druhé, znamená to tedy, že se nepřekrývají.
Do tabulky přidejme tyto záznamy:
insert into timeTable
  values (interval 3 year 2 month, period [1979-1987), date '1965-02-03');
insert into timeTable
  values (interval 2 second, period [1986-1991), date '1980-06-12');
Nyní se můžeme dotázat například na to, která období předchází jinému období.
select *
from   timeTable a1, timeTable a2
where  a1.i precedes a2.i;
Výsledkem je:
s                 i           e         s               i           e
------------------------------------------------------------------------------
3 year 2 month    [1989-1991) 1970      3 year 2 month  [1979-1987) 1965/2/3
2 month 3 minute  [1989-1991) 1989/6/12 3 year 2 month  [1979-1987) 1965/2/3
2 second          [1989-1991) 1989/6/12 3 year 2 month  [1979-1987) 1965/2/3
Dalším dotazem může být, která událost (tedy datum) je dřívější.
select *
from   timeTable a1, timeTable a2
where  a1.e precedes a2.e;
Výsledkem jsou takové záznamy, kde první datum (první sloupeček e) následuje druhé datum (druhý sloupeček e).
s                 i           e         s               i           e
-------------------------------------------------------------------------------
2 second          [1986-1991) 1980/6/12 3 year 2 month  [1979-1987) 1965/2/3
2 second          [1986-1991) 1980/6/12 3 year 2 month  [1989-1991) 1970
3 year 2 month    [1989-1991) 1970      3 year 2 month  [1979-1987) 1965/2/3
2 month 3 minute  [1989-1991) 1989/6/12 3 year 2 month  [1979-1987) 1965/2/3
2 month 3 minute  [1989-1991) 1989/6/12 2 second        [1986-1991) 1980/6/12
2 month 3 minute  [1989-1991) 1989/6/12 3 year 2 month  [1989-1991) 1970
2 second          [1989-1991) 1989/6/12 3 year 2 month  [1979-1987) 1965/2/3
2 second          [1989-1991) 1989/6/12 2 second        [1986-1991) 1980/6/12
2 second          [1989-1991) 1989/6/12 3 year 2 month  [1989-1991) 1970
Nyní provedeme dotaz na to, který interval předchází události.
select *
from   timeTable a1, timeTable a2
where  a1.i precedes a2.e;
Výsledkem jsou záznamy, kde interval (druhý sloupeček i) skončil dříve než nastala událost (první sloupeček e).
s                 i           e         s               i           e
------------------------------------------------------------------------------
2 month 3 minute  [1989-1991) 1989/6/12 3 year 2 month  [1979-1987) 1965/2/3
2 second          [1989-1991) 1989/6/12 3 year 2 month  [1979-1987) 1965/2/3
Dalším dotazem jedotaz opačný, tedy která událost předchází intervalu.
select *
from   timeTable a1, timeTable a2
where  a1.e precedes a2.i;
Výsledkem jsou záznamy, kde událost (druhý sloupeček e) nastala dříve než doba intervalu (první sloupeček i).
s                 i           e         s               i           e
-------------------------------------------------------------------------------
3 year 2 month    [1979-1987) 1965/2/3  3 year 2 month  [1979-1987) 1965/2/3
3 year 2 month    [1979-1987) 1965/2/3  3 year 2 month  [1989-1991) 1970
2 second          [1986-1991) 1980/6/12 3 year 2 month  [1979-1987) 1965/2/3
2 second          [1986-1991) 1980/6/12 2 second        [1986-1991) 1980/6/12
2 second          [1986-1991) 1980/6/12 3 year 2 month  [1989-1991) 1970
3 year 2 month    [1989-1991) 1970      3 year 2 month  [1979-1987) 1965/2/3
3 year 2 month    [1989-1991) 1970      2 second        [1986-1991) 1980/6/12
3 year 2 month    [1989-1991) 1970      3 year 2 month  [1989-1991) 1970
2 month 3 minute  [1989-1991) 1989/6/12 3 year 2 month  [1979-1987) 1965/2/3
2 month 3 minute  [1989-1991) 1989/6/12 2 second        [1986-1991) 1980/6/12
2 month 3 minute  [1989-1991) 1989/6/12 3 year 2 month  [1989-1991) 1970
2 second          [1989-1991) 1989/6/12 3 year 2 month  [1979-1987) 1965/2/3
2 second          [1989-1991) 1989/6/12 2 second        [1986-1991) 1980/6/12
2 second          [1989-1991) 1989/6/12 3 year 2 month  [1989-1991) 1970

Následující časové období nebo datum

TimeDB neumí pracovat s následujícím časovým obdobím, místo výrazu precedes tedy nelze použít follows.
Tento případ však lze snadno obejít takto: budeme-li požadovat, aby např. a1 follows a2, můžeme použít a2 precedes a1.

Časové období přesahuje druhé

To znamená, že první období začalo dříve než druhé skončilo a zároveň skončilo později než druhé začalo.
Pro příklad použijeme tabulku
create table timeTable(s interval, i period, e date);
Naplníme ji těmito daty:
insert into timeTable
  values (interval 3 year 2 month, period [1979-1987), date '1965-02-03');
insert into timeTable
  values (interval 2 second, period [1986-1988), date '1980-06-12');
insert into timeTable
  values (interval 2 second, period [1989-1991), date '1989-06-12');
Nyní můžeme položit následující dotaz:
select *
from   timeTable a1, timeTable a2
where  a1.i overlaps a2.i;
Výsledek je:
s               i           e         s               i           e
-----------------------------------------------------------------------------
3 year 2 month  [1979-1987) 1965/2/3  3 year 2 month  [1979-1987) 1965/2/3
3 year 2 month  [1979-1987) 1965/2/3  2 second        [1986-1988) 1980/6/12
2 second        [1986-1988) 1980/6/12 3 year 2 month  [1979-1987) 1965/2/3
2 second        [1986-1988) 1980/6/12 2 second        [1986-1988) 1980/6/12
2 second        [1989-1991) 1989/6/12 2 second        [1989-1991) 1989/6/12

Časové období obsahuje jiné nebo obsahuje datum

To znamená, že první období je "podobdobím" druhého nebo se určité datum nachází uvnitř časového období.
Tabulku vytvoříme stejným způsobem jako v předchozím případě a naplníze těmito záznamy:
insert into timeTable
  values (interval 3 year 2 month, period [1979-1997), date '1975-02-03');
insert into timeTable
  values (interval 2 second, period [1986-1991), date '1980-06-12');
insert into timeTable
  values (interval 2 second, period [1986-1988), date '1987-06-12');
Dotaz na podinterval:
select *
from   timeTable a1, timeTable a2
where  a1.i contains a2.i;
Výsledek:
s        i           e         s               i           e
----------------------------------------------------------------------
2 second [1986-1991) 1980/6/12 3 year 2 month  [1979-1997) 1975/2/3
2 second [1986-1988) 1987/6/12 3 year 2 month  [1979-1997) 1975/2/3
2 second [1986-1988) 1987/6/12 2 second        [1986-1991) 1980/6/12
Po následujím dotazu by se zdála pravděpodobná odpověď, že rok 1980 je obsažen v období [1979-1997) a rok 1987 je v [1986-1988).
select *
from   timeTable a1, timeTable a2
where  a1.i contains a2.e;
Výsledkem je bohužel
No rows selected

 

7. Aritmetické výrazy

V dotazech je možno použít aritmetické výrazy, jako sčítání, odčítání, násobení, dělení nebo absolutní hodnotu.
Než uvedeme, jak databáze s těmito výrazy pracuje, vytvoříme si pro tento účel následující tabulku
create table test (i integer, r real, f float) as validtime;
a naplníme daty:
validtime period [1980-1990)
  insert into test values(1, 1.23, 1.235);
validtime period [1970-1995)
  insert into test values(1.9, 5.23875, 11.235);
validtime period [1990-2001)
  insert into test values(1.9, 5.23875, 11.235);
Nyní uvádíme některé příklady:
validtime select 3*4+12 from test;
Výsledek je:
valid       3 * 4 + 12
------------------------
[1980-1990) 24
[1970-1995) 24
[1990-2001) 24
nebo např. dotaz s absolutní hodnotou:
validtime select abs(3*4+12-25) from test;
Výsledek:
valid       abs(3 * 4 + 12 - 25)
----------------------------------
[1980-1990) 1
[1970-1995) 1
[1990-2001) 1
Pokud nechceme, aby se v záhlaví výsledku objevoval výraz abs(3 * 4 + 12 - 25), můžeme si sloupeček označit např. symbolem a. Pak by dotaz vypadal takto:
validtime select abs(3*4+12-25) a from test;
Výsledek je stejný, liší se jen pojmenování sloupečku.
valid       a
---------------
[1980-1990) 1
[1970-1995) 1
[1990-2001) 1
V dotazu lze použít rovněž jména atributů:
validtime select abs(3*4+r-25) from test;
Výsledek pak vypadá:
valid       abs(3 * 4 + r - 25)
---------------------------------
[1980-1990) 11.77
[1970-1995) 7.761250
[1990-2001) 7.761250
nebo jiný příklad:
validtime select i+r+f a from test;
Výsledkem je:
valid       a
-----------------------
[1980-1990) 3.4650
[1970-1995) 18.473750
[1990-2001) 18.473750