Datenbankkonzept: Richtige oder falsche Normalisierung?

2nd

Aktives Mitglied
Thread Starter
Dabei seit
25.07.2004
Beiträge
9.018
Reaktionspunkte
243
Moin,

ich habe 3 Tabellen mit Inhalten:

1. Artikel mit: ArtikelID, Name, Preis
2. Gruppen mit: GruppenID, Gruppenname
3. Untergruppen mir: UntergruppenID, Untergruppenname

Diese Tabellen setze ich per ID in Relation zueinander mit jeweils einer Verknüpfungstabelle:

1. Artikel <-> Gruppen = Artikel_Gruppen mit: ArtikelID, GruppenID

2. Artikel <-> Untergruppen = Artikel_Untergruppen mit: ArtikelID, UntergruppenID


Also insgesamt 5 Tabellen.

Richtiges Konzept?

Danke für Hilfe,

2nd
 
Wäre es nicht logischer UntergruppenID <-> GruppenID und ArtikelID <-> UntergruppenID?
 
Da die Untergruppen bestimmten Gruppen zugeteilt sind: jo.

Aber wie realisiere ich das denn bzgl. der Tabellen? So hier?

Artikel_Gruppen_Untergruppen mit: ArtikelID, GruppenID, UntergruppenID

Also 3 Fremdschlüssel in einer Tabelle und damit nur 4 Tabellen?

Hmm, glaube schon oder :kopfkratz: & :kopfrauch:

2nd
 
Mir ist nicht so ganz klar, warum du Gruppen und Untergruppen aufteilst auf zwei Tabellen.

Ich würde hier mit einer Tabelle für die Artikel, einer für die Gruppen und einer Kreuztabelle arbeiten. Die Informationen, welche Gruppe Vater bzw. Kind von welcher anderen Gruppe ist, kannst du ja direkt in der Gruppentabelle ablegen.

Matt
 
jup, ich würde hierbei wie folgt vorgehen:

Gruppe: Gruppen_id, name, parent_gruppen_id
artikel: artikel_id, etc.

wenn ein artikel in mehreren gruppen sein soll, dann natürlich noch eine 3. tabelle mit den beiden fremdschlüsseln - ansonsten direkt in die tabelle artikel packen.
 
matt schrieb:
Ich würde hier mit einer Tabelle für die Artikel, einer für die Gruppen und einer Kreuztabelle arbeiten. Die Informationen, welche Gruppe Vater bzw. Kind von welcher anderen Gruppe ist, kannst du ja direkt in der Gruppentabelle ablegen.

Also in der Gruppentabelle 4 Attribute erzeugen?

id, gruppenname, untergruppenname , id_to_parent

Und wenn id_to_parent leer ist, ist es automatisch eine Elterngruppe? Ich bau auch die Menus aus den Datensätzen, deswegen brauche ich die Differenzierung.

2nd
 
Pinacolada schrieb:
jup, ich würde hierbei wie folgt vorgehen:

Gruppe: Gruppen_id, name, parent_gruppen_id
artikel: artikel_id, etc.

wenn ein artikel in mehreren gruppen sein soll, dann natürlich noch eine 3. tabelle mit den beiden fremdschlüsseln - ansonsten direkt in die tabelle artikel packen.

Ok, stimmt, ich muss gar nicht zwischen Untergruppe und Gruppe differenzieren, das macht der parent_gruppen_id.

Und meien Bezug zum Artikel bau ich in die Artikeltabelle ein, also 2 zusätzliche Attribute: parent_gruppen_id und child_gruppen_id als Fremdschlüssel auf die Gruppentabelle?

EDIT: Ich weiss das noch nicht genau, wo die Artikel überall stehen sollen, also werde ich parent_gruppen_id und child_gruppen_id mit einer artikel_id in eine 3. Tabelle tun, richtig? Also 3 Fremdschlüssel in einer Tabelle (artikel, gruppe, untergruppe)?

2nd
 
2ndreality schrieb:
Ok, stimmt, ich muss gar nicht zwischen Untergruppe und Gruppe differenzieren, das macht der parent_gruppen_id.

