2009-10-27 5 views
0

Utilisation d'Oracle 10.2.0.Oracle - lignes de tri naturelles sur plusieurs niveaux

J'ai une table composée d'un numéro de ligne, d'un niveau d'indentation et d'un texte. J'ai besoin d'écrire une routine pour trier «naturellement» le texte à l'intérieur d'un niveau d'indentation [qui est un enfant d'un niveau d'indentation inférieur]. J'ai une expérience limitée avec les routines analytiques et je me connecte avant/après, mais d'après ce que j'ai lu ici et ailleurs, il semble qu'ils pourraient être utilisés pour aider ma cause, mais je n'arrive pas à comprendre comment.

CREATE TABLE t (ord NUMBER(5), indent NUMBER(3), text VARCHAR2(254)); 

INSERT INTO t (ord, indent, text) VALUES (10, 0, 'A'); 
INSERT INTO t (ord, indent, text) VALUES (20, 1, 'B'); 
INSERT INTO t (ord, indent, text) VALUES (30, 1, 'C'); 
INSERT INTO t (ord, indent, text) VALUES (40, 2, 'D'); 
INSERT INTO t (ord, indent, text) VALUES (50, 2, 'Z'); 
INSERT INTO t (ord, indent, text) VALUES (60, 2, 'E'); 
INSERT INTO t (ord, indent, text) VALUES (70, 1, 'F'); 
INSERT INTO t (ord, indent, text) VALUES (80, 2, 'H'); 
INSERT INTO t (ord, indent, text) VALUES (90, 2, 'G'); 
INSERT INTO t (ord, indent, text) VALUES (100, 3, 'J'); 
INSERT INTO t (ord, indent, text) VALUES (110, 3, 'H'); 

Ce:

SELECT ord, indent, LPAD(' ', indent, ' ') || text txt FROM t; 

... Retours:

ORD  INDENT  TXT 
---------- ---------- ---------------------------------------------- 
    10   0  A 
    20   1  B 
    30   1  C 
    40   2  D 
    50   2  Z 
    60   2  E 
    70   1  F 
    80   2  H 
    90   2  G 
    100   3   J 
    110   3   H 

11 lignes sélectionnées.

Dans le cas, je l'ai défini pour vous, je dois ma routine pour définir 60 = 50 ORD et 50 = 60 ORD [les retourner] parce que E est après D et avant Z.
même avec 80 et 90 ORD [90 apportant 100 et 110 avec elle parce qu'ils lui appartiennent], 100 et 110. La sortie finale devrait être:

ORD  INDENT TXT 

10   0 A 
    20   1 B 
    30   1 C 
    40   2 D 
    50   2 E 
    60   2 Z 
    70   1 F 
    80   2 G 
    90   3 H 
    100   3 J 
    110   2 H 

Le résultat est que chaque niveau de retrait est triée par ordre alphabétique, dans son niveau d'indentation, au niveau du retrait parent.

+0

Cela ressemble à une tâche de devoirs pour moi. – Gandalf

+0

Semble assez hardcode pour un devoir, mais qui sait? – Lucero

+0

Ce n'est pas un devoir. J'ai grandement simplifié le tableau et les données pour mettre l'accent sur la réponse, pas sur les données non liées. – user55904

Répondre

1

Voici ce que j'ai commencé à travailler. Aucune idée de son efficacité sur des ensembles plus grands. Le plus dur pour moi était d'identifier le «parent» pour une rangée donnée basée uniquement sur l'indentation et l'ordre original.

WITH 
    a AS (
     SELECT 
      t.*, 
      (SELECT MAX(ord) 
       FROM t t2 
       WHERE t2.ord < t.ord AND t2.indent = t.indent-1 
      ) AS parent_ord 
     FROM 
      t 
    ) 
SELECT 
    ROWNUM*10 AS ord, 
    indent, 
    rpad(' ', LEVEL-1, ' ') || text 
FROM 
    a 
CONNECT BY 
    PRIOR ord = parent_ord 
START WITH 
    parent_ord IS NULL 
ORDER SIBLINGS BY 
    text 
+0

Hehe ... presque posté en même temps. Je dois avouer que votre façon de trouver les parents est beaucoup plus petite, j'ai eu du mal avec ça ... – Lucero

+0

Merci! Je ne pouvais pas envelopper ma tête autour de trouver le parent! Solution fantastique! – user55904

0

Bon, c'est parti. La partie difficile dans votre structure de données est que le parent n'est pas (explicitement) connu, de sorte que la première partie de la requête ne fait qu'identifier le parent selon les règles (pour chaque nœud, il subit tous les sous-nœuds dès que l'identité est plus petite ou égale au nœud de départ).

Le reste est facile, fondamentalement juste une certaine récursion avec connexion par pour obtenir les éléments dans l'ordre que vous les voulez (en les renumérotant dynamiquement).

WITH OrdWithParentInfo AS 
(SELECT ID, 
     INDENT, 
     TEXT, 
     MIN(ParentID) ParentID 
    FROM (SELECT O.*, 
       CASE 
        WHEN (CONNECT_BY_ROOT ID = ID) THEN 
        NULL 
        ELSE 
        CONNECT_BY_ROOT ID 
       END ParentID 
      FROM (SELECT ROWNUM ID, 
         INDENT, 
         TEXT 
        FROM T 
        ORDER BY ORD) O 
      WHERE (INDENT = CONNECT_BY_ROOT INDENT + 1) 
       OR (CONNECT_BY_ROOT ID = ID) 
      CONNECT BY ((ID = PRIOR ID + 1) AND (INDENT > CONNECT_BY_ROOT INDENT))) 
    GROUP BY ID, 
      INDENT, 
      TEXT) 
SELECT ROWNUM * 10 ORD, O.INDENT, O.TEXT 
FROM OrdWithParentInfo O 
START WITH O.ParentID IS NULL 
CONNECT BY O.ParentID = PRIOR ID 
ORDER SIBLINGS BY O.Text; 
+0

grands esprits se ressemblent :-) – kurosch

+0

Je suppose que oui (même si je ne me vois pas comme "grand esprit";)). C'était amusant de toute façon. – Lucero

Questions connexes