Avatar billede andreas13_fam Nybegynder
01. april 2010 - 12:41 Der er 22 kommentarer og
2 løsninger

generel database normalisering

Database normalisering har aldig været min lykke, dette forsøger jeg dog her i påsken at lave om på. I den forbindelse har jeg et spørgsmål:

Jeg har 2 tabeller i samme database som liner disse:


Opskrift:
+----+------------+---------------+
| id |  Navn      |      land    |
+----+------------+---------------+
| 1  | suppe ris  | Danmark      |
+----+------------+---------------+
| 2  | Frugtsalat | England      |
+----+------------+---------------+
| 3  | Brød      | Frankrig      |
+----+------------+---------------+



Opskrift_kategori:
+----+---------------+----------------+
| id | opskrift_id  | kategori      |
+----+---------------+----------------+
| 1  | 1            | Forret        |
+----+---------------+----------------+
| 2  | 1            | Risret        |
+----+---------------+----------------+
| 3  | 1            | Suppe          |
+----+---------------+----------------+
| 4  | 2            | Hovderet      |
+----+---------------+----------------+
| 5  | 2            | Salat          |
+----+---------------+----------------+
| 6  | 3            | Hovderet      |
+----+---------------+----------------+


Jeg ønsker så at få de "opskrifter" som kommer fra Danmark (land) og som har kategorien "Forret og Suppe".

resultatet skulle så meget gerne blive:

Resultat #1
+----+------------+---------------+-----------------------+
| id |  Navn      |      land    |    kategori          |
+----+------------+---------------+-----------------------+
| 1  |  Suppe ris | Danmark      | Forret, Risret, Suppe |
+----+------------+---------------+-----------------------+


Jeg kan tænke mig til at jeg skal bruge JOIN, men så vil jeg mene at jeg for dette resultat:


Resultat #2
+----+------------+---------------+---------------+
| id |  Navn      |      land    |    kategori  |
+----+------------+---------------+---------------+
| 1  |  Suppe ris | Danmark      | Forret        |
+----+------------+---------------+---------------+
| 1  |  Suppe ris | Danmark      | Risret        |
+----+------------+---------------+---------------+
| 1  |  Suppe ris | Danmark      | Suppe        |
+----+------------+---------------+---------------+


Hvilket jeg ikke syntes er særlig optimalt at arbejde med, så hvordan for jeg "Resultat #1" eller er jeg helt galt på den.
Avatar billede janus_007 Nybegynder
01. april 2010 - 12:58 #1
Det er mere en denormalisering du laver der! Jeg går udfra at du blot gerne vil trække data ud fra databasen og så præsentere som Resultat #1

Men hvilken database bruger du?, jeg ved at en SqlServer kan det du gerne vil ( http://www.janusknudsen.dk/post/T-SQL-Pivot-String-Concatenation-without-XML.aspx )
Avatar billede janus_007 Nybegynder
01. april 2010 - 12:59 #2
Jo.. og hvis du spørger om de tabeller d uvisr her er normaliseret korrekt, så er svaret Ja :)
Avatar billede intenz Novice
01. april 2010 - 13:06 #3
Du kal nok overveje at normalisere det yderligere, så du får (gider ikke lige at sætte det så pænt op som dig :P):

Opskrift navn:
-----
opskrift_navn_id
opskrift_navn

Opskrift kategori:
-----
opskrift_kategori_id
opskrift_kategori_navn

Opskrift land:
-----
opskrift_land_id
opskrift_land_navn

Og så samle selve opskrifter sådan:

Opskrifter:
-----
opskrift_id
opskrift_kategory_id
opskrift_land_id


Så slipper for at have navnet på land og kategori flere gange.
Avatar billede intenz Novice
01. april 2010 - 13:17 #4
Hov, så lige resten af dit spørgsmål :)

"Hvilket jeg ikke syntes er særlig optimalt at arbejde med, så hvordan for jeg "Resultat #1"".

Jeg kan ikke se hvordan du kan få mysql til at give dig resultat 1. De forskellige opskrifter ligger netop i forskellige rækker, med forskelligt indhold, så det er vel logisk at du får alle rækkerne ud (på trods af at de har nogle ens rækker).

Jeg ville håndtere det i din efterfølgende kode, evt. lave en funktion du lige kan sende resultatet til som så ordner det for dig og returnerer et array i det format du vil have.
Avatar billede andreas13_fam Nybegynder
01. april 2010 - 13:30 #5
Jeg svare bare for en ende af :)

Jeg bruge MySQL, og mit mål er sådan set at have en normaliseret database. Som gør det muligt at søge efter flere kategorier uden at skulle ud i noget RegExp.