Und meien Bezug zum Artikel bau ich in die Artikeltabelle ein, also 2 zusätzliche Attribute: parent_gruppen_id und child_gruppen_id als Fremdschlüssel auf die Gruppentabelle?

2nd


nein, du speicherst einfach die gruppe - in der sich das produkt befindet. Wozu sollte denn auch die parent_gruppen_id mitgespeichert werden? - solche daten können nämlich bei späteren änderungen (untergruppe wird verschoben) extreme probleme verursachen
 
Was willst du mit dem Gruppennamen und dem Untergruppennamen?

Ich hab früher auch viel rumexperimentiert mit Tabellen für Kategorien etc. Aber mit mehreren Tabellen nimmst du die die Flexibilität, wenn du mal eine Unterkategorie unterhalb der Unterkategorie brauchst.
Deswegen arbeite ich mittlerweile nur noch mit einer Tabelle, die als Nested Set aufgebaut ist. Das ist IMHO die einfachste uns sauberste Lösung.

Matt
 
Pinacolada schrieb:
nein, du speicherst einfach die gruppe - in der sich das produkt befindet. Wozu sollte denn auch die parent_gruppen_id mitgespeichert werden? - solche daten können nämlich bei späteren änderungen (untergruppe wird verschoben) extreme probleme verursachen

Moment, ich bin ein bisschen langsamer als Ihr :)

D. h. ich speichere in der Artikeltabelle den Namen als String (Varchar) von der Gruppe und Untergruppe in 2 Extraspalten? Und wenn man mal eine Gruppe umbenennt muss man alle Gruppennamen in der Artikeltabelle umbenennen :kopfkratz:

@matt: Jo, habs schon verstanden...


2nd
 
Zuletzt bearbeitet:
2ndreality schrieb:
Moment, ich bin ein bisschen langsamer als Ihr :)

D. h. ich speichere in der Artikeltabelle den Namen als String (Varchar) von der Gruppe und Untergruppe in 2 Extraspalten? Und wenn man mal eine Kategorie umbenennt muss man alle Artikel in der Kategorie umbenennen :kopfkratz:

@matt: Jo, habs schon verstanden...

argh :)

nein natürlich nicht.

artikel: artikel_id, artikel_name,
gruppe: gruppen_id, parent_gruppen_id, gruppen_name
artikel_to_gruppe: artikel_to_gruppe_id (unwichtig, aber da 3. normalform - notwendig), artikel_id, gruppen_id


wenn ein produkt in mehrere gruppen soll - dann einfach bei artikel_to_gruppe mehrere einträge machen.
 
Zuletzt bearbeitet:
Jo, das ist gut - danke erstmal :)

Noch eine Frage: Ich speichere meine Ids von den Untergruppen der Verknüpfungstabelle und kann dann von der jeweiligen Untergruppe die Artikel anzeigen.

Wenn ich von der Elterngruppe alle Artikel anzeigen lassen will, anstatt nur die Untergruppen, dann muss ich 2 Querys machen oder?

Erstmal alle Ids aller Untergruppen der einen Elterngruppe abfragen (aus der Gruppentabelle) und dann die artikel_to_gruppe Tabelle damit abfragen? Oder kann man auch die ElterngruppenId mit in der Verknüpfungstabelle abspeichern? Erscheint mir auf den ersten Blick praktikabler :)

2nd
 
kannst du auch recht einfach mit left joins lösen... - ist aber wahrscheinlich unperformanter (müsste man ausprobieren)

- natürlich könntest du das elternelement mitspeichern - doch 2 dinge sprechen dagegen:
3. normalform und wenn sich eine gruppe verschiebt, darfste beim updaten nichts vergessen

dafür spricht die performance
 
