2009-07-05 13 views
1

Ich habe dieses Problem ein paar Mal jetzt, und ich würde gerne einen besseren Weg finden, um das Problem zu lösen.SQL Get ersten nicht Null-Wert von Eltern Zeilen

Grundsätzlich habe ich eine Kategorie, die Unterkategorien hat. Wenn die Unterkategorie keine Beschreibung hat, möchte ich die Übergeordnete Kategorie Beschreibung abrufen.

Es wird schwierig, wenn ich Sub-Unterkategorien habe.

Die SQL-Tabelle sieht wie folgt aus:

CategoryID   int, 
ParentCategoryID int null, 
Name    varchar(255), 
Description  varchar(MAX) null 

Ich möchte eine Funktion erstellen, die die Beschreibung aussehen würde, aber ich habe keine Ahnung, ob das ist die beste Lösung, oder wie die Funktion zu erstellen.

Ich bin hauptsächlich auf der Suche nach dem besten/richtigen Weg, um dieses Problem zu lösen.

+0

Welche Marke von SQL verwenden Sie? – Talljoe

+1

Sieht wie vielleicht SQLServer von der varchar (MAX) -Syntax aus? – Plasmer

+0

MsSql, tut mir leid, das war nur Pseudocode für das, wie die Tabelle aussieht. Glaubt jemand, dass eine Funktion verwendet werden könnte, um dies zu erreichen? Ich mag das "mit" Zeug. Hat MySql 'mit'? –

Antwort

0

Sie benötigen natürlich zwei Tabellenreferenzen und einen Join. Dies würde Ihnen Zugriff auf beide Beschreibungen geben. Dann benötigen Sie eine inline if-Anweisung, um den zu verwendenden Wert zu wählen, basierend darauf, dass der erste Wert null ist.

So etwas wie dies funktionieren sollte (Tabelle namens Tabelle1)

SELECT Table1.CategoryID, IIf(Parent.Description Is Null, Table1.description, parent.description) AS [Desc] 
FROM Table1 INNER JOIN Table1 AS Parent ON Table1.CategoryID = Parent.ParentCategoryID; 
+0

Funktioniert das nicht nur, wenn die Hierarchie nur zwei Ebenen hat? Sie könnten auch COALESCE() anstelle von IF() verwenden. –

+0

Ja, es ist eine zweistufige Lösung. So habe ich die Frage gelesen. Es sollte eine darüber liegende Ebene aufnehmen, aber ich habe das nicht getestet. –

+0

Woher weißt du, dass es eine Ebene darüber abheben sollte? –

1

Angesichts Ihrer Struktur, Lösung Alex Martelli ist wahrscheinlich das Beste, was Sie finden. Eine andere Möglichkeit wäre, wenn Sie das Modell ändern könnten, von einer Baumstruktur mit verknüpften Listen zum Modell mit verschachtelten Mengen zu wechseln.

Joe Celko hat a book on trees and hierarchies, die in die verschiedenen Möglichkeiten geht, die Sie sie modellieren können und die Vorteile/Nachteile jeder Methode. Sie können wahrscheinlich auch viele Informationen zu diesem Thema über Google oder Google Groups finden.

Einer der Nachteile des Nested-Set-Modells ist, dass Änderungen an der Baumstruktur ein wenig teurer sind, aber für Produkte, die Sie normalerweise abrufen, viel mehr als Sie aktualisieren. Besonders Dinge bewegen zwischen Kategorien, die in den meisten Geschäftsfällen selten sind.

die Nested Sets verwenden, würde die folgenden Ihnen, was Sie wollen:

SELECT 
    P1.Name, 
    COALESCE(P1.Description, P2.Description) AS Description 
FROM 
    Products P1 
LEFT OUTER JOIN Products P2 ON 
    P2.lft < P1.lft AND 
    P2.rgt > P1.rgt AND 
    P2.Description IS NOT NULL 
LEFT OUTER JOIN Products P3 ON 
    P3.lft < P1.lft AND P3.lft > P2.lft AND 
    P3.rgt > P1.rgt AND P3.rgt < P2.rgt AND 
    P3.Description IS NOT NULL 
