Il teatro lirico “social” Un teatro lirico gestisce gli spettacoli tramite un sistema di row-level trigger. I visitatori del sito si iscrivono e registrano.

Slides:



Advertisements
Presentazioni simili
Stored Procedure Function Trigger
Advertisements

© 2007 SEI-Società Editrice Internazionale, Apogeo Unità C1 Il linguaggio SQL.
Structured Query Language (SQL) Presentazione 13.1 Informatica Generale (Prof. Luca A. Ludovico)
Esercizio zSi vuole realizzare un data warehouse per una azienda che vende mobili allingrosso. zIl data warehouse deve permettere di analizzare i ricavi.
Liste di Interi Esercitazione. Liste Concatenate Tipo di dato utile per memorizzare sequenze di elementi di dimensioni variabile Definizione tipicamente.
Query OQL e XQUERY a confronto
Biglietti e Ritardi: schema E/R
Biglietti e Ritardi: schema E/R
Biglietti: schema E/R.
1 Biglietti: schema E/R. 2 Biglietti: albero degli attributi.
Basi di dati attive Dispongono di un sottosistema integrato per definire e gestire regole di produzione (regole attive) Regole di tipo: Evento-Condizione-Azione.
Esercitazioni del Corso di Sistemi Informativi Marina Mongiello
Esercitazioni del Corso di Sistemi Informativi Marina Mongiello
19/01/2014 Viste. 19/01/2014 Viste Le Viste Logiche o Viste o View possono essere definite come delle tabelle virtuali, i cui dati sono riaggregazioni.
1 Corso di Laurea in Biotecnologie Informatica (Basi di Dati) SQL: Data Manipulation Language (DML) Anno Accademico 2009/2010 Da: Atzeni, Ceri, Paraboschi,
Basi di Dati II Sara Romano
Basi di dati attive Paolo Atzeni.
Access: Query semplici
Basi di dati Università Degli Studi Parthenope di Napoli
SQL: Lezione 7 Nataliya Rassadko
SQL Per la definizione di basi di dati SQL per definire ed amministrare Ogni utente puo definire una base di dati di cui diventa lamministratore potendo.
Cercare informazioni sul Web. 5-2 Organizzazione dellinformazione tramite gerarchie Classificazione gerarchica Linformazione è raggruppata in un piccolo.
Basi di dati 2002 EER Vogliamo aumentare lespressività degli Entity Model EER: Entity Model Esteso.
Le transazioni Itis Max Planck.
Esercitazione.
Duplicati Lalgebra relazionale non ammette duplicati, SQL li ammette. Quindi select Città from Persona where Cognome= Rossi estrae una lista di città in.
Vincoli di integrità generici Con i costrutti visti sinora, non è sempre possibile definire tutti i possibili vincoli di integrità. Per questo esiste listruzione.
Manipolazione dei dati I comandi SQL che permettono di modificare il contenuto di una base di dati sono insertdeleteupdate insert ha la seguente sintassi:
Daniel Stoilov Tesi di Laurea
Viste. Cosè una vista? è possibile creare un subset logico di dati o una combinazione di dati una vista è una tabella logica basata su una tabella o su.
Creazione e manipolazione tabelle. TABELLE una tabella può essere creata in qualsiasi momento,anche quando gli utenti stanno usando il database la struttura.
Basi di dati Claudia Raibulet
Corso di INFORMATICA anno scolastico 2009/10 Linguaggio SQL IDENTIFICATORI di tabelle e attributi: stringhe di lunghezza max 18 caratteri, composte da.
Esercitazione di Basi di Dati
BROKER SERVER Progetto di Ingegneria del Web 2008 Alessio Bianchi Andrea Gambitta Giuseppe Siracusano.
Realizzato da Roberto Savino
Gerarchie Ricorsive Una gerarchia ricorsiva deriva dalla presenza di una ricorsione o ciclo (un anello nel caso più semplice) nello schema operazionale.
Basi di Dati e Sistemi Informativi
Everywhere Takeaway Progetto di SSCSWeb A.A. 2011/2012.
Everywhere Takeaway Progetto di SSCSWeb A.A. 2011/2012.
Everywhere Takeaway Progetto di SSCSWeb A.A. 2011/2012.
Everywhere Takeaway Progetto di SSCSWeb A.A. 2011/2012 V. Costamagna, F. Dotta, F. Barbano, L. Violanti, Oltikuka.
Everywhere Takeaway Progetto di SSCSWeb A.A. 2011/2012.
BIOINFO3 - Lezione 51 INSERIMENTO DEI DATI Visto come si creano le tabelle (sinora tristemente vuote), cominciamo ad occuparci di come riempirle con dei.
CORSI DI FORMAZIONE - Basi di Dati: MySql - Parte 4 - Dicembre Utenti e privilegi del database - 1 Root è lutente amministratore predefinito, ma.
Sistemi Informativi Insieme di “strutture” in grado di acquisire, elaborare, trasmettere ed archiviare informazioni in genere ad uso di un’organizzazione.
Microsoft Access (parte 5) Introduzione alle basi di dati Scienze e tecniche psicologiche dello sviluppo e dell'educazione, laurea magistrale Anno accademico:
Pagine ASP parte 3 I data base Stefano Schacherl.
Tecnologie di InternetDocument Type Definition Dott. Nicola Dragoni Document Type Definition  Document Type Definition (DTD)  Documento XML valido 
Il Linguaggio SQL. Le interrogazioni in SQL (continua…) La parte di SQL dedicata alla formulazione di interrogazioni fa parte del DML. SQL esprime le.
SQL, esempi 30/10/2014Basi di dati SQL1. 30/10/2014Basi di dati SQL2 CREATE TABLE, esempi CREATE TABLE corsi( codice numeric NOT NULL PRIMARY KEY, titolo.
MySQL Database Management System
TW Asp - Active Server Pages Nicola Gessa. TW Nicola Gessa Introduzione n Con l’acronimo ASP (Active Server Pages) si identifica NON un linguaggio di.
Nozione ed uso Operazioni eseguite automaticamente ogni volta che avviene un certo evento Uso: –Gestione di vincoli di integrità: Per fallimento Per modifica.
1 Laboratorio di Introduzione alla Programmazione §II MODULO §3 crediti §Esame e voto unico (su 6 crediti totali)
Vincoli Interrelazionali Per i vincoli visti l’inserimento di un valore che li viola viene semplicemente impedito. In caso di vincoli di integrità referenziale,
Vincoli interrelazionali
Raggruppamenti e target list scorretta select padre, avg(f.reddito), p.reddito from persone f join paternita on figlio = nome join persone p on padre =
Everywhere Takeaway Progetto di SSCSWeb A.A. 2011/2012 V. Costamagna, F. Dotta, F. Barbano, L. Violanti, Oltikuka.
Funzionalità del DBMS relazionale l Funzioni per –definizione della base di dati –inserimento / rimozione /aggiornamento di informazioni deve soddisfare.
Integrazione con e /. Integrazione e/ - Banche24 Aggiornamenti previsti nel 2012 Sono previsti due momenti di rilascio per le funzionalità di integrazione.
Approfondimenti SQL.
Basi di Dati e Sistemi Informativi Esercitazione: Il Linguaggio SQL (DDL+DML) Home page del corso:
Microsoft Access Filtri, query. Filtri Un filtro è una funzione che provoca la visualizzazione dei soli record contenenti dati che rispondono a un certo.
Linguaggio SQL prima parte Linguaggio SQL prima parte A. Lorenzi, E. Cavalli INFORMATICA PER SISTEMI INFORMATIVI AZIENDALI Copyright © Istituto Italiano.
Primo Appello a.a Gennaio Call Me (on the line) Una base di dati supporta una giuria tecnica che deve votare per delle canzoni votate.
Interazione Persona Computer prova di progetto Gruppo: IO Componenti: Carlo Solimando Sito analizzato:
Access Breve introduzione. Componenti E’ possibile utilizzare Access per gestire tutte le informazioni in un unico file. In un file di database di Access.
Basi di Dati attive. Sistemi Informativi DEE - Politecnico di Bari E. TinelliBasi di dati attive2 Definizione Una base di dati si dice attiva quando dispone.
Linguaggio SQL. Linguaggi per database La diffusione del modello relazionale ha favorito l’uso prevalente di linguaggi non procedurali: in questo modo.
Transcript della presentazione:

Il teatro lirico “social” Un teatro lirico gestisce gli spettacoli tramite un sistema di row-level trigger. I visitatori del sito si iscrivono e registrano le parole chiave di loro interesse. Quando (a) viene pubblicato un nuovo spettacolo (descritto con parole chiave) i visitatori interessati ricevono una di avviso, e alcuni potranno acquistare un biglietto. In caso di (b) cancellazioni o (c) cambi di orario, chi ha acquistato un biglietto riceve una notifica. Si scrivano solo i trigger relativi agli eventi descritti (a,b,c). Si assuma disponibile una funzione send-mail( Dest, Subj,... AltriAttr...) con opportuni parametri utili a comporre il messaggio. VISITATORE( Id-Vis, Nome, ) INTERESSE( Id-Vis, Keyword ) SPETTACOLO( Id-Spet, Titolo, Data, OraInizio ) DESCRIZIONE( Id-Spet, Keyword ) ACQUISTO( Id-Vis, Id-Spet, DescrizionePosti )

Il teatro lirico “social” Assumiamo che l’inserimento delle descrizioni sia contestuale a quello dello spettacolo create rule NuovoSpettacolo after insert into SPETTACOLO for each row send-mail( ( select from ( VISITATORE V join INTERESSE I on V.Id-Vis = I.Id-Vis ) join DESCRIZIONE D on D.Keyword = I.Keyword where D.Id-Spet = new.Id-Spet ), “Nuovo spettacolo”, new.Titolo, new.Data, new.OraInizio )

Il teatro lirico “social” create rule CancellazioneSpettacolo after delete from SPETTACOLO for each row send-mail( ( select from VISITATORE V join ACQUISTO A on V.Id-Vis = A.Id-Vis where A.Id-Spet = old.Id-Spet), “Cancellazione”, old.Titolo, old.Data, old.OraInizio )

Il teatro lirico “social” create rule NuovoSpettacolo after update of OraInizio on SPETTACOLO for each row send-mail( ( select from VISITATORE V join ACQUISTO A on V.Id-Vis = A.Id-Vis where A.Id-Spet = old.Id-Spet), “Cambio Orario”, old.Titolo, old.Data, new.OraInizio )

I have a drink <!ELEMENT Cocktail ( Nome, Componente+, TipoBicchiere, Guarniture, Procedimento )> Nel DTD soprastante, relativo ai dati del sito di un corso online per barman, gli elementi non ulteriormente specificati contengono solo PCData. La quantità è espressa in millilitri o grammi (per liquidi e solidi rispettivamente), e si riferisce alla dose per una persona. I nomi degli ingredienti sono univoci.

I have a drink 1) Estrarre in XQuery, per ogni ingrediente, la lista dei nomi dei cocktail che lo contengono. (2 p.) { for $i in //Ingrediente/Nome return { for $n in //Cocktail[ Componente/NomeIngr = $i ]/Nome return { $n } } }

I have a drink 1) Estrarre in XQuery, per ogni ingrediente, la lista dei nomi dei cocktail che lo contengono. (2 p.) { for $i in //Ingrediente/Nome return { //Cocktail[ Componente/NomeIngr = $i ]/Nome } }

I have a drink 2) Costruire in XQuery un documento “riassuntivo” che, per ogni cocktail, indichi: - le calorie totali del cocktail (considerando il contributo di tutti gli ingredienti) e - la lista dei nomi dei 4 ingredienti presenti in quantità maggiore (considerando equivalenti 1 ml e 1 g). In caso di parimerito sulla quantità, si prendano comunque solo 4 ingredienti, discriminando in base all’ordine alfabetico). (5 p.) <!ELEMENT Cocktail ( Nome, Componente+, TipoBicchiere, Guarniture, Procedimento )>