ich brüte gerade über den LEFT JOIN Selects aber ich kriege nichts brauchbares raus :(

Mein Query wäre ja wortgemäß: Gib mir alle Artikel aus der Tabelle artikel_to_gruppe die zu einer Elterngruppe gehören (also alle Artikel, die sich in einer Untergruppe mit vorhandener gleicher parent_gruppen_id in der Gruppentabelle befinden).

Wie übersetze ich das mittels LEFT JOIN?

2nd
 
Wie sieht denn deine Tabellenstruktur aus?

Matt
 
as_pArticle
• as_pArticle.article_id (Primary Key)
• as_pArticle.articleName
...
...


as_pGroup
• as_pGroup.group_id (Primary Key)
• as_pGroup.groupname
• as_pGroup.parent_group_id (Fremdschlüssel auf die Eltern innerhalb dieser Tabelle)


as_pArticle_to_pGroup
• as_pArticle_to_pGroup_id (Primary Key)
• as_pArticle_to_pGroup.article_id (Fremdschlüssel Artikel)
• as_pArticle_to_pGroup.group_id (Fremdschlüssel Gruppe)


Ich habe es mit der Abfrage hinbekommen, aber mich dünkt, dass es noch eleganter geht. Vor allem wegen meinen verwendeten Bedingungen (1015 -> 1035). Das ist bestimmt schon wieder Schrott, obwohl es funktioniert:

SELECT as_pArticle.article_id FROM as_pArticle INNER JOIN as_pArticle_to_pGroup ON (as_pArticle.article_id = as_pArticle_to_pGroup.article_id AND as_pArticle_to_pGroup.group_id >= 1015 AND as_pArticle_to_pGroup.group_id <= 1035)

Einmal durchbeissen, für immer kapiert haben... grmpffff

2nd
 
Wo hast du denn die Information mit 1015 und 1035 her?

Ich kann mir drei unterschiedliche Szenarien vorstellen. Alle Beispiele gelten nur bei zwei Ebenen, also Hauptebene und Unterebene. Wenn das verschachtelter werden soll, würde ich ernsthaft über den Einsatz von nested sets nachdenken:

Du weißt die ID des Elternelements und willst alle Produkte haben, die Kindkategorien dieser Kategorie zugeordnet sind. Das ist der einfachste Fall:
Code:
select a.id as id_article, a.name, c.name as category from xtbl_article_category ac, as_article a, as_category c where c.id_root = 1 && c.id = ac.id_category && ac.id_article = a.id

Du weißt die ID der Unterkategorie und willst alle Produkte haben, die ebenfalls Kindkategorien des Elternelements dieser Kategorie zugeordnet sind:
Code:
select a.id as id_article, a.name, c.name as category from as_category cr, xtbl_article_category ac, as_article a, as_category c where cr.id = 2 && cr.id_root = c.id_root && c.id = ac.id_category && ac.id_article = a.id

Dritter Fall: Du weißt die ID eines Artikels und willst alle Artikel auslesen, die ebenfalls der selben Hauptkategorie zugeordnet sind:
Code:
select a.id as id_article, a.name, c.name as category from as_article r, xtbl_article_category acr, as_category cr, as_article a, as_category c, xtbl_article_category ac where r.id = 5 && r.id = acr.id_article && acr.id_category = cr.id && cr.id_root = c.id_root && c.id = ac.id_category && ac.id_article = a.id

Testaufbau:
Code:
-- 
-- Tabellenstruktur für Tabelle `as_article`
-- 

CREATE TABLE `as_article` (
  `id` int(8) unsigned zerofill NOT NULL auto_increment,
  `name` varchar(100) NOT NULL default '',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=13 ;

-- 
-- Daten für Tabelle `as_article`
-- 

INSERT INTO `as_article` VALUES (00000001, 'Artikel 1');
INSERT INTO `as_article` VALUES (00000002, 'Artikel 2');
INSERT INTO `as_article` VALUES (00000003, 'Artikel 3');
INSERT INTO `as_article` VALUES (00000004, 'Artikel 4');
INSERT INTO `as_article` VALUES (00000005, 'Artikel 5');
INSERT INTO `as_article` VALUES (00000006, 'Artikel 6');
INSERT INTO `as_article` VALUES (00000007, 'Artikel 7');
INSERT INTO `as_article` VALUES (00000008, 'Artikel 8');
INSERT INTO `as_article` VALUES (00000009, 'Artikel 9');
INSERT INTO `as_article` VALUES (00000010, 'Artikel 10');
INSERT INTO `as_article` VALUES (00000011, 'Artikel 11');
INSERT INTO `as_article` VALUES (00000012, 'Artikel 12');

-- --------------------------------------------------------

-- 
-- Tabellenstruktur für Tabelle `as_category`
-- 

CREATE TABLE `as_category` (
  `id` int(8) unsigned zerofill NOT NULL auto_increment,
  `id_root` int(8) unsigned zerofill NOT NULL default '00000000',
  `name` varchar(100) NOT NULL default '',
  PRIMARY KEY  (`id`),
  KEY `id_root` (`id_root`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;

-- 
-- Daten für Tabelle `as_category`
-- 

INSERT INTO `as_category` VALUES (00000001, 00000000, 'Hauptkategorie 1');
INSERT INTO `as_category` VALUES (00000002, 00000001, 'Unterkategorie 1');
INSERT INTO `as_category` VALUES (00000003, 00000001, 'Unterkategorie 2');
INSERT INTO `as_category` VALUES (00000004, 00000001, 'Unterkategorie 3');
INSERT INTO `as_category` VALUES (00000005, 00000000, 'Hauptkategorie 2');
INSERT INTO `as_category` VALUES (00000006, 00000005, 'Unterkategorie 4');
INSERT INTO `as_category` VALUES (00000007, 00000005, 'Unterkategorie 5');

-- --------------------------------------------------------

-- 
-- Tabellenstruktur für Tabelle `xtbl_article_category`
-- 

CREATE TABLE `xtbl_article_category` (
  `id_article` int(8) unsigned zerofill NOT NULL default '00000000',
  `id_category` int(8) unsigned zerofill NOT NULL default '00000000',
  PRIMARY KEY  (`id_article`,`id_category`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

-- 
-- Daten für Tabelle `xtbl_article_category`
-- 

INSERT INTO `xtbl_article_category` VALUES (00000001, 00000002);
INSERT INTO `xtbl_article_category` VALUES (00000002, 00000002);
INSERT INTO `xtbl_article_category` VALUES (00000003, 00000002);
INSERT INTO `xtbl_article_category` VALUES (00000004, 00000002);
INSERT INTO `xtbl_article_category` VALUES (00000005, 00000003);
INSERT INTO `xtbl_article_category` VALUES (00000006, 00000004);
INSERT INTO `xtbl_article_category` VALUES (00000007, 00000004);
INSERT INTO `xtbl_article_category` VALUES (00000008, 00000006);
INSERT INTO `xtbl_article_category` VALUES (00000009, 00000007);
INSERT INTO `xtbl_article_category` VALUES (00000010, 00000006);
INSERT INTO `xtbl_article_category` VALUES (00000011, 00000007);
INSERT INTO `xtbl_article_category` VALUES (00000012, 00000007);

Den zusätzlichen Primärschlüssel in der Kreuztabelle sehe ich für unnötig an, da die Kombination Artikel-Kategorie ja schon eindeutig ist.

Matt
 
msslovi0 schrieb:
Den zusätzlichen Primärschlüssel in der Kreuztabelle sehe ich für unnötig an, da die Kombination Artikel-Kategorie ja schon eindeutig ist.

ist im prinzip auch unnütz, jedoch schreibt die 3. normalform dieses vor. Nach meinem empfinden sollte man sich zwingend dran halten (auch weil sonst datenbank tools immer alarmschlagen...)
 
@Matt: Oh man, da steckt schon ein wenig Mühe drinne, danke Mann!

Ich muss mir das morgen anschauen, vor allem die Querys, heute kapiere ich das nicht mehr, mein Kopf ist aufgebraucht.


2nd
 
Pinacolada schrieb:
artikel_to_gruppe: artikel_to_gruppe_id (unwichtig, aber da 3. normalform - notwendig), artikel_id, gruppen_id

??? warum für 3.NF (transitive abhaengigkeiten) notwendig?

-j
 
Zuletzt bearbeitet:
Zurück
Oben Unten