WHERE 
    P3.ID IS NULL 
3

Annahmen:

  1. CategoryId ist ein Primärschlüssel.

  2. Wenn die Beschreibung auf Zeilenebene null ist, sehen Sie sich die übergeordnete Zeile an. Wenn die übergeordnete Zeile null Beschreibung hat, sehen Sie sich das übergeordnete Element an. Mit anderen Worten: Verwenden Sie die erste Nicht-Null-Beschreibung von Vorfahren.

  3. Wenn Zeilenebene Beschreibung null ist, und kein Vorfahr existiert mit nicht null Beschreibung, ist allgemeine Beschreibung null

einrichten Beispieltabelle und Testdaten.

create table #SO (CategoryID int primary key 
    , ParentCategoryID int Null 
    , Name varchar(255) not null 
    , Description varchar(MAX) Null 
    ) 

insert into #SO (CategoryID, ParentCategoryID, Name, Description) 
values (1, null, 'Top 1', 'Top 1 Description') 
    , (2, null, 'Top 2', 'Top 2 Description') 
    , (3, null, 'Top 3', null) 
    , (11, 1, 'Child 11', 'Child 11 Description') 
    , (12, 1, 'Child 12', null) 
    , (21, 2, 'Child 21', null) 
    , (211, 21, 'Child 211', null) 
    , (2111, 211, 'Child 2111', null) 
    , (2112, 211, 'Child 2112', 'Child 2112 Description') 
    , (31, 3, 'Child 31', 'Child 31 Description') 
    , (32, 3, 'Child 32', null) 

Verwendung eines rekursiven CTE. Beachten Sie, dass der Baum nach oben geht.Wir beginnen mit allen Zeilen und sehen dann nach Bedarf die Eltern an, anstatt die normale Baummanipulation zu machen, indem wir ganz oben im Baum beginnen und arbeiten.

; with Description (BaseCategoryId 
     , CurrentParentCategoryId 
    , CurrentDescription 
    , CurrentLevel) 
    as 
(-- Anchor -- Start with all rows in the table. 
select CategoryId as BaseCategoryId 
    , ParentCategoryId as CurrentParentCategoryId 
    , Description as CurrentDescription 
    , 0 as CurrentLevel 
from #SO -- Recursive -- We are walking up the tree from all nodes, 
    -- We only continue up the tree when we do not have a description yet. 
union all 
select D.BaseCategoryId 
    , so.ParentCategoryId 
    , so.Description 
    , D.CurrentLevel + 1 
from #SO so 
inner join Description D 
    on D.CurrentParentCategoryId = so.CategoryId 
    and D.CurrentDescription is null) 
select DL.BaseCategoryId as CategoryId 
    , DL.CurrentDescription as UltimateDescription 
-- Now self outer join with the CTE every step of the walk 
-- for each BaseCategoryId, and then filter all but the top 
-- level. (Level is measured as distance from base.) 
from Description as DL 
left outer join Description as DR 
on DL.BaseCategoryId = DR.BaseCategoryId 
and DL.CurrentLevel < DR.CurrentLevel 
where DR.BaseCategoryId is null 
order by DL.BaseCategoryId 

Die Ausgabe ist eine Zuordnung von CategoryId zur endgültigen Beschreibung.

In Bezug auf die Wiederverwendung würde ich das obige eine Sicht machen.

+0

Das funktioniert gut, bis Sie mehr als 100 Stufen der Rekursion haben. An diesem Punkt stoppt SQL Server den CTE. Aber in den meisten Fällen beschäftigen sich die Menschen nicht mit so vielen Ebenen oder Daten, die spärlich sind. Leider bin ich einer der wenigen Glücklichen. :) –

1

Wenn Sie den übergeordneten Baum rekursiv durchlaufen müssen, siehe answer from Alex. Wenn Sie nur eine Ebene benötigen, dann sollte einfach LEFT JOIN funktionieren:

SELECT  c.CategoryID, 
      c.ParentCategoryID, 
      c.Name, 
      COALESCE(c.Description, p.Description) AS Description 
FROM  dbo.Category c 
LEFT JOIN dbo.Category p 
     ON c.ParentCategoryID = p.CategoryID