Question

J'ai une table qui stocke des informations hiérarchiques en utilisant le modèle Liste contiguïté. (Utilise une clé d'auto-référentiel. - exemple ci-dessous Ce tableau peut paraître familier ):

category_id name                 parent
----------- -------------------- -----------
1           ELECTRONICS          NULL
2           TELEVISIONS          1
3           TUBE                 2
4           LCD                  2
5           PLASMA               2
6           PORTABLE ELECTRONICS 1
7           MP3 PLAYERS          6
8           FLASH                7
9           CD PLAYERS           6
10          2 WAY RADIOS         6

Quelle est la meilleure méthode pour "aplatir" les données ci-dessus dans quelque chose comme ça?

category_id lvl1        lvl2        lvl3        lvl4
----------- ----------- ----------- ----------- -----------
1           1           NULL        NULL        NULL
2           1           2           NULL        NULL
6           1           6           NULL        NULL
3           1           2           3           NULL
4           1           2           4           NULL
5           1           2           5           NULL
7           1           6           7           NULL
9           1           6           9           NULL
10          1           6           10          NULL
8           1           6           7           8

Chaque ligne est un « chemin » dans la hiérarchie, sauf qu'il ya une ligne pour chaque noeud (et pas seulement chaque nœud feuille ). La colonne category_id représente le nœud courant et les colonnes « LVL » sont ses ancêtres. La valeur du nœud actuel doit également être dans la colonne lvl le plus à droite. La valeur dans la colonne lvl1 toujours représenter le nœud racine, les valeurs lvl2 toujours représenter les descendants directs de lvl1, et ainsi de suite.

Si possible, la méthode pour générer cette sortie serait en SQL, et travaillerait pour les hiérarchies n-tiers.

Était-ce utile?

La solution

Pour faire des requêtes à plusieurs niveaux à travers une simple liste de contiguïté implique invariablement l'auto-gauche rejoint. Il est facile de faire une table aligné à droite:

SELECT category.category_id,
    ancestor4.category_id AS lvl4,
    ancestor3.category_id AS lvl3,
    ancestor2.category_id AS lvl2,
    ancestor1.category_id AS lvl1
FROM categories AS category
    LEFT JOIN categories AS ancestor1 ON ancestor1.category_id=category.category_id
    LEFT JOIN categories AS ancestor2 ON ancestor2.category_id=ancestor1.parent
    LEFT JOIN categories AS ancestor3 ON ancestor3.category_id=ancestor2.parent
    LEFT JOIN categories AS ancestor4 ON ancestor4.category_id=ancestor3.parent;

à gauche aligner comme votre exemple est un peu plus délicat. Cela vient à l'esprit:

SELECT category.category_id,
    ancestor1.category_id AS lvl1,
    ancestor2.category_id AS lvl2,
    ancestor3.category_id AS lvl3,
    ancestor4.category_id AS lvl4
FROM categories AS category
    LEFT JOIN categories AS ancestor1 ON ancestor1.parent IS NULL
    LEFT JOIN categories AS ancestor2 ON ancestor1.category_id<>category.category_id AND ancestor2.parent=ancestor1.category_id
    LEFT JOIN categories AS ancestor3 ON ancestor2.category_id<>category.category_id AND ancestor3.parent=ancestor2.category_id
    LEFT JOIN categories AS ancestor4 ON ancestor3.category_id<>category.category_id AND ancestor4.parent=ancestor3.category_id
WHERE
    ancestor1.category_id=category.category_id OR
    ancestor2.category_id=category.category_id OR
    ancestor3.category_id=category.category_id OR
    ancestor4.category_id=category.category_id;
  

travaillerait pour les hiérarchies n-tiers.

Désolé, les requêtes de profondeur arbitraire ne sont pas possibles dans le modèle contiguïté liste. Si vous faites ce genre de requête beaucoup, vous devez changer votre schéma à l'un des

Autres conseils