Resultaterne skulle så for brugeren gerne blive præsenteret som i Resultat #2 eller http://opskrift.netkogeren.dk/

Koden er ligger bag kan så have mange udformninger.

@intenz
Sådan som jeg forstår det skal jeg have 3 tabeller som indeholder en id og en beskrivelse.

Og så en database som samler dem alle ved at "linke" til en id i de andre tabeller.

Men jeg kan ikke helt se hvordan jeg med dit eksempel vil få mulighed for at have mere end en kategori til en opskrift.
01. april 2010 - 13:30 #6
Det resultat du er ude efter,

"Suppe ris | Danmark | Forret, Risret, Suppe"

er egenlig ikke det SQL og relational databases er konstrueret til at give.  Meningen er at du i SQL skal traekke data og deres relationer ud, og skal du have det formatteret paa specielle maader goer du det i din application, for eksempel en php side.

Hvor mange tabeller du i din normalisering skal lave afhaenger af naturen af dine data.  Inden jeg saa intenz's indlaeg med de fire tabeller havde jeg for test lavet tre tabeller, andreas_opskrift, andreas_kategori, og andreas_opskrift_kategori idet jeg gik ud fra at en karakteristik for en ret var dens land saa du for eksempel ikke har Supperis fra flere forskellige lande.  Hvis du vil kunne skelne mellem dansk supperis, fransk supperis, o.s.v. giver jeg intenz ret i de fire tabeller.

Her er tabellerne:

CREATE TABLE andreas_opskrift(id INT, navn VARCHAR(20), land VARCHAR(20));
CREATE TABLE andreas_kategori(id INT, kategory VARCHAR(20));
CREATE TABLE andreas_opskrift_kategori(opskrift_id INT, kategori_id INT);

Hvis du saa vil have opskrifter fra Danmark i to kategorier, forret og suppe, saa virker denne query:

SELECT navn, land, kategory
FROM andreas_opskrift o
JOIN andreas_opskrift_kategori ok ON o.id = ok.opskrift_id
JOIN andreas_kategori k ON ok.kategori_id = k.id
WHERE kategory = 'Forret' OR kategory = 'Suppe' AND land = 'Danmark'

som giver dette resultat:

navn  land  kategory 
suppe ris Danmark Forret
suppe ris Danmark Suppe

og at faa det paa en linie er saa efter min mening en opgave for den application du bruger til at kalde og behandle sql querien.
01. april 2010 - 13:31 #7
Jeg saa ikke intenz's #4 foer jeg fyrede min af, men vi synes stort set at vaere enige.
Avatar billede andreas13_fam Nybegynder
01. april 2010 - 14:09 #8
Tak det var specielt den sidste linje:
"og at faa det paa en linie er saa efter min mening en opgave for den applikation du bruger til at kalde og behandle sql querien."
Der var enlig svaret på mit spørgsmål.

Jeg går ud fra at jeg så ved hjælp af opskriftens id kan finde ud af i min applikation, om der er tale om den samme opskrift og så samle det til en række, og til sidst præsenter det for brugeren i en tabel.

I forbindelse med denne tråd er har spørgsmålet "Er normalisering en fordel" strejfet mig. Er det noget i kan kaste lys over?
Avatar billede intenz Novice
01. april 2010 - 14:47 #9
Normalisering er en fordel ved at du kun har en værdi ét sted i din database.

