Interrogazioni multiple di SQL Server 2005 con MARS

di Andrea Montanari, in SQL Server,

É prassi comune, al di là della tradizionale normalizzazione delle basi di dati, avere la necessità di ottenere tramite interrogazioni dati legati gerarchicamente tra loro in soluzioni di tipo master-detail, dove la natura gerarchica delle informazioni da presentare possa, ma non necessariamente, avere anche rappresentazione di tipo drill-down.

In questa esperienza utilizzeremo uno schema scaricabile come allegato a tutti noi caro verso l'ora di pranzo; lo schema è volutamente semplice basandosi su 2 relazioni principali, [Ristorazione].[Menu] e [Ristorazione].[Pietanze] vincolate tra loro da una relazione interdipendente, [Ristorazione].[Menu_Pietanze], con legame reciproco uno a molti.

Esplosione di join

Una prima soluzione può derivare dalla denormalizzazione in fase di selezione nella proiezione dovuta all'esplosione delle join coinvolte nella normalizzazione strutturale delle relazioni. Chiaramente non è una soluzione gerarchica, ma semplicemente una proiezione monotabellare del risultato.

SET NOCOUNT ON;
SELECT m.Descrizione AS [Menu], p.Descrizione AS [Pietanza]
  FROM [Ristorazione].[Menu] m
    JOIN [Ristorazione].[Menu_Pietanze] mp ON mp.IdMenu = m.Id
    JOIN [Ristorazione].[Pietanze] p ON p.Id = mp.IdPietanza
  ORDER BY m.[Id], [Menu], [Sequenza];

La query produce un risultato monotabellare come di seguito.

MenuPietanza
Il gusto della carneTagliatelle al ragout di coniglio
Il gusto della carneArrosto di spalla di vitello in 'Pan Brioche' à la glace
Il gusto della carneCosciotto di lattonzolo ai profumi di sottobosco
Il gusto della carnePatate al sale profumato e verdure di campo
Sapori di mari viciniPassatelli ai cuori di carciofo e calamari
Sapori di mari viciniFiletto di tonno su misticanza di verdure con lenticchie di cappero al balsamico
Sapori di mari viciniPunte di asparago all'olio di frantoio
Piccoli OspitiCotoletta alla milanese
Piccoli OspitiPatate fritte

Questa soluzione, implementata completamente lato server e direttamente su SQL Server tramite una query unica, è probabilmente la più performante dal punto di vista prestazionale, specialmente per quanto concerne le risorse del server, al di là del consumo dei dati tramite gli appositi modelli di accesso, ma potrebbe non esaudire le nostre necessità gerarchiche, visto che, come già enunciato, il risultato è un appiattimento monotabellare.

Aggiungiamo anche, come ulteriore valutazione negativa, che l'indicazione della descrizione del menu è ridondante, ripetuta per ogni singola riga restituita per ogni componente del menu. Questo potrebbe essere irrilevante (anche dal punto di vista di costo tradotto in termini di memoria del resultset restituito) come nella nostra esperienza corrente, ma tale valutazione potrebbe anche risultare diversa.

Un ulteriore inconveniente di questa soluzione è costituito dalla scarsa capacità di aggiornamento, sicuramente per la parte "molti" della relazione.

A parziale vantaggio discriminante, ricordiamo però che le griglie in commercio che consentono il raggruppamento per riga/colonna sono numerose, ovviando, ma solo a livello di presentation layer, la ridondanza di informazione.

Passiamo così a vedere le eventuali risorse disponibili lato client al fine di esaudire le nostre richieste.

DataSet e DataTable

ADO.NET, la parte del .NET Framework dedicata all'accesso a database e sorgenti dati relazionali, mette a disposizione un modello ad oggetti completo e facilmente utilizzabile.

La soluzione tipicamente utilizzata si basa appunto su tale modello, utilizzando un oggetto DataSet che contiene e gestisce uno o più oggetti DataTable tra loro eventualmente legati da DataRelation.

Il codice che possiamo scrivere per accedere a tale funzionalità può assomigliare a quanto segue.

Dim con As New SqlClient.SqlConnection
With con
  .ConnectionString = "Server=(Local);Database=tempdb;Trusted_Connection=True;Persist Security Info=False"
  .Open()
End With

Dim ds As New DataSet("Menu della casa")
Dim dtMenu As New DataTable("Menu")
Dim dtPietanze As New DataTable("PietanzeEsplose")
ds.Tables.Add(dtMenu)
ds.Tables.Add(dtPietanze)

Dim cmd As New SqlClient.SqlCommand
With cmd
  .CommandText = "Select Id, Descrizione FROM [Ristorazione].[Menu] ORDER BY Id;"
  .CommandType = CommandType.Text
  .CommandTimeout = 0
  .Connection = con
End With
Dim ad As New SqlClient.SqlDataAdapter(cmd)
ad.Fill(ds, "Menu")

cmd = New SqlClient.SqlCommand
With cmd
  .CommandText = "SELECT p.Descrizione, mp.IdMenu FROM [Ristorazione].[Pietanze] p JOIN [Ristorazione].[Menu_Pietanze] mp ON p.Id = mp.IdPietanza ORDER BY IdMenu, Sequenza;"
  .CommandType = CommandType.Text
  .CommandTimeout = 0
  .Connection = con
End With
ad = New SqlClient.SqlDataAdapter(cmd)
ad.Fill(ds, "PietanzeEsplose")
cmd.Dispose()
cmd = Nothing
con.Close()
con = Nothing

Dim drMenuPietanze As New DataRelation("MenuPietanze", _
  ds.Tables("Menu").Columns("Id"), _
  ds.Tables("PietanzeEsplose").Columns("IdMenu"))
ds.Relations.Add(drMenuPietanze)

For Each dr As DataRow In ds.Tables("Menu").Rows
  Debug.WriteLine(dr("Descrizione"))

  For Each drPietanza As DataRow In dr.GetChildRows("MenuPietanze")
    Debug.WriteLine(ControlChars.Tab & drPietanza("Descrizione"))
  Next
Next
ds.Dispose()

Il risultato, a prescindere da come lo consumiamo a livello di presentation layer, risulta essere qualcosa di simile a quanto segue.

Menu 
Il gusto della carneCiclo interno sulla relazione...
Tagliatelle al ragout di coniglio
Arrosto di spalla di vitello in 'Pan Brioche' à la glace
Cosciotto di lattonzolo ai profumi di sottobosco
Patate al saleprofumato e verdure di campo
Sapori di mari viciniCiclo interno sulla relazione...
Passatelli ai cuori di carciofo e calamari
Filetto di tonno su misticanza di verdure con lenticchie di cappero al balsamico
Punte di asparago all'olio di frantoio
Piccoli OspitiCiclo interno sulla relazione...
Cotoletta alla milanese
Patate fritte
3 pagine in totale: 1 2 3

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti