picoSQL Guida di riferimento del linguaggio di interrogazione Release 2.0.2 Copyright © 2003 Picosoft s.r.l. - Corso Italia 178 - Pisa Italy Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation. The GNU Free Documentation License is available from www.gnu.org. picoSQL - linguaggio SQL 1/28 picoSQL - Guida di riferimento al linguaggio SQL Questo manuale illustra tutti i comandi SQL supportati da picoSQL con la relativa sintassi e una descrizione. Lo SQL supportato da picoSQL si rifà allo standard ANSI-92 e in particolare alla CAE (Common Application Environment) Specification "Structured Query Language" X/open del 1992. Rispetto agli standard suddetti ci sono alcune limitazioni e alcune estensioni che saranno evidenziate nel testo. 1) Convenzioni sintattiche In questo manuale sono adottate le seguenti convenzioni per la descrizione della sintassi SQL. Tutte le parole riservate sono scritte in lettere maiuscole, mentre gli elementi che l'utente deve rimpiazzare con identificatori o espressioni sono scritti in lettere minuscole. Questa è solo una convenzione per distinguere meglio i vari elementi che compongono un comando. picoSQL non fa distinzione tra maiuscoli e minuscoli, sia nelle parole riservate che nei nomi utente, per cui il comando "SELECT PIPPO ..." è equivalente a "Select Pippo ..." e a "select pippo ...". Porzioni di comando opzionali vengono racchiuse tra parentesi quadre []. Le barre verticali separano opzioni mutuamente esclusive. Le parentesi graffe {} racchiudono opzioni diverse, separate da una barra verticale, tra le quali una deve essere scelta. N.B. Le parentesi graffe vengono usate nel linguaggio come sequenze di escape (vedi nel seguito) per cui in tal caso debbono essere riportate come appaiono. Nel seguito è sempre rimarcato l'uso delle graffe come sequenza di escape in modo da evitare ambiguità. I puntini (...) indicano che la sintassi racchiusa nelle parentesi quadre o graffe precedenti può essere ripetuta un numero indefinito di volte. Tutti gli altri caratteri, come le parentesi tonde, sono elementi sintattici dell'SQL e debbono essere riportati come appaiono. 2) Elementi del linguaggio Riportiamo in questo paragrafo una lista degli elementi basilari del linguaggio SQL. numeri Un numero è una sequenza di cifre seguita opzionalmente da una parte decimale e opzionalmente preceduto da un segno negativo. Sono ammessi numeri in notazione esponenziale a base 10 posponendo la lettera E seguita dal valore dell'esponente. Sono numeri validi, per esempio: 523 ­43.001 7.8E5 3.4E­3 picoSQL - linguaggio SQL 2/28 stringhe Una stringa è una qualsiasi sequenza di caratteri racchiusa tra apici singoli o doppi (SQL standard prevede solo apici singoli). Per inserire il carattere usato per racchiudere la stringa nella stringa stessa è sufficiente ripeterlo due volte. Le seguenti stringhe sono, per esempio, equivalenti: "L'opera" 'L''opera' sequenze di escape Nel caso si voglia indicare dei valori non ascrivibili direttamente al gruppo dei numeri o delle stringhe, come per esempio le date, si può utilizzare una sequenza di escape che identifica il tipo in modo univoco. picoSQL usa le parentesi graffe per racchiudere questi valori. Le sequenze di escape riconosciute da picoSQL sono le seguenti: {d 'AAAA­MM­GG'} per indicare una data; {t 'oo:mm:ss.uuu'} per indicare un'ora del giorno; {ts 'AAAA­MM­GG oo:mm:ss.uuu'} per indicare un'ora di un determinato giorno (timestamp); valori letterali Un qualsiasi valore stringa, numerico o definito con una sequenza di escape è detto valore letterale (literal). parametri dinamici Se si usa picoSQL da un linguaggio di programmazione, può accadere che certi valori di una query siano variabili e dipendenti dal contesto. Per questo motivo si può preparare una query al database usando il carattere punto interrogativo (?) al posto di un valore letterale. Ovviamente in questi casi è necessario fornire al database il valore voluto prima di eseguire la query tramite un'apposita funzione (SQLBindParameter). identificatori Un identificatore è un nome che identifica un elemento del database, come una tabella o un attributo di una tabella. Il nome deve iniziare con una lettera e può contenere lettere, numeri e caratteri di sottolineatura (_). Per picoSQL negli indentificatori le lettere minuscole sono equivalenti alle relative lettere maiuscole. Un identificatore non può coincidere col nome di una parola riservata. separatori Un comando SQL è formato da un certo numero di parole separate da caratteri particolari detti separatori. Un separatore può avere l'unico scopo di separare le parole oppure avere associato anche un altro significato. Fanno parte del primo tipo i caratteri spazio (ASCII 32), tabulazione (ASCII 9), ritorno carrello (ASCII 13) e nuova linea (ASCII 10). Fanno parte del secondo tipo i caratteri: , ( ) < > . = * + ­ / ? e le coppie di caratteri: <> != >= <= 3) Tipi dato In picoSQL esistono i seguenti tipi di dato. picoSQL - linguaggio SQL 3/28 CHAR(dimensione) VARCHAR( dimensione ) Definisce un attributo stringa di caratteri lunga al massimo dimensione caratteri. Nella release attuale di picoSQL non c'è differenza tra i due tipi in quanto entrambi sono a lunghezza fissa e quindi corrispondenti al tipo CHAR nello standard SQL. La lunghezza può arrivare fino 32767 caratteri, che é anche la massima dimensione di una riga di tabella, mentre lo standard SQL prevede una lunghezza massima di 255 caratteri. NUMERIC(precisione[, decimali]) Definisce un attributo numerico a virgola fissa che può contenere fino a precisione cifre, di cui decimali per la parte decimale. Nella versione attuale la massima precisione ammessa è 18. Lo spazio occupato da ciascun attributo di questo tipo si calcola con la seguente formula spazio-occupato = precisione / 2 + 1 SERIAL SMALLINT INTEGER INT REAL DOUBLE dove la divisione si considera intera. Definisce un attributo numerico intero di 11 cifre, memorizzato nello stesso formato di un NUMERIC, che si incrementa di 1 automaticamente a ogni inserimento, partendo dal valore 1. In inserimento è concesso assegnare a colonne di questo tipo un valore predeterminato; nel caso che tale valore sia maggiore di quelli già esistenti, esso diventa il riferimento per i successivi inserimenti. Una colonna SERIAL può però venir modificata a piacere con un'operazione di aggiornamento (UPDATE) . Definisce un attributo numerico intero di 2 byte il cui valore è compreso tra -32767 e 32767. Il valore -32768 viene considerato come NULL. N.B. i dati di questo tipo sono memorizzati nel formato nativo del computer ospite. Definisce un attributo numerico intero di 4 byte il cui valore è compreso tra -2147483647 e 2147483647. Il valore -2147483648 viene considerato come NULL. INTEGER e INT sono sinonimi. N.B. i dati di questo tipo sono memorizzati nel formato nativo del computer ospite. Definisce un attributo numerico in floating point di 4 byte. N.B. i dati di questo tipo sono memorizzati nel formato nativo (float) del computer ospite per cui i valori minimo e massimo dipendono da quest'ultimo. Il valore più piccolo (FLT_MIN) viene considerato come NULL. Definisce un attributo numerico in floating point di 8 byte. N.B. i dati di questo tipo sono memorizzati nel formato nativo (double) del computer ospite per cui i valori minimo e massimo dipendono da quest'ultimo. Il valore più piccolo (DBL_MIN) viene considerato come NULL. picoSQL - linguaggio SQL 4/28 DATE Definisce un attributo che contiene una data. Occupa 5 byte. Per inviare un dato di questo tipo al database, è necessaria una stringa col seguente formato: 'AAAA-MM-GG' oppure la corretta sequenza di escape. Definisce un attributo che contiene un'ora con approssimazione al millesimo di secondo. Occupa 5 byte. Per inviare un dato di questo tipo al database, è necessaria una stringa col seguente formato: TIME 'oo:mm:ss.uuu' TIMESTAMP oppure una adeguata sequenza di escape. Definisce un attributo che contiene una data e un'ora con approssimazione al millesimo di secondo. Occupa 9 byte. Per inviare un dato di questo tipo al database, è necessaria una stringa col seguente formato: 'AAAA-MM-GG oo:mm:ss.uuu' oppure una adeguata sequenza di escape. Definisce un insieme di byte che occupa uno spazio variabile compreso tra 0 e 2147483648 byte. L'allocazione viene fatta in blocchi di 1024 byte. Quando picoSQL legge un attributo di questo tipo, lo carica in memoria centrale, per cui bisogna essere sicuri di averne a sufficienza. Come il BLOB ma è destinato a contenere testi. BLOB CLOB Ciascuno di questi tipi può contenere un valore convenzionale, chiamato NULL, che indica che il valore è sconosciuto o non applicabile al contesto. Per esempio, quando si inserisce una riga non specificando il valore di alcuni attributi, in questi viene posto il valore NULL. 4) Espressioni Un'espressione rappresenta un singolo valore che può essere ottenuto anche tramite un'operazione. Un'espressione viene definita numerica se il valore che restituisce è un numero. Possono far parte dell'espressione numerica i valori letterali numerici, i parametri dinamici, gli identifiatori di colonna e i risultati delle funzioni (vedi nel seguito) che restituiscono valori numerici. Un'espressione numerica può essere ottenuta combinando questi elementi tramite i 4 operatori aritmetici. Gli operatori consentiti sono, in ordine di precedenza dalla massima alla minima, i seguenti: ­ * / + ­ meno unario; moltiplicazione e divisione; addizione e sottrazione. Le operazioni con uguale precedenza sono eseguite da sinistra a destra. L'ordine di precedenza dell'esecuzione delle operazioni può essere alterato tramite l'utilizzo di parentesi tonde (). Un'espressione viene definita di tipo stringa se il valore che restituisce è una stringa. Un'espressione stringa è formata o da un valore letterale stringa, o da un parametro dinamico, o da un identificatore picoSQL - linguaggio SQL 5/28 di colonna, o infine dal risultato di una funzione stringa (vedi nel seguito). Un'espressione viene definita temporale se restituisce una data, un tempo o un timestamp. Un'espressione temporale è formata o da una sequenza di escape, o da un parametro dinamico, o da un identificatore di colonna. Nella release attuale sono permesse addizioni e sottrazioni tra un numero e un'espressione temporale e il risultato è un'espressione temporale pari a quella di partenza più/meno i secondi specificati dal numero. 5) Funzioni picoSQL ha predefinito una serie di funzioni al proprio interno per rendere più significativi i risultati delle ricerche sul database. Queste funzioni sono principalmente di due tipi: funzioni semplici e funzioni aggreganti. Le funzioni aggreganti vengono applicate sull'insieme dei risultati di una colonna in una ricerca e restituiscono un valore che può essere numerico o stringa. Le funzioni semplici eseguono dei calcoli su uno o più argomenti e restituiscono il risultato che può essere numerico o stringa. Le funzioni semplici possono essere a propria volta distinte in funzioni numeriche, funzioni stringa e funzioni temporali a seconda del tipo di argomento su cui operano. 5.1)Funzioni aggreganti COUNT ([DISTINCT ]nome-colonna) COUNT(*) MIN(nome-colonna) MAX(nome-colonna) SUM(nome-colonna) AVG(nome-colonna) Restituisce il numero di righe che hanno soddisfatto la ricerca. Nel caso sia specificato anche DISTINCT nomecolonna viene restituito il numero di righe che hanno soddisfatto la ricerca senza che il valore contenuto nella colonna specificata sia ripetuto. Restituisce il valore minimo della colonna specificata tra le righe che soddisfano la ricerca. Restituisce il valore massimo della colonna specificata tra le righe che soddisfano la ricerca. Restituisce la somma dei valori della colonna specificata tra le righe che soddisfano la ricerca. La colonna specificata deve essere di tipo numerico. Restituisce la media dei valori della colonna specificata tra le righe che soddisfano la ricerca. La colonna specificata deve essere di tipo numerico. ☞ Rispetto allo standard SQL, picoSQL non permette di usare una funzione aggregata in un'espressione aritmetica. 5.2 Funzioni numeriche Nella descrizione di queste funzioni quando viene riportata la parola espr-num (o espr-num-1, espr-num-2 etc.) si intende un'espressione numerica. picoSQL - linguaggio SQL 6/28 ABS(espr-num) ACOS(espr-num) ASIN(espr-num) ATAN(espr-num) CEILING(espr-num) Restituisce il valore assoluto dell'espressione data. Restituisce l'arcocoseno dell'espressione data. Restituisce l'arcoseno dell'espressione data. Restituisce l'arcotangente dell'espressione data. Restituisce il minimo intero superiore o uguale all'espressione data. COS(espr-num) Restituisce il coseno dell'espressione data. COT(espr-num) Restituisce la cotangente dell'espressione data. EXP(espr-num) Restituisce il numero e, la base dei logaritmi naturali, elevata alla potenza definita dall'espressione data. FLOOR(espr-num) Restituisce il massimo intero inferiore o uguale all'espressione data. LOG(espr-num) Restituisce il logaritmo naturale dell'espressione data. LOG10(espr-num) Restituisce il logaritmo in base 10 dell'espressione data. ROUND( espr-num-1, espr-num-2) Restituisce il valore di espr-num-1 arrotondato al numero di decimali specificato in espr-num-2. SIGN(espr-num) Restituisce -1 se l'espressione data vale un numero negativo, 1 se l'espressione data vale un numero positivo e 0 se l'espressione vale 0. SIN(espr-num) Restituisce il seno dell'espressione data. SQRT(espr-num) Restituisce la radice quadrata dell'espressione data. TAN(espr-num) Restituisce la tangente dell'espressione data. TRUNCATE( espr-num-1, espr-num-2) Restituisce il valore di espr-num-1 troncato al numero di decimali specificato in espr-num-2. 5.3)Funzioni stringa Nella descrizione di queste funzioni quando viene riportata la parola stringa (o stringa-1, stringa-2 etc.) si intende un'espressione stringa. ASCII(stringa) CHAR(espr-num) CONCAT(stringa-1, stringa-2) CONVERT(espr-num,SQL_CHAR) CONVERT(stringa,SQL_DOUBLE) Restituisce un valore numerico corrispondente al valore ASCII del primo carattere di stringa. Restituisce una stringa di un carattere il cui valore ASCII corrisponde al valore di espr-num. Restituisce una stringa formata dalla concatenazione delle due stringhe fornite come argomento. Restituisce una stringa contenente la rappresentazione del numero espresso da espr-num. Restituisce un numero corrispondente al valore rappresentato in stringa. Se il valore contenuto in stringa non può essere trasformato in un numero, allora questa funzione restituisce 0. picoSQL - linguaggio SQL 7/28 CURRENT_TIMESTAMP(stringa) Restituisce una stringa contenente il timestamp corrispondente al momento in cui è stato eseguito il comando, formattato secondo il contenuto di stringa. La formattazione prevede l'uso dei seguenti caratteri speciali: Y cifra dell'anno; M cifra del mese; D cifra del giorno; H cifra dell'ora; N cifra del minuto; S cifra del secondo; T cifra del millesimo di secondo (sempre 0). I caratteri diversi da questi, vengono riportati letteralmente. Per ottenere il formato standard del timestamp è necessario dunque fornire la seguente stringa formato: LCASE(stringa) LOWER(stringa) LENGTH(stringa) LOCATE(stringa-1, stringa-2, espr-num) LTRIM(stringa) RTRIM(stringa) SPACE(espr-num) SUBSTRING(stringa, espr-num-1, espr-num-2) UCASE(stringa) UPPER(stringa) 'YYYY­MM­DD HH:NN:SS.TTT' Restituisce una stringa come quella passata come argomento ma formata da tutte lettere minuscole. LCASE e LOWER sono sinonimi. Restituisce la lunghezza della stringa fornita come argomento. Cerca la prima occorrenza di stringa-1 all'interno di stringa-2 iniziando dal carattere nella posizione specificata in espr-num. Se viene individuata una tale occorrenza, la funzione restituisce un numero che indica la posizione del primo carattere di tale occorrenza, altrimenti restituisce 0. Le posizioni dei caratteri sono calcolate a partire da 1. Restituisce una stringa corrispondente a quella passata come argomento ma priva dei caratteri spazio presenti prima del primo carattere non spazio. Restituisce una stringa corrispondente a quella passata come argomento ma priva dei caratteri spazio presenti dopo l'ultimo carattere non spazio. Restituisce una stringa formata da tanti caratteri spazio quanti ne sono specificati nell'argomento. Restituisce la sottostringa della stringa data come argomento partendo dalla posizione specificata da espr-num-1 e lunga tanti caratteri quanti specificati in espr-num-2. La posizione dei caratteri sono calcolate partendo da 1. Restituisce una stringa come quella passata come argomento ma formata da tutte lettere maiuscole. UCASE e UPPER sono sinonimi. picoSQL - linguaggio SQL 8/28 5.4)Funzioni temporali Queste funzioni debbono avere come argomento un oggetto di tipo DATE, TIME o TIMESTAMP. Argomenti stringa non sono accettati mentre sono valide le sequenze di escape per date, tempi e date con il tempo. DAYOFMONTH(timestamp) HOUR(timestamp) MILLISECOND(timestamp) MINUTE(timestamp) MONTH(timestamp) NOW() SECOND(timestamp) YEAR(timestamp) Restituisce un numero, compreso tra 1 e 31, corrispondente al giorno del mese presente nell'argomento. Restituisce un numero, compreso tra 0 e 23, corrispondente all'ora presente nell'argomento. Restituisce un numero, compreso tra 0 e 999, corrispondente ai millisecondi presenti nell'argomento. Restituisce un numero, compreso tra 0 e 59, corrispondente ai minuti presenti nell'argomento. Restituisce un numero, compreso tra 1 e 12, corrispondente al mese presente nell'argomento. Restituisce un oggetto di tipo timestamp con la data e l'ora corrente. Restituisce un numero, compreso tra 0 e 59, corrispondente ai secondi presenti nell'argomento. Restituisce un numero, compreso tra 1 e 9999, corrispondente all'anno presente nell'argomento. 6) Condizioni di ricerca Le condizioni di ricerca sono formate da uno o più predicati, combinati tra loro tramite gli operatori logici AND, OR e NOT. Esse sono usate per scegliere un sottoinsieme di righe da una tabella o da un insieme di più tabelle. I predicati di una condizione di ricerca vengono valutati ogni volta che viene letta una riga dal database e se il risultato della valutazione risulta soddisfatto allora la riga viene restituita come valida, altrimenti viene ignorata. ☞ SQL standard usa una logica a tre stati, VERO, FALSO e SCONOSCIUTO dove quest'ultimo valore viene assegnato ogni volta si esegue un confronto dove compare un valore NULL. Le tabelle di verità di questo tipo di logica per gli operatori logici menzionati sono le seguenti: AND VERO FALSO SCON. VERO VERO FALSO SCON. FALSO FALSO FALSO FALSO SCON. SCON. FALSO SCON. OR VERO FALSO SCON. VERO VERO VERO VERO FALSO VERO SCON. VERO FALSO SCON. SCON. SCON. picoSQL - linguaggio SQL 9/28 NOT VERO FALSO FALSO VERO SCON. SCON. picoSQL usa una più semplice logica booleana a due valori, VERO e FALSO. Questo comporta un'unica differenza di comportamento tra lo standard SQL e picoSQL e cioè che il primo non ritiene soddisfatto un predicato che controlla se due valori NULL sono uguali mentre il secondo si. PicoSQL permette in particolare di fare join su campi a NULL, aggirando in tal modo la necessità delle outer-join, che comunque sono implementate. Due operandi confrontati in un predicato, direttamente o indirettamente, debbono essere di tipi comparabili. 6.1)Predicato di confronto Un predicato di confronto è formato da due espressioni confrontate da un'operatore, nella forma seguente: espressione-1 operatore-di-confronto espressione-2 Gli operatori di confronto ammessi sono i seguenti: = <> (oppure !=) > >= < <= uguale a diverso da maggiore di maggiore o uguale a minore di minore o uguale a 6.2)Predicato BETWEEN Un predicato BETWEEN controlla se un valore è contenuto tra due valori e ha la forma: espressione-1 [NOT] BETWEEN espressione-2 AND espressione-3 Questo predicato, senza il NOT, è equivalente a: espressione-1 >= espressione-2 AND espressione-1 <= espressione-3 Il NOT nega il risultato del confronto in modo analogo a quanto fa l'operatore logico omonimo. 6.3)Predicato IN Il predicato IN confronta il valore di un'espressione con una serie di valori e ha la forma: picoSQL - linguaggio SQL 10/28 espressione [NOT] IN (valore-1[ ,valore-2] ...) Questo predicato, senza il NOT è equivalente a: espressione = valore-1 [ OR espressione = valore-2] ... Il NOT nega il risultato del confronto in modo analogo a quanto fa l'operatore logico omonimo. 6.4)Predicato LIKE Il predicato LIKE consente di effettuare delle ricerche su stringhe utilizzando dei caratteri jolly. La forma del predicato è la seguente: espressione-stringa [NOT] LIKE stringa-ricerca All'interno della stringa di ricerca, i caratteri sono interpretati nel modo seguente: il carattere di sottolineatura (_) indica un qualsiasi carattere; il carattere di percentuale (%) indica una qualsiasi sequenza di 0 o più caratteri; il carattere barra rovesciata (\) indica che il carattere successivo deve essere considerato letteralmente anche se è un carattere jolly; ogni altro carattere viene considerato letteralmente. Per esempio la stringa di ricerca 'B%' restituisce vero per ogni valore di colonna che inizia con la lettera B, mentre la stringa di ricerca 'B__' restituisce vero per ogni valore di colonna che inizia con la lettera B ed è lungo 3 caratteri. Il NOT nega il risultato del confronto in modo analogo a quanto fa l'operatore logico omonimo. 6.5)Predicato NULL Questo predicato permette di controllare se un'espressione è uguale a NULL e ha la forma: espressione IS [NOT] NULL Il NOT nega il risultato del confronto in modo analogo a quanto fa l'operatore logico omonimo. Questo in SQL standard è l'unico modo per controllare l'esistenza di un valore NULL in una colonna. In picoSQL confrontando due valori di colonna uguali a NULL si ottiene il valore VERO. 6.6)Predicato EXISTS Questo predicato permette di controllare l'esistenza di almeno una riga in una qualsiasi tabella che soddisfi determinate condizioni e ha la forma: EXISTS (sotto-interrogazione) Il risultato del predicato è VERO se sotto-interrogazione, che è una interrogazione indipendente ottenuta tramite il comando SELECT, restituisce almeno una riga, FALSO altrimenti. picoSQL - linguaggio SQL 11/28 7) Elenco dei comandi Questo capitolo riporta in ordine alfabetico tutti i comandi SQL supportati nella release attuale da picoSQL. picoSQL - linguaggio SQL 12/28 ALTER TABLE Sintassi: Formato 1: ALTER TABLE nome-tabella RENAME nuovo-nome-tabella Formato 2: ALTER TABLE nome-tabella ADD [COLUMN] nome-colonna definizione-dato Formato 3: ALTER TABLE nome-tabella DROP [COLUMN] nome-colonna Scopo: Permette modificare una tabella modificandone il nome oppure aggiungendo una colonna o infine eliminando una colonna. Vedi anche: CREATE TABLE, DROP INDEX Descrizione: Questo statement modifica la struttura di una tabella esistente senza alterarne il contenuto. Il formato 1 consente di modificare il nome di una tabella. Il formato 2 consente di aggiungere una nuova colonna a una tabella, che viene posta in fondo alle colonne esistenti. Se la tabella contiene già delle righe, la nuova colonna conterrà tutti valori NULL. Il nome della nuova colonna deve essere univoco all'interno dei nomi di colonna della tabella. La clausola COLUMN è solo documentativa e non altera il comportamento del comando. Il formato 3 permette di eliminare una colonna da una tabella. La colonna da eliminare può contenere dati, e in tal caso questi vengono perduti, ma non può fare parte di un indice: in questo caso è necessario prima cancellare l'indice e quindi rimuovere la colonna. picoSQL - linguaggio SQL 13/28 CALL Sintassi: CALL nome-procedura ([argomento[, argomento]...]) Scopo: Permette di invocare una stored procedure. Vedi anche: SELECT Descrizione: Questo statement permette di eseguire una stored procedure precedentemente creata. Il numero di argomenti può essere fisso o variabile in dipendenza di come viene dichiarata la procedura. Gli argomenti sono espressioni analoghe a quelle che è possibile specificare nell SELECT. Come qualsiasi altro comando SQL, una CALL restituisce solo se ha avuto successo o meno; essa può generare un set di risultati, in modo analogo al comando SELECT, oppure non generare alcunché, come un comando INSERT, UPDATE o DELETE. Introdotta a partire dalla release 2.0. picoSQL - linguaggio SQL 14/28 CREATE INDEX Sintassi: CREATE [UNIQUE] INDEX nome-indice ON nome-tabella (nome-colonna [ASC|DESC] [, nome-colonna [ASC|DESC]] ...) Scopo: Permette la creazione di un indice su una tabella specificata. Gli indici sono importanti per migliorare le performance sul database. Vedi anche: DROP Descrizione: Questo statement crea un indice sulle colonne specificate della tabella specificata. Gli indici sono utilizzati automaticamente da picoSQL per migliorare le performance del database per quanto riguarda la ricerca di righe e per l'ordinamento delle stesse in presenza della clausola ORDER BY sia crescenti che decrescenti. Un indice può essere creato su una tabella in qualsiasi momento, tanto se contiene dei dati quanto se è vuota. La clausola UNIQUE assicura che nella tabella specificata non esistano due righe con valori uguali nelle colonne specificate come indice. Il nome-indice in picoSQL non viene usato, ma deve essere fornito per compatibilità con lo standard SQL. Agli indici viene assegnato un nome convenzionale formato nel modo seguente: nome-tabella_numero-progressivo Il numero progressivo parte da 02 perché il numero 01 è riservato alla chiave primaria. Il nome convenzionale degli indici deve essere usato nella DROP INDEX per eliminare un indice esistente. Non rimangono mai buchi nei numeri progressivi, per cui se si cancella l'ennesimo indice, quelli successivi assumono un nuovo nome con il numero progressivo diminuito di 1. Un indice può essere formato al massimo da 8 parti, dove con parte si intende un insieme di colonne adiacenti di tipo non nativo (CHAR, VARCHAR, NUMERIC, DATE, TIMESTAMP), e la sua occupazione può essere al massimo di 255 caratteri. La clausola DESC, che permette di creare un indice con ordinamento decrescente, non è supportata nella release attuale. picoSQL - linguaggio SQL 15/28 CREATE TABLE Sintassi: CREATE TABLE nome-tabella (nome-colonna definizione-dato [PRIMARY KEY] [, nome-colonna definizione-dato [PRIMARY KEY]] ... ) [, PRIMARY KEY (nome-colonna [ ,nome-colonna]...) ] Scopo: Permette la creazione di una tabella sul database. Vedi anche: DROP Descrizione: Questo statement crea una nuova tabella sul database, formata dalle colonne specificate. La clausola PRIMARY KEY specificata dopo la dichiarazione di una colonna fa in modo che questa faccia parte di un indice univoco creato sulla tabella. A differenza dello standard SQL, questa clausola può apparire in più definizioni di colonne, creando in tal modo un indice primario formato da più colonne. La clausola PRIMARY KEY specificata dopo la dichiarazione di tutte le colonne permette di specificare un indice primario su un numero qualsiasi di colonne in un qualsiasi ordine. Non è permesso dichiarare entrambi i tipi di clausole. picoSQL - linguaggio SQL 16/28 CREATE VIEW Sintassi: CREATE VIEW nome-vista [(nome-colonna [, nome-colonna] ... )] AS interrogazione-semplice [ WITH CHECK OPTION ] Scopo: Permette la creazione di una vista sul database. Vedi anche: DROP, SELECT Descrizione: Questo statement crea una nuova vista sul database. Una vista serve per dare una visione particolare del database anche se i dati sono memorizzati in modo diverso. In pratica si tratta di una interrogazione, fatta usando lo statement SELECT senza le clausole ORDER BY e GROUP BY né alcuna funzione aggregata, che viene memorizzata e che può venire usata per nuove interrogazioni come una qualsiasi tabella. Le viste però non esistono fisicamente sul database e vengono derivate ciascuna volta che vengono usate. Una vista può ereditare i nomi delle colonne dall'interrogazione che la crea oppure è possibile definirne di nuovi all'atto della creazione; in quest'ultimo caso il numero dei nomi specificati deve corrispondere a quello delle colonne restituite dall'interrogazione. Quando si crea una vista che mette in join più tabelle, picoSQL richiede che la condizione di join sia espressa con una clausola-join (vedi il comando SELECT). Le viste che riguardano una sola tabella possono essere aggiornate come una qualsiasi tabella. Se viene specificata la clausola WITH CHECK OPTION, ogni volta che si modifica una riga della vista, viene eseguita una verifica che la nuova riga o la modifica sulla riga esistente sia congruente con le condizioni specificate nella clausola WHERE della SELECT usata per creare la vista stessa. Se la verifica fallisce, la tabella non viene aggiornata e viene restituito un errore. picoSQL - linguaggio SQL 17/28 DELETE Sintassi: DELETE FROM nome-tabella [WHERE {condizione-di-ricerca|CURRENT OF nome-cursore}] Scopo: Permette l'eliminazione di un numero qualsiasi di righe da una tabella. Vedi anche: INSERT, UPDATE, SELECT Descrizione: Questo statement elimina dalla tabella specificata tutte le righe che soddisfano la condizione di ricerca. Se la clausola WHERE non viene specificata, vengono cancellate tutte le righe della tabella. La clausola CURRENT OF può essere usata al posto della condizione di ricerca per cancellare solo la riga correntemente letta da un cursore. Questa clausola può però essere usata solo da un linguaggio di programmazione in quanto è necessario usare una API per sapere il nome di un cursore (SQLGetCursorName) o per assegnarlo (SQLSetCursorName). picoSQL - linguaggio SQL 18/28 DROP Sintassi: DROP { INDEX nome-indice | TABLE nome-tabella | VIEW nome-view } Scopo: Permette l'eliminazione di un indice, di una tabella o di una vista. Vedi anche: CREATE INDEX, CREATE TABLE, CREATE VIEW Descrizione: Questo statement elimina definitivamente dal database un oggetto, che può essere un indice, una tabella o una vista. Nel caso di DROP INDEX lo spazio ricuperato rimane allocato e viene riusato per gli indici rimasti o per la creazione di nuovi indici. Nel caso di DROP TABLE, lo spazio su disco occupato dalla tabella con i relativi indici viene immediatamente rilasciato e reso disponibile per qualsiasi applicazione. Nel caso di DROP VIEW lo spazio rilasciato, che è sempre molto limitato perché corrisponde solo alla lunghezza del comando usato nella creazione della vista, viene riusato nella creazione di nuove viste. picoSQL - linguaggio SQL 19/28 INSERT Sintassi: Formato 1: INSERT INTO nome-tabella [(nome-colonna [, nome-colonna]...)] VALUES (espressione |NULL [, espressione | NULL] ...) Formato 2: Scopo: INSERT INTO nome-tabella [(nome-colonna [, nome-colonna]...)] interrogazione Permette l'inserimento di una o più righe in una tabella Vedi anche: REPLACE, DELETE, SELECT Descrizione: Questo comando è usato per aggiungere una o più righe a una tabella di database. Il formato 1 permette l'inserimento di una singola riga con i valori specificati dopo la clausola VALUES. Se viene fornita la lista opzionale di attributi, i valori dopo la clausola VALUES finiscono in essi con una corrispondenza posizionale (il primo valore nel primo attributo, il secondo nel secondo e così via). Se la lista degli attributi non è completa, negli attributi mancanti viene inserito il valore NULL. Se invece la lista opzionale di attributi non viene specificata, dopo la clausola VALUES devono essere specificati tanti valori quanti attributi conta la tabella e anche in questo caso viene fatta una corrispondenza posizionale. La riga viene inserita nel database in una posizione arbitraria. Il formato 2 permette di prelevare i valori da inserire da una qualsiasi tabella tramite un'apposita operazione SELECT. Anche in questo caso la corrispondenza tra attributi specificati, o della tabella se non viene specificata la lista opzionale degli attributi, e quelli restituiti dalla SELECT viene fatta posizionalmente. Se uno o più attributi nella tabella sono stati dichiarati di tipo SERIAL e contengono il valore NULL, a essi viene assegnato un numero sequenziale. Il valori assegnati possono essere ricuperati con il comando SELECT SERIAL (disponibile dalla release 2.0) a patto che esso venga eseguito sulla stessa statement handle usata per la INSERT. picoSQL - linguaggio SQL 20/28 REPLACE Sintassi: Formato 1: REPLACE INTO nome-tabella [(nome-colonna [, nome-colonna]...)] VALUES (espressione |NULL [, espressione | NULL] ...) Formato 2: Scopo: REPLACE INTO nome-tabella [(nome-colonna [, nome-colonna]...)] interrogazione Permette l'inserimento o la sovrascrittura di una o più righe in una tabella Vedi anche: INSERT, DELETE Descrizione: Questo comando è analogo a INSERT, con la differenza che nel caso la riga non possa essere inserita a causa di un indice univoco che verrebbe duplicato, REPLACE sovrascrive l'intera riga che contiene il valore univoco. Da un punto di vista logico quindi usare REPLACE equivale ad eseguire prima un DELETE sulla tabella in base al primo indice univoco e successivamente una INSERT con i valori specificati. In pratica però l'operazione viene fatta in modo atomico, vale a dire che sulla tabella non esiste un momento in cui siano assenti sia la vecchia riga che la nuova, ed è molto più efficiente dell'esecuzione di DELETE e INSERT. Un errore per indice duplicato quindi si può avere solo se sulla tabella sono presenti più di un indice univoco e se la sovrascrittura provocasse la duplicazione di un indice univoco successivo al primo. picoSQL - linguaggio SQL 21/28 SELECT Sintassi: Formato 1: SELECT [ ALL | DISTINCT ] lista-di-espressioni FROM { nome-tabella [[AS] alias][, nome-tabella [[AS] alias]] ... | clausola-join } [ WHERE condizione-di-ricerca ] [ GROUP BY nome-colonna [, nome-colonna] ...] [ HAVING condizione-ricerca ] [ UNION comando-select ] [ ORDER BY {numero-intero [ASC|DESC]|espressione [ASC | DESC]} [, {numero-intero [ASC|DESC]|espressione [ASC | DESC]}]...] [ LIMIT numero-intero ] [ OFFSET numero-intero ] Formato 2: SELECT lista-di-espressioni FROM nome-tabella [ WHERE condizione-di-ricerca ] FOR UPDATE Formato 3 (release >= 2.0): SELECT SERIAL Scopo: Permette di interrogare il database e di ottenerne informazioni sulla base di condizioni logiche di ricerca. Vedi anche: CREATE VIEW, UNION, INSERT Descrizione: Questo comando è il più complesso di tutto il linguaggio poiché fornisce i mezzi necessari per interrogare il database e individuare un insieme di dati. Per l’uso del formato 3, vedere INSERT. Il formato 2 è un sottoinsieme del formato 1, con in più solo la clausola FOR UPDATE, e viene usato nei programmi per fare in modo che la riga che si sta leggendo non sia modificabile da un altro utente. La riga torna disponibile quando si legge la riga successiva o quando viene chiuso il cursore. Vediamo quindi tutte le clausole del formato 1. ALL | DISTINCT Se viene specificata la clausola DISTINCT non vengono visualizzate le righe duplicate. Questa è chiamata 'proiezione' del risultato del comando. Va comunque notato che per poter eliminare le righe duplicate, picoSQL esegue un ordinamento dei risultati, il che può portare a un incremento dei tempi di risposta, specialmente se il numero di righe selezionate è molto ampio. La clausola ALL viceversa lascia tutte le righe e rappresenta il comportamento di default quando non viene specificato alcunché. picoSQL - linguaggio SQL 22/28 lista-di-espressioni La lista-di-espressioni indica quali sono i dati da visualizzare ogni volta che viene trovata una riga con le caratteristiche richieste; la forma di lista-di-espressioni è la seguente: { * | espressione [[ AS ] alias] [, espressione [[ AS ] alias]]... } Se viene specificato un asterisco (*), esso rappresenta tutte le colonne di tutte le tabelle che vengono menzionate nella successiva clausola FROM. È permesso usare funzioni aggreganti come espressioni. Alle espressioni possono essere assegnati dei nomi alias. Tali nomi vengono visualizzati normalmente in testa alle colonne dei risultati nei programmi d'interrogazione interattivi, ma possono servire anche per distinguere, nelle clausole successive come ORDER BY e GROUP BY, colonne con nomi uguali ma appartenenti a tabelle diverse. FROM Permette di specificare la lista delle tabelle da cui si vuole estrarre i dati. Nel caso in cui l'estrazione avvenga da più di una tabella, normalmente è necessario indicare una o più condizioni di join, altrimenti si ottiene come risultato la combinazione di ciascuna riga di ciascuna tabella con tutte le altre (prodotto cartesiano). Una join è una condizione che lega una colonna di una tabella a una condizione di un'altra tabella. In picoSQL si possono specificare join in due modi: mettendo la condizione insieme a tutte le altre nella WHERE (vedi seguito); specificando le condizioni direttamente nella FROM con una sintassi opportuna; Usando il primo modo, la clausola FROM si riduce a una semplice lista di nomi di tabella. Si possono specificare dei nomi alias delle tabelle in modo da rendere possibili le 'self-join', cioè join di una tabella con se stessa. Il secondo modo è apparentemente più complesso ma ha il vantaggio di separare le condizioni di join dalle condizioni di ricerca e di poter impostare le join esterne (outer join). La sintassi della clausola-join è la seguente: nome-tabella [[AS] alias] {INNER | LEFT OUTER| RIGHT OUTER} JOIN {nome-tabella [[AS] alias] | clausola-join} ON condizione-di-join Come si vede è possibile impostare un numero qualsiasi di join, anche di tipo diverso tra loro, annidando più clausole-join. Mettere la condizione di join nella WHERE o usare la clausola INNER JOIN fa ottenere gli stessi risultati ma può influenzare le performance: nel primo caso infatti è picoSQL che sceglie l'ordine ottimale di scansione delle tabelle in modo da minimizzare le letture mentre nel secondo caso l'ordine di scansione è fissato nell'ordine con cui sono riportate le tabelle. Questo può risultare svantaggioso quando si debbano fare delle query dinamiche nelle quali non si conosce a priori su quale colonna di quale tabella verrà fatta la ricerca. picoSQL - linguaggio SQL 23/28 WHERE Permette di specificare una condizione-di-ricerca che limita il numero delle righe ottenute. Anche le condizioni di join possono essere messe dopo questa clausola. Vedi anche "Condizioni di ricerca". GROUP BY Permette di raggruppare più righe dal risultato della ricerca sul database. Quando si usa questa clausola, in lista-di-espressioni possono essere messe solo funzioni aggreganti o nomi di colonna riportate anche in GROUP BY. Il risultato ottenuto contiene una riga per ciascun valore distinto delle colonne elencate, che viene definita 'gruppo'. Eventuali funzioni aggreganti specificate vengono applicate a ciascun gruppo. HAVING Questa clausola può essere specificata solo dopo una GROUP BY e serve per limitare il numero di gruppi selezionati. Essa infatti permette di definire una condizione-diricerca nella quale possono comparire anche le funzioni aggreganti definite nella lista-di-espressioni. ORDER BY Ordina il risultato dell'interrogazione. Ciascun elemento nella lista specificata dopo ORDER BY può avere la clausola ASC, che indica che è richiesto l'ordinamento ascendente, oppure la clausola DESC che indica che è richiesto l'ordinamento discendente. Nel caso che nessuna di queste due clausole venga specificata, viene sottinteso l'ordinamento ascendente. Se la lista contiene un numero intero N, l'ordinamento viene fatto sull'elemento N-esimo della lista-di-espressioni. LIMIT Limita il numero di righe restituite al numero specificato come argomento. Se il numero totale di righe selezionate è inferiore al numero specificato come argomento, la clausola non ha alcun effetto. OFFSET Restituisce le righe selezionate saltandone un numero pari al numero specificato come argomento. Se il numero totale di righe selezionate è inferiore al numero specificato come argomento, restituisce un messaggio di dati non trovati. picoSQL - linguaggio SQL 24/28 UNION Sintassi: select-senza-order-by UNION [ALL] select-senza-order-by [ UNION [ALL] select-senza-order-by] ... [ORDER BY intero [ASC | DESC ][, intero [ASC | DESC ]] ...] Scopo: Permette di fare l'unione del risultato di due o più comandi SELECT Vedi anche: SELECT Descrizione: La UNION è un'operazione che permette di combinare il risultato di due o più comandi SELECT, opzionalmente ordinati per le colonne specificate. Ciascuna SELECT deve restituire un uguale numero di colonne, che debbono corrispondere anche come tipo, e non può specificare una propria ORDER BY. La UNION normalmente elimina le colonne duplicate a meno di non specificare la clausola ALL. La clausola ORDER BY è analoga a quella usata nel comando SELECT con la limitazione che si possono specificare solo numeri interi corrispondenti alle posizioni delle colonne da ordinare. picoSQL - linguaggio SQL 25/28 UPDATE Sintassi: UPDATE nome-tabella SET nome-colonna = {espressione | NULL} [, nome-colonna = {espressione | NULL}] ... [ WHERE {condizione-di-ricerca|CURRENT OF nome-cursore}] Scopo: Permette di modificare una o più righe sul database Vedi anche: INSERT, DELETE, SELECT Descrizione: Questo comando è usato per modificare una o più righe su una tabella. Ciascuna colonna prende il valore dell'espressione o NULL corrispondente. Nell'espressione può comparire anche il nome della colonna che si sta modificando, che avrà il valore appena letto. La clausola WHERE permette di specificare l'insieme di righe su cui viene effettuato l'aggiornamento: nel caso tale clausola non venga specificata, vengono aggiornate tutte le righe della tabella. La clausola CURRENT OF può essere usata al posto della condizione di ricerca per modificare solo la riga correntemente letta da un cursore. Questa clausola può però essere usata solo da un linguaggio di programmazione in quanto è necessario usare una API per sapere il nome di un cursore (SQLGetCursorName) o per assegnarlo (SQLSetCursorName). picoSQL - linguaggio SQL 26/28 8) Ottimizzatore delle query picoSQL ha al proprio interno un ottimizzatore che trova il modo migliore per soddisfare le interrogazioni richieste. Sono previste solo due strategie di lettura, la scansione sequenziale di un'intera tabella oppure l'utilizzo degli indici dichiarati. picoSQL si ricava la possibilità di usare un indice analizzando la condizione di ricerca ed è per questo motivo che è importante, specialmente su tabelle con molte righe, la definizione degli indici e l'utilizzo di condizioni di ricerca che li sfruttino. Per illustrare in modo semplice come deve essere strutturata una condizione, supponiamo intanto di avere un indice di una sola colonna di nome I. L'ottimizzatore di picoSQL ricava la propria strategia di ricerca analizzando l'interrogazione in modo formale, non considerando cioè i valori effettivi con cui le colonne vengono confrontate né, di conseguenza, i valori realmente presenti nella base dati. Per capire questo punto, bisogna considerare che spesso vengono fatte delle query usando i parametri, come nel caso seguente: I = ? Questa query può essere eseguita più volte, sostituendo ogni volta un valore diverso al posto del punto interrogativo. È evidente che in casi come questo il modo più efficiente di procedere è di eseguire un'unica analisi del comando e stabilire una strategia di ricerca valida per qualsiasi parametro. Fare l'analisi conoscendo il valore del parametro costringerebbe a ripetere l'analisi ogni volta che quest'ultimo viene modificato, non apportando, nella quasi totalità dei casi, informazioni utili a migliorare la strategia di ricerca. Su questi presupposti, picoSQL riesce sempre a individuare un indice di ricerca, purché ne esista uno usabile. Affinché un indice possa essere usato per una ricerca, una condizione deve individuare sulla colonna dell'indice uno o più intervalli esaustivi di valori che contengano tutte le righe richieste, come, per esempio, la seguente: I < 5 Per il fatto che l'ottimizzazione non tiene conto dei dati può accadere che l'utilizzo di un indice non porti alcun vantaggio: supponendo, per esempio, che nel database siano presenti solo righe in cui la colonna I ha sempre un valore inferiore a 5, la condizione di ricerca precedente non limita in alcun modo la lettura della tabella e sarebbe stata più efficiente una scansione sequenziale. Condizioni in AND a una condizione che individua un intervallo esaustivo non influenzano la valenza di un indice, per cui anche la seguente condizione permette l'utilizzo dell'indice: I < 5 AND A > 10 Le cose cambiano se, al posto di una AND si usa una OR. Nella condizione seguente: I < 5 OR A > 10 non è detto che le righe che ci interessano rientrino nell'intervallo definito dall'indice (I < 5) per cui tale intervallo non è più esaustivo e l'uso dell'indice diventa inutile. La cosa non migliora neppure se ci fosse un'indice definito sulla colonna A, in quanto neanche questo intervallo sarebbe esaustivo. picoSQL - linguaggio SQL 27/28 L'analisi di entrambi gli intervalli, con eliminazione di eventuali sovrapposizioni, sarebbe pesante da un punto di vista di calcolo e potrebbe portare anche a una doppia scansione completa della tabella. In questi casi quindi viene eseguita una scansione sequenziale. Quest'ultima osservazione ci induce a una prima semplice regola empirica: nelle ricerche è meglio non usare l'operatore logico OR perché porta a scandire l'intera tabella. Questo però non è sempre vero: ci sono infatti casi in cui anche in presenza di OR si riesce a individuare degli intervalli esaustivi, come, per esempio: I < 5 OR I > 10 In questo caso gli intervalli sono due, ma racchiudono tutte le righe richieste. picoSQL è in grado di sfruttare gli indici in presenza di più intervalli esaustivi, anche nel caso che tali intervalli si sovrappongono, come nel caso: I > 5 OR I > 10 In presenza di indici composti da più colonne, la teoria non cambia, anche se può essere più complicato capire se la ricerca impostata può essere fatta usando un indice o meno. Per prima cosa l'elemento in posizione n di un indice composto può essere usato in una ricerca se è n = 1 oppure se può essere usato l'elemento in posizione n - 1. Supponiamo di avere un indice composto formato dalle colonne I, J e K e di avere le seguenti condizioni di ricerca: I > 5 AND J < 10 AND K = 4 I > 5 AND K = 4 K = 4 Nella prima ricerca è possibile usare l'indice in tutte le sue parti, nella seconda si può usare solo la prima parte dell'indice mentre nella terza l'indice non può essere usato. Fatta questa considerazione, le regole di utilizzo di un indice sono le stesse già viste per indici semplici. Analizzando le seguenti condizioni: (I = 5 AND J = 10 AND K = 4) OR (I = 6 AND J = 11 AND K = 5) (I = 5 AND J = 10 AND K > 4) OR (I = 5 AND J > 10) OR I > 5 si può notare che la prima usa completamente tutti e tre gli indici mentre la seconda utilizza solo la componente sulla colonna I; in tal caso infatti J e K possono avere un valore qualsiasi e quindi non possono essere usati come indice. In realtà, analizzando i valori attentamente si nota che sarebbe possibile individuare un intervallo esaustivo più stretto, ma questo comporterebbe un analisi contestuale che picoSQL non fa. picoSQL - linguaggio SQL 28/28