2009-07-06 9 views
1

J'ai un tableau des catégories. Chaque catégorie peut être une catégorie de niveau racine (parent est NULL) ou avoir un parent qui est une catégorie de niveau racine. Il ne peut y avoir plus d'un niveau de nidification.SQL - Commande par critères multiples

je la structure de tableau suivant:

Categories Table Structure http://img16.imageshack.us/img16/8569/categoriesi.png

Est-il possible que je pourrais utiliser une requête qui a produit la sortie suivante:

 
Free Stuff 
Hardware 
Movies 
CatA 
CatB 
CatC 
Software 
Apples 
CatD 
CatE 

Ainsi, les résultats sont classés par niveau supérieur catégorie, puis après chaque catégorie de niveau supérieur, les sous-catégories de cette catégorie sont répertoriées?

Ce n'est pas vraiment une commande par Parent ou Nom, mais une combinaison des deux. J'utilise SQL Server.

+1

Vouliez-vous dire que: Free Stuff de Aa2 Matériel Films Cata etc ... ? –

Répondre

1

Ok, on y va:

with foo as 
(
select 1 as id, null as parent, 'CatA' as cat from dual 
union select 2, null, 'CatB' from dual 
union select 3, null, 'CatC' from dual 
union select 4, 1, 'SubCatA_1' from dual 
union select 5, 1, 'SubCatA_2' from dual 
union select 6, 2, 'SubCatB_1' from dual 
union select 7, 2, 'SubCatB_2' from dual 
) 
select child.cat 
from foo parent right outer join foo child on parent.id = child.parent 
order by case when parent.id is not null then parent.cat else child.cat end, 
     case when parent.id is not null then 1 else 0 end 

Résultat:

CatA 
SubCatA_1 
SubCatA_2 
CatB 
SubCatB_1 
SubCatB_2 
CatC 

Modifier - Changement de solution inspirer de l'ordre de van par! Beaucoup plus simple de cette façon.

+0

Cette solution est interrompue dès que vous ajoutez un autre niveau à l'arborescence. C'est à dire. Si j'ajoute union, sélectionnez 8, 7, 'SubCatB_2.1' à partir du dual, alors SubCatB_2.1 apparaît sous CatC dans le résultat. –

+0

"Il ne peut y avoir plus d'un niveau d'imbrication.". Aucun intérêt à discuter d'une très grande solution avec un très grand design ... –

+0

C'était l'un des rares qui semblait réellement fonctionner, et je peux aussi y faire face avec une relative facilité :) Merci Scirpi0. – Echilon

1

Pas tout à fait sûr de vos questions mais il semble que PARTITION PAR pourrait être utile pour vous. Il y a un bon message d'introduction sur PARTITION BY here.

4

Il me semble que vous cherchez à aplatir et à ordonner votre hiérarchie, la façon la moins chère d'obtenir cette commande serait de stocker une colonne supplémentaire dans la table qui a le chemin complet.

Ainsi, par exemple:

 
Name   | Full Path 
Free Stuff  | Free Stuff 
aa2    | Free Stuff - aa2    

Une fois que vous enregistrez le chemin complet, vous pouvez commander sur elle. Si vous avez seulement une profondeur de un, vous pouvez générer automatiquement une chaîne à cet effet avec une seule sous-requête (et l'ordre dessus), mais cette solution ne fonctionne pas aussi facilement quand elle est profonde.

Une autre option consiste à déplacer tout cela dans une table temporaire et de calculer le chemin complet à la demande. Mais c'est assez cher.

2

Vous pouvez faire en sorte que la table se regarde d'elle-même, en commençant par le nom du parent, puis le nom de l'enfant.

select categories.Name AS DisplayName 
from  categories LEFT OUTER JOIN 
     categories AS parentTable ON categories.Parent = parentTable.ID 
order by parentTable.Name, DisplayName 
+0

Cela ne fonctionnerait que pour une hiérarchie d'au plus deux niveaux de profondeur. –

+0

J'étais sur le point de poster en disant que cela ne fonctionne que si vous changez la LEFT JOIN vers un DROIT, alors j'ai réalisé que j'avais fait une faute de frappe;). – Echilon

1

Ici vous avez un exemple de travail complet utilisant une expression de table commune resursive.

DECLARE @categories TABLE 
(
    ID INT NOT NULL, 
    [Name] VARCHAR(50), 
    Parent INT NULL 
); 

INSERT INTO @categories VALUES (4, 'Free Stuff', NULL); 
INSERT INTO @categories VALUES (1, 'Hardware', NULL); 
INSERT INTO @categories VALUES (3, 'Movies', NULL); 
INSERT INTO @categories VALUES (2, 'Software', NULL); 
INSERT INTO @categories VALUES (10, 'a', 0); 
INSERT INTO @categories VALUES (12, 'apples', 2); 
INSERT INTO @categories VALUES (8, 'catD', 2); 
INSERT INTO @categories VALUES (9, 'catE', 2); 
INSERT INTO @categories VALUES (5, 'catA', 3); 
INSERT INTO @categories VALUES (6, 'catB', 3); 
INSERT INTO @categories VALUES (7, 'catC', 3); 
INSERT INTO @categories VALUES (11, 'aa2', 4); 

WITH categories(ID, Name, Parent, HierarchicalName) 
AS 
(
    SELECT 
     c.ID 
     , c.[Name] 
     , c.Parent 
     , CAST(c.[Name] AS VARCHAR(200)) AS HierarchicalName 
    FROM @categories c 
    WHERE c.Parent IS NULL 

    UNION ALL 

    SELECT 
     c.ID 
     , c.[Name] 
     , c.Parent 
     , CAST(pc.HierarchicalName + c.[Name] AS VARCHAR(200)) 
    FROM @categories c 
    JOIN categories pc ON c.Parent = pc.ID 
) 
SELECT c.* 
FROM categories c 
ORDER BY c.HierarchicalName 
1
SELECT 
    ID, 
    Name, 
    Parent, 
    RIGHT(
    '000000000000000' + 
    CASE WHEN Parent IS NULL 
    THEN CONVERT(VARCHAR, Id) 
    ELSE CONVERT(VARCHAR, Parent) 
    END, 15 
) 
    + '_' + CASE WHEN Parent IS NULL THEN '0' ELSE '1' END 
    + '_' + Name 
FROM 
    categories 
ORDER BY 
    4 

Le rembourrage est longue pour tenir compte du fait que le type de données INT SQL Server passe de 2.147.483.648 par 2147483647.

Vous pouvez ORDER BY l'expression directement, pas besoin d'utiliser ORDER BY 4. C'était juste pour montrer ce qu'il triait.

Il est à noter que cette expression ne peut utiliser aucun index.Cela signifie que le tri d'une grande table sera lent.

Questions connexes