Raggruppamento "PER XML PERCORSO" di SQL Server 2005
-
22-07-2019 - |
Domanda
Sto cercando di generare un documento XML dal database di SQL Server 2005 utilizzando " FOR XML " costruire.
Esistono due semplici tabelle nel database con una relazione uno-a-molti:
1) Riviste
| Id | Number | Name |
----------------------------
| 53 | 0001 | Magazine 1 |
| 54 | 0002 | Magazine 2 |
| 55 | 0003 | Magazine 3 |
2) Articoli
| Id | Title | MagazineId | Size |
--------------------------------------
| 1 | Article 1 | 53 | 1205 |
| 2 | Article 2 | 53 | 817 |
| 3 | Article 3 | 54 | 1570 |
| 4 | Article 4 | 54 | 2510 |
| 5 | Article 5 | 55 | 910 |
Supponiamo che devo trovare tutte le riviste che hanno un articolo con dimensioni superiori a 1000 e per produrre il seguente xml:
<Magazines>
<Magazine Id="53">
<Number>0001</Number>
<Articles>
<Article Id="1">
<Title>Article 1</Title>
<Size>1205</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="54">
<Number>0002</Number>
<Articles>
<Article Id="3">
<Title>Article 3</Title>
<Size>1570</Size>
</Article>
<Article Id="4">
<Title>Article 4</Title>
<Size>2510</Size>
</Article>
</Articles>
</Magazine>
</Magazines>
Sto provando a produrre tale xml usando un " PATH " Modalità:
SELECT Magazines.Id AS "@Id",
Magazines.Number AS "Number",
Articles.Id AS "Articles/Article/@Id",
Articles.Title AS "Articles/Article/Title",
Articles.Size AS "Articles/Article/Size"
FROM Magazines INNER JOIN Articles ON Magazines.Id = Articles.MagazineId
WHERE Articles.Size > 1000
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
Produrrà il seguente xml:
<Magazines>
<Magazine Id="53">
<Number>0001</Number>
<Articles>
<Article Id="1">
<Title>Article 1</Title>
<Size>1205</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="54">
<Number>0002</Number>
<Articles>
<Article Id="3">
<Title>Article 3</Title>
<Size>1570</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="54">
<Number>0002</Number>
<Articles>
<Article Id="4">
<Title>Article 4</Title>
<Size>2510</Size>
</Article>
</Articles>
</Magazine>
</Magazines>
Quindi ci sono due elementi per la rivista con Id = " 54 " (uno per ogni articolo) e questo è il problema.
Posso riscrivere la query usando una sottoquery come questa:
SELECT M.Id AS "@Id",
M.Number AS "Number",
(SELECT Articles.Id AS "@Id",
Articles.Title AS "Title",
Articles.Size AS "Size"
FROM Articles
WHERE Articles.MagazineId = M.Id
FOR XML PATH('Article'), ROOT('Articles'), TYPE
)
FROM Magazines AS M
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
E questo produce il seguente xml:
<Magazines>
<Magazine Id="53">
<Number>0001</Number>
<Articles>
<Article Id="1">
<Title>Article 1</Title>
<Size>1205</Size>
</Article>
<Article Id="2">
<Title>Article 2</Title>
<Size>817</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="54">
<Number>0002</Number>
<Articles>
<Article Id="3">
<Title>Article 3</Title>
<Size>1570</Size>
</Article>
<Article Id="4">
<Title>Article 4</Title>
<Size>2510</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="55">
<Number>0003</Number>
<Articles>
<Article Id="5">
<Title>Article 5</Title>
<Size>910</Size>
</Article>
</Articles>
</Magazine>
</Magazines>
Ma usando una subquery non riesco a filtrare le riviste per colonne di articoli (senza complesse query aggiuntive).
Il " FOR XML AUTO " la modalità non è adatta, perché è molto semplice e non supporta alcuni "PERCORSO" funzioni (come attributi che utilizzano @, ROOT, ecc.)
Quindi, c'è qualche possibilità in " PERCORSO " modalità per raggruppare i dati della tabella interna come in " AUTO " modalità?
Grazie!
Soluzione
Bene, puoi fare un passo in più specificando la " size > 1000 " all'interno della tua sottoquery:
SELECT M.Id AS "@Id",
M.Number AS "Number",
(SELECT Articles.Id AS "@Id",
Articles.Title AS "Title",
Articles.Size AS "Size"
FROM Articles
WHERE Articles.MagazineId = M.Id
AND Articles.Size > 1000
FOR XML PATH('Article'), ROOT('Articles'), TYPE
)
FROM Magazines AS M
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
Quello che ti manca ora è il fatto che otterrai ancora riviste che non hanno articoli con una dimensione > 1000. Puoi eliminare qualcosa del genere:
SELECT M.Id AS "@Id",
M.Number AS "Number",
(SELECT Articles.Id AS "@Id",
Articles.Title AS "Title",
Articles.Size AS "Size"
FROM Articles
WHERE Articles.MagazineId = M.Id
AND Articles.Size > 1000
FOR XML PATH('Article'), ROOT('Articles'), TYPE
)
FROM Magazines AS M
WHERE EXISTS(SELECT * FROM Articles
WHERE Articles.MagazineId = M.Id
AND Articles.Size > 1000)
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
(non testato, non ho un server SQL a portata di mano in questo momento).
Funziona per te? Ti dà le riviste e gli articoli che stai cercando?
Marc
Altri suggerimenti
Usa esplicito FOR XML. È il codice più lungo da scrivere ma scompaiono i problemi di prestazioni provocati dalle query secondarie.