I have a drink { for $c in //cocktail let $compord := for $ingr in $c/Componente order by $ingr/Quantità, $ingr/NomeIngr return $ingr let $cals := for $i in $compord return $i/Quantità * //Ingrediente[Nome = $i/NomeIngr]/CaloriePerGr return { sum( $cals ) } { $compord[position() }

I have a drink 3) Definire uno schema O-O per gli stessi dati ed esprimere la query 1) in OQL. (4 p.) L’ingrediente ha le calorie/gr come proprietà: non vogliamo ripeterle nelle ricette dei cocktail: Class Cocktail ( Nome: String, Prezzo: Decimal, Ingredienti: list-of( struct( Ingrediente: * Ingrediente, Quantità: Integer ) ) ) Class Ingrediente ( Nome: String, CaloriePerGrammo: Integer ) Query1: select struct( Ingrediente: I, CocktailsCheLoContengono: ( select C.Nome from C in Cocktail where I in C.Ingredienti.Ingrediente.Nome ) ) from I in Ingrediente

Locking gerarchico Il seguente schedule è relativo a una gerarchia in cui una pagina PagA contiene le tuple t 1 e t 2 : r 1 ( PagA ), w 2 ( t 1 ), w 1 ( t 2 ) Il sistema usa il lock gerarchico. Si mostri una sequenza di richieste di lock, unlock, lock escalation e lock downgrade delle transazioni T1 e T2, tenendo conto che lo schedule deve essere compatibile con la classe 2PL.

