2009-12-13 3 views
1

Je travaille sur une instruction SQL que je n'arrive pas à comprendre. J'ai besoin de classer les résultats par ordre alphabétique, mais j'ai besoin que les «enfants» viennent juste après leur «parent» dans l'ordre. Voici un exemple simple de la table et des données avec lesquelles je travaille. Toutes les colonnes non pertinentes ont été supprimées. J'utilise SQL Server 2005. Y at-il un moyen facile de faire cela?Sql Commande Hiarchy

tblCats 
======= 

idCat | fldCatName  | idParent 
-------------------------------------- 
1  | Some Category  | null 
2  | A Category  | null 
3  | Top Category  | null 
4  | A Sub Cat   | 1 
5  | Sub Cat1   | 1 
6  | Another Cat  | 2 
7  | Last Cat   | 3 
8  | Sub Sub Cat  | 5 


Results of Sql Statement: 

A Category 
Another Cat 
Some Category 
A Sub Cat1 
Sub Cat 1 
    Sub Sub Cat 
Top Category 
Last Cat 

(Les espaces préfixés dans le résultat sont juste pour ajouter dans la compréhension des résultats, je ne veux pas les espaces préfixés dans mon résultat sql. Le résultat n'a besoin que d'être dans cet ordre.)

+0

C'est quelque chose que j'ai voulu faire pendant un certain temps, en particulier en utilisant la récursion CTE, mais je ne l'ai jamais réussi, je suis intéressé de voir s'il ya une réponse. –

+0

Je pense que vous avez 'A Category' et 'Some Category' inversé dans les résultats – Ray

+0

ouais, je l'ai fait en fait. J'ai attrapé ça et l'ai réparé. – stephenbayer

Répondre

3

Vous pouvez le faire avec une requête hiérarchique, comme ci-dessous.

Cela semble beaucoup plus compliqué que cela, en raison de l'absence d'une fonction PAD dans t-sql. La graine de la hiérarchie sont les catégories sans parents. La quatrième colonne que nous sélectionnons est leur classement par ordre alphabétique (converti en une chaîne et rembourré). Ensuite, nous unissons cela avec leurs enfants. A chaque récursion, les enfants seront tous au même niveau, donc nous pouvons obtenir leur classement par ordre alphabétique sans avoir besoin de partitionner. Nous pouvons concaténer ces classements ensemble dans l'arbre, et ordonner par là.

;WITH Hierarchy AS (
    SELECT 
     idCat, fldCatName, idParent, 
     CAST(RIGHT('00000'+ 
        CAST(ROW_NUMBER() OVER (ORDER BY fldCatName) AS varchar(8)) 
        , 5) 
      AS varchar(256)) AS strPath 
    FROM Category 
    WHERE idParent IS NULL 

    UNION ALL 

    SELECT 
     c.idCat, c.fldCatName, c.idParent, 
     CAST(h.strPath + 
      CAST(RIGHT('00000'+ 
          CAST(ROW_NUMBER() OVER (ORDER BY c.fldCatName) AS varchar(8)) 
         , 5) AS varchar(16)) 
      AS varchar(256)) 
    FROM Hierarchy h 
     INNER JOIN Category c ON c.idParent = h.idCat 
) 
SELECT idCat, fldCatName, idParent, strPath 
FROM Hierarchy 
ORDER BY strPath 

Avec vos données:

idCat fldCatName  idParent strPath 
------------------------------------------------ 
    2 A Category  NULL  00001 
    6 Another Category 2  0000100001 
    1 Some Category  NULL  00002 
    4 A Sub Category 1  0000200001 
    5 Sub Cat1   1  0000200002 
    8 Sub Sub Category 5  000020000200001 
    3 Top Category  NULL  00003 
    7 Last Category  3  0000300001 
+0

vient de le tester, ça marche! merci .. La requête prend un peu de temps, mais cela serait attendu avec la récursivité. – stephenbayer

+0

Oui, j'aurais peur. La manipulation de la chaîne ne va pas aider non plus. – Paul

1

Je ne connais aucun support inhérent à SQL Server (ou Ansi-SQL) pour cela.

Je ne suppose pas que vous considéreriez une table temporaire et une procédure stockée récursive comme un moyen "facile"? J

+0

ça me donne une idée, je vais y réfléchir ce soir. J'ai en fait trouvé une solution en manipulant les données du côté du code dans le DAL. J'obtiens l'ensemble complet et construis une table de hachage en utilisant une clé matelassée qui est basée sur les parents, puis je trie les clés. C'est un peu inefficace, mais ce n'est que quelques centaines d'enregistrements. – stephenbayer

+0

La clause récurrente WITH est la manière ANSI de gérer les requêtes hiérarchiques. –

+0

Puis-je juste dire - Toutes ces solutions de sous-interrogation ici ne sont pas différentes que de créer une table temporaire en mémoire performances. Tout va bien et dandy, mais à mon humble avis quand vous arrivez à un point où vous avez besoin d'écrire des instructions SQL récursives, vous pouvez déplacer toute cette logique dans le code de votre application.Selon votre scénario, il y a de fortes chances que vous ne manquiez pas de performance, et je garantis que votre code sera plus clair, plus lisible et plus facile à comprendre, et que vos collègues programmeurs et successeurs seront éternellement reconnaissants. –

0

La réponse de Paul est excellente, mais je pensais que je jetterais dans une autre idée pour vous. Joe Celko a une solution pour cela dans son livre SQL for Smarties (chapitre 29). Cela implique de maintenir une table séparée contenant les informations de la hiérarchie. Les insertions, les mises à jour et les suppressions sont un peu compliquées, mais les sélections sont très rapides.

Désolé, je n'ai aucun lien ou code à publier, mais si vous avez accès à ce livre, vous pouvez le trouver utile.

2

Cela peut être fait en CTE ... Est-ce ce que vous recherchez?

With MyCats (CatName, CatId, CatLevel, SortValue) 
    As 
    (Select fldCatName CatName, idCat CatId, 
     0 Level, Cast(fldCatName As varChar(200)) SortValue 
     From tblCats 
     Where idParent Is Null 
     Union All 
     Select c.fldCatName CatName, c.idCat CatID, 
     CatLevel + 1 CatLevel, 
     Cast(SortValue + '\' + fldCatName as varChar(200)) SortValue 
     From tblCats c Join MyCats p 
      On p.idCat = c.idParent) 

    Select CatName, CatId, CatLevel, SortValue 
    From MyCats 
    Order By SortValue 

EDIT: (thx à Pauls commentaire ci-dessous) Si 200 caractères ne suffit pas de tenir le plus long « chemin » de chaîne concaténée, puis modifiez la valeur aussi élevée que nécessaire ... vous pouvez faire il est aussi élevé que 8000

+0

C'est une bonne solution ... serait plus rapide à exécuter que le mien. Vous devez faire attention à ce que la hiérarchie n'atteigne pas la profondeur où les noms de champs concaténés dépassent 200 caractères. – Paul

+0

Vous venez de créer des chaînes abstraites pour représenter les noms à chaque niveau ... Ce seront des chaînes plus courtes, mais pourquoi votre solution serait-elle plus lente? –

+0

Je pensais que le CASTing supplémentaire le ralentirait un peu, mais en y réfléchissant, cela ne ferait probablement pas tellement de différence dans le grand schéma des choses. – Paul