Hvis du f.eks. vil ændre en kategori eller et land, er det nemmere bare at skulle ændre det ét sted i stedet for xx steder i din tabel. Det gør det også nemmere at udvide system (afhængigt af opbygning.
Derudover er det nemmere at overskue systemet når det hele ikke er rodet sammen i en (eller flere) tabeller.

Du får også en fordel ved dine joins, idet det er hurtigere for mysql at søge på INT felter (keys) end på VARCHAR/CHAR felter. Så ved kun at skulle slå f.eks. Danmark op i én tabel, hente dens ID og så joine på det ID med opskrift tabeller vil det (alt andet lige) være hurtigere end at skulle scanne hele opskrift tabellen efter for "Danmark".

Du kan læse om det på wikipedia: http://en.wikipedia.org/wiki/Database_normalization
Eller søge på google.

Lige for at svare på denne her:
"Men jeg kan ikke helt se hvordan jeg med dit eksempel vil få mulighed for at have mere end en kategori til en opskrift"

Hvis du vil have flere kategorier, opretter du bare den samme opskrift igen, bare med et andet land_id.


Og til denne: "Jeg går ud fra at jeg så ved hjælp af opskriftens id kan finde ud af i min applikation, om der er tale om den samme opskrift og så samle det til en række, og til sidst præsenter det for brugeren i en tabel."
Er svaret ja. Hvert ID vil vise om opskriften, landet eller kategorien er den samme. Så kan din logik i applikationen samle det hele til det format du ønsker.
Avatar billede intenz Novice
01. april 2010 - 14:56 #10
Bare lige en ekstra info. Der er efterhånden begyndt at komme flere andre databaser der fungerer som objectorienterede databaser eller key/value databaser, i modsætning til relationelle databaser (som f.eks. MySQL).
De er en del af det der kendes som NoSQL databaser. Ved at bruge sådan en ville man kunne gemme dataet på den måde du gerne vil have, uden at skulle bruge joins eller lign.

Det er dog ikke noget du umiddelbart kan bruge, da de ikke understøttes af typiske webhoteller (som jeg kender til). Og det er heller ikke en teknologi der er udbredt nok til at man nemt kan få hjælp til at bruge dem, så det er ikke noget man lige gør.

Men om nogle år begynder det måske så småt at være en reel løsning til det bruger du stiller op :)
01. april 2010 - 14:59 #11
1.  Det synes at du har faaet kastet lys over dit oprindelige spoergsmaal og at mit indlaeg har vaeret til nytte.  Jeg opretter derfor et svar for pointgivning.  (Hvis du mener at andre skal have points ogsaa vil du nok opfordre dem til at lave svar.)

2.  Saa opstaar der nye spoergsmaal saasom hvordan du i din applikation for udformet resultatet af sql queryen efter oenske.  Det vil nok vaere mest hensigtsmaessigt at behandle det i et nyt spoergsmaal (hvis noedvendigt) i stedet for en udvidelse af dette.  Saa bliver det set af flere medlemmer hvoraf nogle maaske har bedre ideer end mig.  (I et saadan nyt spoergsmaal vil du naturligvis forklare hvilken applikation du bruger og hvilken SQL version o.s.v.)

3.  Og saa til slut spoergsmaalet om normalisering er en fordel.  Det kan jeg give et entydigt svar paa:  "Det afhaenger af omstaendighederne!"  Hvis du for eksempel skal have lavet en liste over medlemmerne i din soen's fodboldklub saa du eller han kan ringe/emaile/besoege og det ikke er sandsynligt at det skal bruges til mere saa er det nok spild af tid at proeve at normalisere noget.  Saa saet det op paa et simpelt Excel regneark.  Men hvis det kunne taenkes at du kommer i bestyrelsen i klubben og ogsaa skal kende spillernes foraeldre, holde styr paa om der er betalt kontingent, holde regnskab med spillernes resultater, indberette til forbundet, o.s.v., saa er normalisering uundvaerlig.  Hvis hver "slags" data sidder i sin egen tabel, tabellerne relaterer til hinanden med fremmednoegler, tabellerne og relationerne er udformet ud fra logikken og strukturen i dataerne, ikke ud fra de resultater du oensker at se, altsaa hvis dataerne er normaliserede, saa vil du ud fra tabellerne i fremtiden kunne traekke svar ud paa spoergsmaal du ikke havde taenkt paa da du konstruerede databasen og du vil nemt kunne udbrede databasen.
Avatar billede andreas13_fam Nybegynder
01. april 2010 - 15:44 #12
Christian_Belgien du har helt sikkert kastet lys over mit spørgsmål, det samme har intenz. janus_007 har ikke været helt dybdegående nok.

Hvis eksperten ellers tillader det bliver pointfordelingen
Christian_Belgien : 50
intenz : 50
janus_007 : 15 (let)
Avatar billede intenz Novice
01. april 2010 - 15:50 #13
Jeg lægger et svar
Avatar billede janus_007 Nybegynder
01. april 2010 - 15:50 #14
Jeg har ikke noget at tilføje; intenz og CB svar er spitzeklasse, specielt CB's pkt. 3 er nemlig uhyre vigtigt at have i tankerne; it depends :)

Læs evt. lidt om Boyce Codd, opfinderen af relationelle db'er.
The key, the whole key and nothing but the key, So help me Codd *GG*


Jeg har dog lige en til intenz -> "Du får også en fordel ved dine joins, idet det er hurtigere for mysql at søge på INT felter (keys) end på VARCHAR/CHAR felter."
- Igen så afhænger det af størrelsen på dine varchars. En int fylder 4 byte, en varchar(2) 2 byte. Dvs. eks.vis landekoder hvis datatypen er int, måske burde laves som varchar(2), valuta ligeså osv. ellers skal man ned til tinyint (1byte)
Eller smallint som fylder 2 byte, her vil man måske styre et flag, 1 for update, 0 for insert, 2 for delete; ved at bruge char(1), U eller I eller D vil man spare 1 byte. (+ performance penalty ved joinet)