Una sequenza compatibile è: PagA t2 t1 SIXL1(PagA)SIXL1 - - XL1(t2) [*] SIXL1 XL1 - r1(PagA) U-SL1(PagA) [lock downgrade] IXL1 XL1 - IXL2(PagA) IXL1, IXL2 XL1 - XL2(t1)IXL1, IXL2 XL1 XL2 w2(t1) U-XL2(t1)IXL1, IXL2 XL1 - U-IXL2(PagA)IXL1 XL1 - Commit(T2) w1(t2) U-XL1(t2)IXL1 - - U-ISL1(PagA) Commit(T1) [*] il “trucco” per rendere la sequenza 2PL è che T1 acquisisca da subito il diritto specifico su t2, anche se w 1 (t2) è la sua ultima operazione, di modo che quel lock resti dopo il rilascio di quello in lettura su tutta la pagina (downgrade di SIXL in IXL)

Play with me Si consideri il seguente schema relativo a un sistema di noleggio di sale prove per gruppi musicali. Le indicazioni orarie di inizio e fine sono relative a ore intere (8:00, 21:00,..., ma non 8:15). L’uso effettivo può avvenire solo in corrispondenza di una prenotazione, e al limite iniziare dopo o terminare prima degli orari della prenotazione. Inoltre, tutte le sale prove aprono alle 7:00 e chiudono tassativamente alle 24:00. Cliente (CodiceFiscale, Nome, Cognome, Tipo) Prenotazione (CodFisCliente, CodiceSala, Giorno, OraInizio, OraFine) UsoEffettivo (CodFisCliente, CodiceSala, Giorno, OraInizio, OraFine, Costo) Sala (Codice, CostoOrario) 1) Si scriva un trigger che impedisce di prenotare una sala già prenotata