Comme mentionné précédemment, SQL n'a aucun moyen propre à mettre en œuvre des tables avec des variables dynamiquement le nombre de colonnes. Les deux seules solutions que j'ai utilisées avant sont les suivants: 1. Un nombre fixe d'auto-joint, ce qui donne un nombre fixe de colonnes (AS par BobInce) 2. Générer les résultats sous forme de chaîne dans une seule colonne

La seconde semble grotesque au départ; stocker les ID comme chaîne ?! Mais lorsque la sortie est formaté en XML ou quelque chose, les gens ne semblent pas à l'esprit tant.

De même, cela est très peu utile si vous voulez ensuite rejoindre sur les résultats dans SQL. Si le résultat doit être fourni à une application, il peut être très approprié. Mais, personnellement, je préfère faire l'aplanissement dans l'application plutôt que SQL


Je suis coincé ici sur un écran de 10 pouces sans accès à SQL je ne peux pas donner le code testé, mais la méthode de base serait d'utiliser récursion d'une certaine façon;
- Une fonction scalaire récursive peut faire
- MS SQL peut le faire en utilisant une récursif WITH (plus efficace)

Fonction Scalar (quelque chose comme):

CREATE FUNCTION getGraphWalk(@child_id INT)
RETURNS VARCHAR(4000)
AS
BEGIN

  DECLARE @graph VARCHAR(4000)

  -- This step assumes each child only has one parent
  SELECT
    @graph = dbo.getGraphWalk(parent_id)
  FROM
    mapping_table
  WHERE
    category_id = @child_id
    AND parent_id IS NOT NULL

  IF (@graph  IS NULL)
    SET @graph = CAST(@child_id AS VARCHAR(16))
  ELSE
    SET @graph = @graph + ',' + CAST(@child_id AS VARCHAR(16))

  RETURN @graph

END


SELECT
  category_id                         AS [category_id],
  dbo.getGraphWalk(category_id)       AS [graph_path]
FROM
  mapping_table
ORDER BY
  category_id

Je ne l'ai pas utilisé une récursif dans un certain temps, mais je vais donner la syntaxe d'un aller même si je n'ai pas SQL ici pour tester quoi que ce soit:)

récursif

WITH
  result (
    category_id,
    graph_path
  )
AS
(
  SELECT
    category_id,
    CAST(category_id AS VARCHAR(4000))
  FROM
    mapping_table
  WHERE
    parent_id IS NULL

  UNION ALL

  SELECT
    mapping_table.category_id,
    CAST(result.graph_path + ',' + CAST(mapping_table.category_id AS VARCHAR(16)) AS VARCHAR(4000))
  FROM
    result
  INNER JOIN
    mapping_table
      ON result.category_id = mapping_table.parent_id
)

SELECT
  *
FROM
  result
ORDER BY
  category_id


EDIT - SORTIE pour les deux est le même:

1   '1'
2   '1,2'
3   '1,2,3'
4   '1,2,4'
5   '1,2,5'
6   '1,6'
7   '1,6,7'
8   '1,6,7,8'
9   '1,6,9'

Traversant un arbre de profondeur arbitraire implique généralement le code de procédure récursive, à moins que vous utilisez des fonctions spéciales de certains SGBD.

Dans Oracle, la clause CONNECT BY vous permettra de traverser l'arbre en profondeur premier ordre si vous utilisez la liste de contiguïté, comme vous l'avez fait ici.

Si vous utilisez des ensembles imbriqués, le numéro de séquence gauche vous fournira l'ordre de visiter les nœuds.

peut-être fait avec SQL dynamique dans une procédure de magasins. Vous devenez alors limité à ce qui peut être fait sith la procédure stockée. De toute évidence, il devient un défi à exec les résultats dans une table temporaire ne sachant pas combien de colonnes à attendre. Toutefois, si l'objectif est de sortie vers une page Web ou une autre interface utilisateur, alors il est peut-être vaut la peine ...

scroll top