Igen så afhænger det af brugen, løsningen med U, I, D, landekoder mv. vil være nemmere at læse efter alle har glemt betydningen af 0, 1 og 2 osv. eller skal til at lave et yderligere join.

Men igen Andreas... som budskabet er i denne tråd, nemlig "it depends!"
Avatar billede intenz Novice
01. april 2010 - 16:06 #15
-> #14
Ja, det er jeg enig i, i de specielle situationer :) Men jeg ville aldrig joine noget på en varchar, med mindre det var en _meget_ specifik situation. Men ja, det kan være anderledes afhængigt af omstændighederne :)

Udover det fylder varchar(2) mellem 1 og 3 bytes, da det er en varchar (indeholder en ekstra byte, da den kan have en variabel længde, derfor vil den indholde mellem 0-2 bytes + 1 ekstra).

En char(2) fylder derimod altid 2 bytes (da den er fast defineret og derfor altid vil have en længde på 2, uanset hvad man putter i den).

Flueknepperi, men alligevel ;)
Avatar billede janus_007 Nybegynder
01. april 2010 - 16:13 #16
Enig, men du virker teoretisk velfunderet og så er det jo altid lidt skægt at nørde og diskutere :)
Avatar billede andreas13_fam Nybegynder
01. april 2010 - 16:13 #17
Tak for jeres sidste input, jeg har vist en hel del database omlægning at se frem til.
Vil janus_007 ligge et svar.

God påske :)
01. april 2010 - 16:19 #18
Andreas, #12, pointsfordeling - det tillader Eksperten ikke i et hug.  Du oprettede spoergsmaalet med 50 points, og det er alt hvad du (i foerste omgang) kan fordele.

Hvis du alligevel vil af med flere points (jeg siger ikke nej) saa synes udvejen at vaere at oprette et nyt spoergsmaal "Points til xxxx."  Om det skriver FAQ dette:

"Hvordan giver jeg en anden bruger flere point?

Først skal vi have på plads at du ikke må udlove mere end 200 point for et spørgsmål, heller ikke fordelt over flere spørgsmål.

Metoden er forholdsvis simpel. Opret et nyt spørgsmål i samme kategori som det oprindelige spørgsmål og kald det "Point til [brugernavn]" hvor du erstatter [brugernavn] med navnet på den bruger du ønsker skal have point. Husk at skrive et link til spørgsmålet hvor du normalt skriver en længere beskrivelse af dit problem."
Avatar billede Slettet bruger
01. april 2010 - 16:22 #19
Syntes ikke jeg så nogle nævne det, men du kunne bruge MySQL's funktion Group_Concat() til at samle data fra en enkelt kolonne i én enkelt række.
Har dog ikke sat mig helt ind i hvad det er du prøver på at opnå og hvilke kommentarer de andre er kommet med.
Avatar billede intenz Novice
01. april 2010 - 16:29 #20
-> #19
Lækker funktion, den kendte jeg ikke. Men ja, en lille google søgning viser at det faktisk kunne løse problemet helt fint.

Jeg fandt selv denne, hvis nogle er interesserede:
http://www.mysqlperformanceblog.com/2006/09/04/group_concat-useful-group-by-extension/


Ang. point, så behøver du ikke oprette tomme spørgsmål for min skyld. Jeg er ligeglad med hvor mange point jeg får.
Avatar billede andreas13_fam Nybegynder
01. april 2010 - 18:35 #21
Point kan hentes her http://www.eksperten.dk/spm/906023.
intenz har fået 50 point i denne tråd.
Avatar billede andreas13_fam Nybegynder
01. april 2010 - 18:44 #22
@ roxki: Det er jo det jeg søgte, jeg er supper glad for svaret. Men spørgsmålet handlede også om Database normalisering, så du for kun 25 point.
Avatar billede Slettet bruger
01. april 2010 - 19:32 #23
Jamen glad for jeg kunne hjælpe en smule :-)
Avatar billede janus_007 Nybegynder
01. april 2010 - 21:43 #24
Jeg skal ikke have point :)

Interessant funktion den Group_Concat()
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Vi tilbyder markedets bedste kurser inden for webudvikling

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester



IT-JOB


White paper
Tidsbegrænset kampagne: Overvejer du at udskifte eller tilføje printere i din forretning? Vi kan tilbyde én eller flere maskiner gratis