Play with me Si deve intercettare l’esistenza di una prenotazione per la stessa sala che sia temporalmente sovrapposta. Possiamo usare una semantica “before” per imporre da subito il rollback della transazione. N.B.: due intervalli di tempo sono sovrapposti se uno inizia prima della fine e finisce dopo l’inizio dell’altro. create trigger TuNonPuoiPrenotare before insert into Prenotazione for each row when exists ( select * from Prenotazione where CodiceSala = new.CodiceSala and Giorno = new.Giorno and OraInizio new.OraInizio ) do rollback

Play with me Si supponga che i dati sull’uso effettivo siano inseriti solo al termine dell’uso della sala. Si propongano un arricchimento dello schema per tener traccia del numero di ore prenotate e non utilizzate da ogni cliente, e un insieme di regole che assegni il valore "Inaffidabile" al campo "tipo" dei clienti all’accumulo di 50 ore prenotate e non usate. Teoricamente si può ricalcolare il numero di ore “sprecate” con una query ad ogni verifica, senza alterare lo schema. Considerazioni di efficienza però suggeriscono di calcolare incrementalmente il numero di sprechi di ogni cliente. Ad esempio aggiungendo un attributo “OreSprecate” alla tabella Cliente. La corrispondenza tra usi effettivi e prenotazioni si sancisce assumndo che il trigger precedente garantisca la correttezza delle prenotazioni e che gli usi effettivi non violino mai i vincoli delle prenotazioni (solo chi ha prenotato può presentarsi, la prenotazione c’è di sicuro, e l’uso non inizia prima e non termina dopo gli orari indicati).

Play with me create trigger AggiornaOreSprecate after insert into UsoEffettivo for each row update Cliente set OreSprecate = OreSprecate + ( select OraFine – OraInizio – ( new.OraFine – new.OraInizio ) from Prenotazione where CodiceSala=new.CodiceSala and Giorno = new.Giorno and OraInizio>= new.OraInizio and OraFine <= new.OraFine ) where CodiceFiscale = new.CodFisCliente Questa è la soluzione più semplice: aggiorna sempre il campo, eventualmente con un contributo pari a 0 se l’utilizzatore è stato puntuale. Si può aggiungere una clausola when per intercettare i casi di contributo nullo e non effettuare l’aggiornamento

