Specifica del livello di nidificazione nelle relazioni ricorsive mediante sql:max-depth
Quando nei database relazionali una tabella viene coinvolta in una relazione con sé stessa, si parla di relazione ricorsiva. In una relazione supervisore-supervisionato (supervisor-supervisee), ad esempio, una tabella in cui sono archiviati i record dei dipendenti è coinvolta in una relazione con sé stessa. In questo caso, la stessa tabella dei dipendenti ricopre il ruolo di supervisore da un lato della relazione e di supervisionato dall'altro lato.
Gli schemi di mapping possono includere relazioni ricorsive nelle quali un elemento e il rispettivo predecessore sono dello stesso tipo.
Esempio A
Si consideri la tabella seguente:
Emp (EmployeeID, FirstName, LastName, ReportsTo)
Nella colonna ReportsTo di questa tabella viene archiviato l'ID dipendente del responsabile.
Si supponga di voler generare una gerarchia XML di dipendenti nella quale il dipendente responsabile si trova al primo posto e i dipendenti che sono sotto la supervisione del responsabile vengono visualizzati nella gerarchia corrispondente, come mostrato nel frammento XML di esempio seguente. Ciò che questo frammento mostra è l'albero ricorsivo per il dipendente 1.
<?xml version="1.0" encoding="utf-8" ?>
<root>
<Emp FirstName="Nancy" EmployeeID="1" LastName="Devolio">
<Emp FirstName="Andrew" EmployeeID="2" LastName="Fuller" />
<Emp FirstName="Janet" EmployeeID="3" LastName="Leverling">
<Emp FirstName="Margaret" EmployeeID="4" LastName="Peacock">
<Emp FirstName="Steven" EmployeeID="5" LastName="Devolio">
...
...
</root>
In questo frammento il dipendente 5 è sotto la supervisione del dipendente 4, il dipendente 4 è sotto la supervisione del dipendente 3 e i dipendenti 3 e 2 sono sotto la supervisione del dipendente 1.
Per produrre questo risultato, è possibile utilizzare lo schema XSD seguente e specificare una query XPath in tale schema. Lo schema descrive un <elemento Emp> di tipo EmployeeType, costituito da un <elemento figlio Emp> dello stesso tipo, EmployeeType. Si tratta di una relazione ricorsiva (l'elemento e il rispettivo predecessore sono dello stesso tipo). Inoltre, lo schema usa una <relazione sql:relationship> per descrivere la relazione padre-figlio tra il supervisore e la supervisione. Si noti che in questa <relazione sql:relationship> Emp è sia l'elemento padre che la tabella figlio.
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:dt="urn:schemas-microsoft-com:datatypes"
xmlns:sql="urn:schemas-microsoft-com:mapping-schema">
<xsd:annotation>
<xsd:appinfo>
<sql:relationship name="SupervisorSupervisee"
parent="Emp"
parent-key="EmployeeID"
child="Emp"
child-key="ReportsTo" />
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="Emp" type="EmployeeType"
sql:relation="Emp"
sql:key-fields="EmployeeID"
sql:limit-field="ReportsTo" />
<xsd:complexType name="EmployeeType">
<xsd:sequence>
<xsd:element name="Emp" type="EmployeeType"
sql:relation="Emp"
sql:key-fields="EmployeeID"
sql:relationship="SupervisorSupervisee"
sql:max-depth="6" />
</xsd:sequence>
<xsd:attribute name="EmployeeID" type="xsd:ID" />
<xsd:attribute name="FirstName" type="xsd:string"/>
<xsd:attribute name="LastName" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>
Dal momento che la relazione è ricorsiva, è necessario trovare un modo per specificare il livello di nidificazione della ricorsione nello schema. In caso contrario, il risultato sarà una ricorsione infinita (il dipendente che è sotto la supervisione del dipendente che, a sua volta è sotto la supervisione del dipendente e così via). L'annotazione sql:max-depth
consente di specificare il livello di nidificazione da raggiungere nella ricorsione. In questo particolare esempio, per specificare un valore per sql:max-depth
, è necessario conoscere il livello di nidificazione della gerarchia di gestione nella società.
Nota
Lo schema specifica l'annotazione sql:limit-field
ma non specifica l'annotazione sql:limit-value
. Questa situazione limita il nodo principale nella gerarchia risultante ai soli dipendenti che non sono sottoposti ad alcuna supervisione (ReportsTo è NULL. sql:limit-field
Se si specifica e non si specifica sql:limit-value
l'annotazione (il valore predefinito è NULL), questa operazione viene eseguita. Se si desidera che l'XML risultante includa ogni possibile albero gerarchico (l'albero gerarchico per ogni dipendente della tabella), rimuovere l'annotazione sql:limit-field
dallo schema.
Nota
Nella procedura riportata di seguito viene utilizzato il database tempdb.
Per testare una query Xpath di esempio sullo schema
Creare una tabella di esempio chiamata Emp nel database tempdb al quale punta la radice virtuale.
USE tempdb CREATE TABLE Emp ( EmployeeID int primary key, FirstName varchar(20), LastName varchar(20), ReportsTo int)
Aggiungere i dati di esempio seguenti:
INSERT INTO Emp values (1, 'Nancy', 'Devolio',NULL) INSERT INTO Emp values (2, 'Andrew', 'Fuller',1) INSERT INTO Emp values (3, 'Janet', 'Leverling',1) INSERT INTO Emp values (4, 'Margaret', 'Peacock',3) INSERT INTO Emp values (5, 'Steven', 'Devolio',4) INSERT INTO Emp values (6, 'Nancy', 'Buchanan',5) INSERT INTO Emp values (7, 'Michael', 'Suyama',6)
Copiare il codice dello schema precedente e incollarlo in un file di testo. Salvare il file con il nome maxDepth.xml.
Copiare il modello seguente e incollarlo in un file di testo. Salvare il file con il nome maxDepthT.xml nella stessa directory nella quale è stato salvato maxDepth.xml. La query nel modello restituisce tutti i dipendenti della tabella Emp.
<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql"> <sql:xpath-query mapping-schema="maxDepth.xml"> /Emp </sql:xpath-query> </ROOT>
Il percorso di directory specificato per lo schema di mapping (maxDepth.xml) è relativo alla directory nella quale viene salvato il modello. È possibile specificare anche un percorso assoluto, ad esempio:
mapping-schema="C:\MyDir\maxDepth.xml"
Creare e utilizzare lo script di test SQLXML 4.0 (Sqlxml4test.vbs) per eseguire il modello. Per altre informazioni, vedere Uso di ADO per eseguire query SQLXML 4.0.
Risultato:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<Emp FirstName="Nancy" EmployeeID="1" LastName="Devolio">
<Emp FirstName="Andrew" EmployeeID="2" LastName="Fuller" />
<Emp FirstName="Janet" EmployeeID="3" LastName="Leverling">
<Emp FirstName="Margaret" EmployeeID="4" LastName="Peacock">
<Emp FirstName="Steven" EmployeeID="5" LastName="Devolio">
<Emp FirstName="Nancy" EmployeeID="6" LastName="Buchanan">
<Emp FirstName="Michael" EmployeeID="7" LastName="Suyama" />
</Emp>
</Emp>
</Emp>
</Emp>
</Emp>
</root>
Nota
Per produrre livelli di nidificazione di gerarchie diverse nel risultato, modificare il valore dell'annotazione sql:max-depth
nello schema ed eseguire nuovamente il modello dopo ogni modifica.
Nello schema precedente tutti gli <elementi Emp> hanno esattamente lo stesso set di attributi (EmployeeID, FirstName e LastName). Lo schema seguente è stato leggermente modificato per restituire un attributo ReportsTo aggiuntivo per tutti gli <elementi Emp> che segnalano a un gestore.
Questo frammento XML, ad esempio, mostra i subalterni del dipendente 1:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<Emp FirstName="Nancy" EmployeeID="1" LastName="Devolio">
<Emp FirstName="Andrew" EmployeeID="2"
ReportsTo="1" LastName="Fuller" />
<Emp FirstName="Janet" EmployeeID="3"
ReportsTo="1" LastName="Leverling">
...
...
Questo è lo schema corretto:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:dt="urn:schemas-microsoft-com:datatypes"
xmlns:sql="urn:schemas-microsoft-com:mapping-schema">
<xsd:annotation>
<xsd:documentation>
Customer-Order-Order Details Schema
Copyright 2000 Microsoft. All rights reserved.
</xsd:documentation>
<xsd:appinfo>
<sql:relationship name="SupervisorSupervisee"
parent="Emp"
parent-key="EmployeeID"
child="Emp"
child-key="ReportsTo" />
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="Emp"
type="EmpType"
sql:relation="Emp"
sql:key-fields="EmployeeID"
sql:limit-field="ReportsTo" />
<xsd:complexType name="EmpType">
<xsd:sequence>
<xsd:element name="Emp"
type="EmpType"
sql:relation="Emp"
sql:key-fields="EmployeeID"
sql:relationship="SupervisorSupervisee"
sql:max-depth="6"/>
</xsd:sequence>
<xsd:attribute name="EmployeeID" type="xsd:int" />
<xsd:attribute name="FirstName" type="xsd:string"/>
<xsd:attribute name="LastName" type="xsd:string"/>
<xsd:attribute name="ReportsTo" type="xsd:int" />
</xsd:complexType>
</xsd:schema>
Annotazione sql:max-depth
In un schema costituito da relazioni ricorsive il livello di nidificazione della ricorsione deve essere specificata in modo esplicito. Questa operazione è necessaria per produrre correttamente la query FOR XML EXPLICIT corrispondente che restituisce i risultati richiesti.
Utilizzare l'annotazione sql:max-depth
nello schema per specificare il livello di nidificazione della ricorsione in una relazione ricorsiva descritta nello schema. Il valore dell'annotazione sql:max-depth
è un numero intero positivo (da 1 a 50) che indica il numero di ricorsioni: un valore pari a 1 arresta la ricorsione all'elemento per il quale viene specificata l'annotazione sql:max-depth
. Il valore 2 arresta la ricorsione al livello successivo dall'elemento in corrispondenza del quale sql:max-depth
viene specificata e così via.
Nota
Nell'implementazione sottostante, una query XPath specificata in base a uno schema di mapping viene convertita in select ... Query FOR XML EXPLICIT. Questa query richiede che venga specificato un livello di nidificazione limitato della ricorsione. Più alto è il valore specificato per sql:max-depth
, più grande sarà la query FOR XML EXPLICIT che verrà generata, con un probabile rallentamento del tempo di recupero.
Nota
Gli updategram e il caricamento bulk XML ignorano l'annotazione max-depth, pertanto gli inserimenti o gli aggiornamenti ricorsivi si verificheranno indipendentemente dal valore specificato per max-depth.
Specifica di sql:max-depth per gli elementi complessi
L'annotazione sql:max-depth
può essere specificata su qualsiasi elemento di contenuto complesso.
Elementi ricorsivi
Se viene specificato un valore sql:max-depth
sia per l'elemento padre che per l'elemento figlio in una relazione ricorsiva, l'annotazione sql:max-depth
specificata per l'elemento padre ha la precedenza. Nello schema seguente, ad esempio, l'annotazione sql:max-depth
viene specificata per gli elementi dipendente sia padre che figlio. In questo caso, sql:max-depth=4
, specificato nell'elemento <padre Emp> (che svolge un ruolo di supervisore), ha la precedenza. L'oggetto sql:max-depth
specificato nell'elemento Emp> figlio< (che gioca un ruolo di supervisione) viene ignorato.
Esempio B
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:dt="urn:schemas-microsoft-com:datatypes"
xmlns:sql="urn:schemas-microsoft-com:mapping-schema">
<xsd:annotation>
<xsd:appinfo>
<sql:relationship name="SupervisorSupervisee"
parent="Emp"
parent-key="EmployeeID"
child="Emp"
child-key="ReportsTo" />
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="Emp" type="EmployeeType"
sql:relation="Emp"
sql:key-fields="EmployeeID"
sql:limit-field="ReportsTo"
sql:max-depth="3" />
<xsd:complexType name="EmployeeType">
<xsd:sequence>
<xsd:element name="Emp" type="EmployeeType"
sql:relation="Emp"
sql:key-fields="EmployeeID"
sql:relationship="SupervisorSupervisee"
sql:max-depth="2" />
</xsd:sequence>
<xsd:attribute name="EmployeeID" type="xsd:ID" />
<xsd:attribute name="FirstName" type="xsd:string"/>
<xsd:attribute name="LastName" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>
Per testare questo schema, seguire i passaggi forniti per il precedente esempio A in questo argomento.
Elementi non ricorsivi
Se l'annotazione sql:max-depth
viene specificata per un elemento dello schema che non determina una ricorsione, viene ignorata. Nello schema seguente un <elemento Emp> è costituito da un <elemento figlio Constant> , che a sua volta ha un <elemento figlio Emp> .
In questo schema, l'annotazione sql:max-depth
specificata nell'elemento <Constant> viene ignorata perché non esiste alcuna ricorsione tra l'elemento <padre Emp> e l'elemento <figlio Constant> . Ma c'è ricorsione tra l'antenato <Emp> e il <figlio Emp> . Lo schema specifica l'annotazione sql:max-depth
per entrambi. Di conseguenza, l'annotazione sql:max-depth
specificata nel predecessore (<Emp> nel ruolo supervisore) ha la precedenza.
Esempio C
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:sql="urn:schemas-microsoft-com:mapping-schema">
<xsd:annotation>
<xsd:appinfo>
<sql:relationship name="SupervisorSupervisee"
parent="Emp"
child="Emp"
parent-key="EmployeeID"
child-key="ReportsTo"/>
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="Emp"
sql:relation="Emp"
type="EmpType"
sql:limit-field="ReportsTo"
sql:max-depth="1" />
<xsd:complexType name="EmpType" >
<xsd:sequence>
<xsd:element name="Constant"
sql:is-constant="1"
sql:max-depth="20" >
<xsd:complexType >
<xsd:sequence>
<xsd:element name="Emp"
sql:relation="Emp" type="EmpType"
sql:relationship="SupervisorSupervisee"
sql:max-depth="3" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="EmployeeID" type="xsd:int" />
</xsd:complexType>
</xsd:schema>
Per testare questo schema, seguire i passaggi forniti per il precedente esempio A in questo argomento.
Tipi complessi derivati dalla restrizione
Se si dispone di una derivazione di tipo complesso per <restrizione>, gli elementi del tipo complesso di base corrispondente non possono specificare l'annotazione sql:max-depth
. In questi casi, l'annotazione sql:max-depth
può essere aggiunta all'elemento del tipo derivato.
Se invece si dispone di una derivazione di tipo complesso per <estensione>, gli elementi del tipo complesso di base corrispondente possono specificare l'annotazione sql:max-depth
.
Lo schema XSD seguente, ad esempio, genera un errore in quanto l'annotazione sql:max-depth
viene specificata per il tipo di base. Questa annotazione non è supportata in un tipo derivato dalla <restrizione> da un altro tipo. Per correggere questo problema, è necessario modificare lo schema e specificare l'annotazione sql:max-depth
per l'elemento nel tipo derivato.
Esempio D
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:dt="urn:schemas-microsoft-com:datatypes"
xmlns:msdata="urn:schemas-microsoft-com:mapping-schema">
<xsd:complexType name="CustomerBaseType">
<xsd:sequence>
<xsd:element name="CID" msdata:field="CustomerID" />
<xsd:element name="CompanyName"/>
<xsd:element name="Customers" msdata:max-depth="3">
<xsd:annotation>
<xsd:appinfo>
<msdata:relationship
parent="Customers"
parent-key="CustomerID"
child-key="CustomerID"
child="Customers" />
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Customers" type="CustomerType"/>
<xsd:complexType name="CustomerType">
<xsd:complexContent>
<xsd:restriction base="CustomerBaseType">
<xsd:sequence>
<xsd:element name="CID"
type="xsd:string"/>
<xsd:element name="CompanyName"
type="xsd:string"
msdata:field="CName" />
<xsd:element name="Customers"
type="CustomerType" />
</xsd:sequence>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>
Nello schema sql:max-depth
viene specificato per un tipo complesso CustomerBaseType
. Lo schema specifica anche un <elemento Customer> di tipo CustomerType
, derivato da CustomerBaseType
. Una query XPath specificata per tale schema genererà un errore, in quanto sql:max-depth
non è supportato per un elemento definito in un tipo restriction di base.
Schemi con una gerarchia profonda
Si potrebbe avere uno schema che include una gerarchia profonda nella quale un elemento contiene un elemento figlio che, a sua volta, contiene un altro elemento figlio e così via. Se l'annotazione sql:max-depth
specificata in uno schema di questo tipo genera un documento XML che include una gerarchia di più di 500 livelli (con l'elemento di livello superiore al livello 1, il rispettivo figlio al livello 2 e così via), viene restituito un errore.