Jag behöver hjälp med hur man bäst konstruerar en databas. Problemet är att jag har idag två listor ”Personer” och ”Grupper”. En person kan tillhöra flera olika grupper som ”studenter”, ”lärare” etc. och varje grupp består av ett visst antal personer. Jag vill kunna lista alla personer, alla grupper, alla personer i varje grupp, alla grupper för varje person. Så här långt är det inga problem. Det finns några olika varianter. Relationsmässigt är följande bättre.. :) Med Olas lösning får du iochförsig inga null men istället svårt att kontrollera att samma utrustning inte ägs av både en grupp och en person. <b>>Samt det ger möjlighet att säga många fler saker om själva ägandet. När det startade, om det är aktivt nu, hur många är uppsatta som ägare. Man kan lätt bygga ut så att en typ av koppling är ägare, en annan typ av koppling är kanske lånare, eller behörig nyttjare.</b> Vad kul att jag fick så många olika lösningsförslag! Tack för det – mycket lärorikt! Först och främst undviker jag alltid "Typ"-kolumner. Det är lätt att göra anomalier. En kommentar på detta: Jag har lite svårt att hålla med om allt som sägs här. Jag tycker fortfarande Niklas förslag 2 är en attraktiv lösning. Att t ex personer skulle få personnummer och andra data behöver inte skapa några problem. Jag redovisade bara de nödvändiga tabellerna för att skapa logiken, men skall man spara person och grupp-relaterade data så bör man skapa ytterligare tabeller och inte försöka spara det i Entitet()-tabellen. Det skulle kunna se ut så här t ex:Hur slå samman två listor till en, men behålla relationerna?
Jag har nu en ”Utrustning”-lista. Jag vill kunna koppla ”utrustning” till antingen en person eller en grupp. Grupp eller person skall alltså vara likvärdiga och borde väl därför egentligen ligga i en och samma lista bägge två eller?
Hur ser en sådan relation/lista ut? Hur löser jag detta problem smidigast? Jag måste även kunna uppfylla kraven i första stycket.Sv: Hur slå samman två listor till en, men behålla relationerna?
1. En enkel är att låta utrustning ha referens både till grupper och till personer, och kräva att det alltid är så att exakt en av dem är icke-null.
2. Eller så bygger du om det du har gjort till en trädstruktur; varje person/grupp är en "entitet" eller något annat lika otydligt, och sen har du en entitet-entitet-tabell där man anger vem som är del av vad. Då behöver du bara en entitet-referens, men då får man vara jävligt försiktig så att man inte gör så att "Arne" kan vara del av "Bengt"...
3. Eller så gör du en extra tabell, "Ägare", som funkar precis som i 1., dvs:
Ägare(ID, Grupp, Person)
men som gör att utrustning då bara behöver referera till Ägar-id.
Ingen lösning är speciellt trevlig, men 3:an separerar ut "fulheten" till en egen tabell, och det gillar jag.Sv: Hur slå samman två listor till en, men behålla relationerna?
Det blir helt flexibelt och inga NULL-värden någonstans.
Samt det ger möjlighet att säga många fler saker om själva ägandet. När det startade, om det är aktivt nu, hur många är uppsatta som ägare. Man kan lätt bygga ut så att en typ av koppling är ägare, en annan typ av koppling är kanske lånare, eller behörig nyttjare.
Person
-----------
*ID
Namn
Grupp
-----------
*ID
Namn
Utrustning
-----------
*ID
Namn
GruppÄgerUtrustning
---------------------------------
*ID
GruppID
UtrustningID
Aktivt
StartDatum
PersonÄgerUtrustning
---------------------------------
*ID
PersonID
UtrustningID
Aktivt
StartDatumSv:Hur slå samman två listor till en, men behålla relationerna?
Om man skall lista var utrustning finns kan man med Niklas lösning göra det med join. Med Olas lösning behövs det en union vilket jag tycker oftast komplicerar frågor.Sv:Hur slå samman två listor till en, men behålla relationerna?
Ja, det funkar ju precis lika väl i min "ägare"-tabell, så det spelar ju ingen roll, men det jag ser som problemet med din variant är duplicering av struktur. Båda ägartabellerna ska ju se precis likadana ut, förutom själva id:t. Det gör i sin tur att du kan få jävligt otrevliga anomlier om något ändras.Sv: Hur slå samman två listor till en, men behålla relationerna?
Jag sammanfattar Olas förslag så som jag förstår det, och för att uppfylla mina krav. Tabellen PersonGrupp() är till för att uppfylla mina krav från första stycket:
Först så har jag mina tre tabeller:
Grupp(*ID, Namn)
Person(*ID, Namn)
Utrustning(*ID, Namn)
Sedan extra tabeller som kopplar ihop ovanstående:
PersonGrupp(Person_ID, Grupp_ID)
UtrustningPerson(Utrustning_ID, Person_ID)
UtrustningGrupp(Utrustning_ID, Grupp_ID)
Men om jag vill lista all utrustning med ägare då måste jag väl hela tiden söka i två tabeller; UtrustningGrupp och UtrustningPerson? Det borde väl i och för sig gå rätt enkelt i en två-pass-process.
-----------------
Niklas olika förslag var svårare. Jag redovisar hur jag tolkat dem. Förslag 2 går att lösa med bara tre tabeller totalt. Rent spontant gillar jag den lösningen – den är faktiskt rätt snygg och enkel! Den får heller inga null-värden som förslag 1 och 3 får.
Förslag 1
Grupp(*ID, Namn)
Person(*ID, Namn)
Utrustning(Namn, Grupp_ID, Person_ID)
PersonGrupp(Person_ID, Grupp_ID)
Förslag 2
Entitet(*ID, Namn, Typ) "Typ" talar om ifall det är en person eller grupp
EntitetEntitet(*ID, Entitet_ID_p, Entitet_ID_g) id, Person, Grupp. Kopplar samman person och grupp
Utrustning(Namn, Entitet_ID) här kan man koppla till person eller grupp
Förslag 3
Grupp(*ID, Namn)
Person(*ID, Namn)
PersonGrupp(Person_ID, Grupp_ID)
Utrustning( Namn, Ägare _ID)
Ägare(*ID, Grupp_ID, Person_ID)
Jag kan väl enkelt undvika att Arne blir en del av Bengt genom att alltid i EntitetEntitet(*ID, Entitet_ID_p, Entitet_ID_g) koppla första Entitet_ID till en person och andra till en grupp? Jag kan skilja på person och grupp genom Typ. Niklas kolla om du inte vill omvärdera din preferens för förslag tre, eller har jag missförstått något?
I förslag tre får man en dubblering av data genom tabell Ägare(). Varje person och grupp måste också finnas en gång i tabell Ägare(). Kanske ger detta en enklare och snabbare logik än förslag 2 när man skall söka i de olika tabellerna?Sv:Hur slå samman två listor till en, men behålla relationerna?
Jag skulle istället säga att en person är någon som inte är parent i entitet-tabellen, men det funkar bara om grupper alltid har medlemmar. (Eller rättare sagt, en grupp kommer betraktas som en person tills den har medlemmar.) Sen finns det andra problem; Om grupper och personer har olika information kopplade till sig får du ändå massa null-fält; det är en balansgång och beror på situationen.
Jag skulle inte kalla det en dubblering i förslag 3, men det är nästan en smaksak. Du behöver ju bara "faktiska ägare" i tabellen. Om man tänker sig att vi har femton fält kopplade till person och tio kopplade till grupp, så är det i mitt tycke ganska mycket renare med förslag 3 än med 2, och man kopplar bort "ägarlogiken".
Det är till exempel inget större problem att lägga till en ny typ av ägare som är frikopplad från de olika personerna och grupperna. Låt säga att vi vill ha "externa företag" i en extra tabell.
Med Olas förslag så innebär det att du måste göra en ny ägartabell (och får större risk att två äger samma sak), med mitt förslag 1 att du lägger till en kolumn i utrustningstabellen, med mitt förslag 2 att du måste hitta på någon modifiering av entitetstabellen, men med förslag 3 bara en extra kolumn i en väldigt enkel tabell.
Som sagt, sånt här är alltid en balansgång, och man måste ha större förståelse för verksamheten för att kunna avgöra vad som är bäst.Sv: Hur slå samman två listor till en, men behålla relationerna?
<code>
Entitet(*ID, Namn, Typ) "Typ" talar om ifall det är en person eller grupp
</code>
Jag tycker inte det är så bra av flera skäl.
Som jag ser det är Personer och Grupper helt olika saker. Genom att generalisera de båda typerna av information till "Entitet" - skapar man en onödigt abstrakt modell. Det blir svårt att sätta sig in i vad databasen har för uppgift. Och antagligen kommer en sådan modell att introducera NULL-kolumner förr eller senare. T.ex. Personer har PersonNr. Men det har inte Grupp. Därför skulle Entitetsposter av typen "Grupp" få NULL i PersonNr. Nu finns visserligen inte PersonNr i det förenklade exemplet, men det är rätt sannolikt att man vill ha attribut som är kopplade just till individer i en Person-tabell. T.ex. personnummer eller civilstatus.
Jag tror att ni tänker för mycket OOP..? :) En relationsdatabas bygger inte på OOAD (obj orienterad analys och design) så det blir ofta fel om man försöker applicera detta tankesätt på relationsdatabasen. I OOP hade man gärna generaliserat personer och grupper till CEntitetsBas som kanske har ett unikt ID, StartDatum, Fritext, samt möjligheten att sparas till DB gemensamt, men i relationsdatabasen kan vi ju inte bygga tabeller som ärver andra tabellers struktur, därför stämmer inte det här generaliserade tänket med hur relationsdatabaser faktiskt fungerar.
Som jag ser det är upplägget med Grupptabell och Persontabell mer logiskt och mer flexibelt.
Om man tänker sig att man ska programmera GUI mot detta kanske man vill ha en trädstruktur med grupper och personer. Jag skulle tycka att det var enklare, och snyggare, att då hantera "Mapparna" (dvs Grupper) och "Filerna" (dvs Personer) i olika tabeller eftersom de är väsentligt skilda genom vilka attribut de kan tänkas ha. Det blir också mycket tydligare SQL / programkod, eftersom man hämtar det som man faktiskt ska ha i GUI:t - rakt upp och ner, inte "det som har fått ett abstrakt namn av det som jag faktiskt vill ha".. (med den enda "fördelen" som jag kan se att man har lyckats rationalisera bort en typ av entitet)..
Skräckexemplet är ju en databas med EN enda monstertabell med 200 varchar kolumner som heter Stuff som kan ha en Typ-kolumn så att den "jättepraktiskt" kan hålla två miljarder olika slags entiteter.. (detta tänk förekommer i en del affärssystem som jag har tvingast programmera mot.. fast med fräckare namn än "stuff".. ingen nämnd, ingen glömd).
Likadant med kopplingstabellerna... även om de ser väldigt lika ut, så hanterar de helt olika saker.. Tänker er säjlva en lite större databas med t.ex. 50 olika entiteter som kan koppla "kors och tvärs".
Då skulle man kunna skapa den generaliserade "Kopplingsplinten", en tabell som håller alla typer av M:M relationer med två PK:s och skiljer dem åt med "TypAvRelation". Visst, det fungerar och man slipper kanske skapa 20 sådana tabeller. Men datamodellen blir ju helt obegriplig. Allt är kopplat till kopplingsplinten och kopplingsplinten är kopplad till allt. Jag vill inte se den ritningen :)
När man kommer lite längre med systemet upptäcker man ofta att man behövde skilja på relationerna mer än vad man först trodde. Det var inte bara ID till ID som skulle lagras.. T.ex. Äger, Hyr, Lånar, FårEjAnvända, är väsentligt olika relationer som behöver sina egna attribut som är kopplade till just den typen av relation. Du ville väl t.ex. inte gärna behandla data av typen "personer som får använda en sak" i samma tabell som "personer som just nu hyr en sak till en viss avgift"?Sv:Hur slå samman två listor till en, men behålla relationerna?
Förslag 2
Entitet(*ID, Namn, Typ) Typ talar om ifall det är en person eller grupp
EntitetEntitet(*ID, Entitet_ID, Entitet_ID) id, Person, Grupp
Utrustning(Entitet_ID, Namn) här kan man referera till person eller grupp
Person(Entitet_ID_p, PersonNR)
Grupp(Entitet_ID_g, Gruppledare_ID_p)
Man får göra en sådan tabell som Person() för varje ”Typ” man har. Därmed är det också enkelt att bygga ut databasen i framtiden om flera ”typer” tillkommer. Tabellen Utrustning() är i grunden en likadan tabell, och man kan hänga på tabellen Grupp() på samma sätt. Denna logik tycker jag verkar ganska enkel och blir inte mer komplicerad om fler tabeller tillkommer. Det blir kanske enklare logik om man byter namn på Entitet till Ägare?
Det slutliga resultatet blir givetvis ganska så komplicerat, det är rätt att grupper och personer är vitt skilda typer, t ex så har grupper även sådant som gruppledare, sekreterare, etc. En bra lösning kräver förståelse för verksamheten, men som generellt koncept så tycker jag denna lösning är värd att provas.
Att ha en ”Typ” ser jag som fördelaktigt om man enkelt skall kunna lista alla personer eller alla grupper. Då räcker det att titta på tabellen Entitet() för att få listan – man behöver bara blanda in en tabell. Vill man veta vilka grupper en person är med i så får man lätt fram detta med hjälp av EntitetEntitet()-tabellen. Med denna lösning så kan man också, som påpekats, skapa en tom grupp, och fylla på med medlemmar efter hand utan att det uppstår problem. Jo en svaghet är om man vill skapa fler typer, då kan det bli komplicerat, men för mitt problem kan jag bara se dessa två typer.
Vilken lösning jag slutligen kommer att använda vet jag inte än, men jag känner mig mogen att med dessa förslag i ryggen prova lite nu. Säkerligen har samtliga metoder sina för och nackdelar och slutligen så skall verksamheten och användarna också förstå och använda lösningen. Tack för hjälpen så länge!
Edit:
Jag kan oxå tillägga att jag för närvarande använder förslag 3 i den databas jag bygger, men tyckte att jag fick problem som var den direkta anledningen till att jag gick ut och ställde en fråga här på pellesoft. Jag vet ännu om det blir enklare om jag ändrar till förslag 2, jag får labba lite - hur som så är era erfarenheter mycket värdefulla för mig. Fyll gärna på mera i tråden :-)