Play with me Resta poi solo da intercettare il limite di 50 ore: create trigger AggiornaTipoCliente after update of OreSprecate on Cliente for each row when old.OreSprecate = 50 do update Cliente set Tipo = “Inaffidabile” where CodiceFiscale = old.CodiceFiscale ATTENZIONE: manca del tutto il contributo di chi non si presenta affatto

Replay with me Si consideri il seguente DTD, facendo riferimento all’esercizio precedente per l’interpretazione degli elementi. Ogni elemento non ulteriormente specificato contiene solo PCData. Estrarre in XQuery, per ogni cliente che abbia qualche prenotazione poi non interamente sfruttata, la lista di tutte le sue prenotazioni non sfruttate appieno. (4 punti)

Replay with me L’unica “difficoltà” rispetto al caso relazionale è l’imposizione del vincolo che la prenotazione e l’uso fossero relativi alla stessa sala, che si esprime attraverso una relazione strutturale nel documento XML (prenotazioni e usi figli dello stesso elemento, assenza di un “codice sala” nella prenotazione). for $cli in //Cliente let $listPrenSottoutilizzate := ( for $sal in //Sala, $pre in $sal/Prenotazione[CodFisCliente = $cli/CodiceFiscale], $uso in $sal/UsoEffettivo[CodFisCliente = $cli/CodiceFiscale] where $pre/Data = $uso/Data and $pre/OraInizio >= $uso.OraInizio and $pre/OraFine <= $uso/OraFine and $pre/Data < fn:current-date() return $pre ) where count($listPrenSottoutilizzate) > 0 return { $listPrenSottoutilizzate }

La soluzione precedente ha, a rigore, il difetto di non considerare le prenotazioni totalmente dimenticate (prive di uso effettivo!). Si può perfezionare modificando la definizione di $listPrenSottoutilizzate, concatenando alle prenotazioni letteralmente sottoutilizzate la lista di quelle quelle totalmente disertate: let $listPrenSottoutilizzate := ( for $sal in //Sala. $pre in $sal/Prenotazione[CodFisCliente = $cli/CodiceFiscale], $uso in $sal/UsoEffettivo[CodFisCliente = $cli/CodiceFiscale] where $pre/Data = $uso/Data and $pre/OraInizio >= $uso/OraInizio and $pre/OraFine <= $uso/OraFine return $pre, for $sal in //Sala $pre in $sal/Prenotazione[CodFisCliente = $cli/CodiceFiscale and ( Data < fn:current-date() or Data= fn:current-date() and OraFine < fn:current-time() ) ] where count( $sal/UsoEffettivo[ CodFisCliente = $cli/CodiceFiscale and Data = $pre/Data and OraInizio >= $pre/OraInizio and OraFine <= $pre/OraFine ] ) = 0 return $pre ) Si noti che occorre escludere le prenotazioni “future” dalla seconda parte della lista, perché non possono evidentemente essere ancora associate ad alcun uso effettivo.

Update Lock Dato lo schedule r 1 (x) r 2 (x) r 3 (y) w 3 (y) w 1 (x) w 2 (y) mostrare una sequenza di richieste di lock e di unlock prodotte dalle transazioni che sia compatibile con una esecuzione 2PL, in un sistema che offre i lock SL, UL e XL (rispettivamente SharedLock, UpdateLock, ed eXclusiveLock). Si suggerisce di usare la notazione SL i (x) per indicare che la transazione T i richiede un lock SL sulla risorsa x e rel(SL i (x)) per indicare che quel lock è stato rilasciato (per evitare ambiguità tra la U di Unlock e la U di Update).

In un sistema con update lock, per evitare deadlock dovuti a tentative incrociati di upgrade SL  XL, tale tipo di upgrade è vietato. Una transazione che intenda prima leggere e poi modificare una risorsa deve richiedere da subito un UL e poi effettuare l’upgrade UL  XL, che è l’unico permesso. Questa situazione riguarda le transazioni 1 e 3, entre la transazione 2 effettua operazioni su risorse diverse. Affnché il sistema sia 2PL, inoltre, è necessario che la transazione 2 anticipi leggerlente l’accquisizione del lock su Y (rispetto alla scrittura w 1 (X)) per poter rilasciare in tempo il suo lock su X. Ne deriva la seguente successione di richieste e operazioni:

X Y UL 1 (x) r 1 (x) SL 2 (x) r 2 (x) UL 3 (y) r 3 (y) XL 3 (y) [upgr. U  X] w 3 (y) rel( XL 3 (y) ) XL 2 (y) rel( SL 2 (x) ) XL 1 (x) [upgr. U  X] w 1 (x) rel( XL 1 (x) ) w 2 (y) rel( XL 2 (y) ) Le tre transazioni possono eseguire il commit subito dopo la loro ultima operazione, senza problemi.

Componentistica Un negozio vende componenti elettronici descritti nel documento catalogo.xml. Di ogni componente si memorizza il costo e, per i componenti complessi, la struttura interna: <!ATTLIST Component Code ID #REQUIRED, Cost CDATA #REQUIRED> Dove Description è un elemento con solo contenuto PCDATA. 1.Estrarre il componente che ha il maggior numero di sotto-componenti (diretti e indiretti). 2.Costruire in XQuery un documento XML che, per ogni componente foglia (cio è privo di sottocomponenti), mostri la lista dei codici dei componenti che lo contengono, ordinati dal pi ù “ basso ” al pi ù “ alto ” nella gerarchia di contenimento.

Componentistica 1.Estrarre il componente che ha il maggior numero di sotto-componenti (diretti e indiretti) Ovviamente possiamo restringere la ricerca ai soli componenti di più alto livello, contenuti in Catalog let $max := max( for $c in doc("comps.xml")/Catalog/Component return count( $c//Component ) ) return doc("comps.xml")/Catalog/Component[ count(.//Component ) = $max ]

Componentistica 2.Costruire in XQuery un documento XML che, per ogni componente foglia (cio è privo di sottocomponenti), mostri la lista dei codici dei componenti che lo contengono, ordinati dal pi ù “ basso ” al pi ù “ alto ” nella gerarchia di contenimento. Data la natura ricorsiva dello schema, la prima idea è quella di usare una funzione di visita ricorsiva del documento che raggiunge tutti i componenti “foglia” e poi costruisce la lista dei componenti “dal basso all’alto” aggiungendoli “a ritroso” durante la fase di risalita. declare function local:reversepath($lc as element(), $root as element()) as element()* { for $c in $root/Component where = return ( local:reversepath($lc, $c), ) }; { for $lc in doc("comps.xml")//Component where count($lc/Component) = 0 return { local:reversepath($lc, doc("comps.xml")/Catalog) } }

Componentistica In alternativa, si può ragionare del fatto che, dato un generico componente “foglia”, la lista dei suoi “ancestors”, che lo contengono, può essere ordinata “dal basso all’alto” in base al numero totale di sottoelementi, (strettamente crescente man mano che si sale in gerarchia): let $foglie := doc("comps.xml")//Component[ count(./Component) = 0 ] for $f in $foglie let $padri := ( for $p in = ] let $numsucomp := count( $p//Component ) where $numsucomp > 0 order by $numsucomp return ) return { $padri }

Componentistica Oppure, si può ragionare del fatto che si possono collezionare genericamente tutti gli elementi che hanno una specifica foglia tra i sottoelementi. In questo caso occorre far leva sulla proprietà di XPath di restituire gli elementi in “document order”, e per rispettare il vincolo, ricorriamo alla funzione reverse() (che inverte gli elementi della sequenza passatale). for $l in doc("comps.xml")//Component[ count(./Component) = 0 ] return { reverse( for $c in = and count(./Component) > 0 ] return ) } Si lascia come utile esercizio al lettore un’analisi sul costo e la complessità computazionale delle varie alternative.

Componentistica 3. Si proponga uno schema a oggetti che rappresenti le stesse informazioni. (2 punti) class Catalog { Components: list of( Component * ) } class Component { Cost : Integer, Description : String, Subcomponents: list of( Component * ) } 4. Si formuli in OQL l’interrogazione che estrae il componente che ha il maggior numero di sotto-componenti diretti. (3 punti) select C from C in Component where count( C.Subcomponents ) = max( select count( X.Subcomponents ) from X in Components )