sql >> Databasteknik >  >> RDS >> Mysql

Hur lagrar man flera alternativ i en enda tabell?

Läs om Datanormalisering , Allmän indexering koncept och Främmande nyckel begränsningar för att hålla data ren med referensintegritet. Detta kommer att få dig igång.

Att lagra data i arrayer kan verka naturligt för dig på papper, men för db-motorn är prestandan för det mesta utan indexanvändning. Dessutom kommer du att upptäcka på dag 2 att att komma till och underhålla din data kommer att vara en mardröm.

Följande bör få dig att komma igång med en bra start när du pysslar. Gå med också.

create table student
(   studentId int auto_increment primary key,
    fullName varchar(100) not null
    -- etc
);

create table dept
(   deptId int auto_increment primary key,
    deptName varchar(100) not null -- Economics
    -- etc
);

create table course
(   courseId int auto_increment primary key,
    deptId int not null,
    courseName varchar(100) not null,
    -- etc
    CONSTRAINT fk_crs_dept FOREIGN KEY (deptId) REFERENCES dept(deptId)
);

create table SCJunction
(   -- Student/Course Junction table (a.k.a Student is taking the course)
    -- also holds the attendance and grade
    id int auto_increment primary key,
    studentId int not null,
    courseId int not null,
    term int not null, -- term (I am using 100 in below examples for this term)
    attendance int not null, -- whatever you want, 100=always there, 0=he must have been partying,
    grade int not null, -- just an idea   
    -- See (Note Composite Index) at bottom concerning next two lines.
    unique key(studentId,courseId,term), -- no duplicates allowed for the combo (note student can re-take it next term)
    key (courseId,studentId),
    CONSTRAINT fk_sc_student FOREIGN KEY (studentId) REFERENCES student(studentId),
    CONSTRAINT fk_sc_courses FOREIGN KEY (courseId) REFERENCES course(courseId)
);

Skapa testdata

insert student(fullName) values ('Henry Carthage'),('Kim Billings'),('Shy Guy'); -- id's 1,2,3
insert student(fullName) values ('Shy Guy');

insert dept(deptName) values ('History'),('Math'),('English'); -- id's 1,2,3

insert course(deptId,courseName) values (1,'Early Roman Empire'),(1,'Italian Nation States'); -- id's 1 and 2 (History dept)
insert course(deptId,courseName) values (2,'Calculus 1'),(2,'Linear Algebra A'); -- id's 3 and 4 (Math dept)
insert course(deptId,courseName) values (3,'World of Chaucer'); -- id 5 (English dept)

-- show why FK constraints are important based on data at the moment
insert course(deptId,courseName) values (66,'Fly Fishing 101'); -- will generate error 1452. That dept 66 does not exist
-- That error is a good error to have. Better than faulty data

-- Have Kim (studentId=2) enrolled in a few courses
insert SCJunction(studentId,courseId,term,attendance,grade) values (2,1,100,-1,-1); -- Early Roman Empire, term 100 (made up), unknown attendance/grade
insert SCJunction(studentId,courseId,term,attendance,grade) values (2,4,100,-1,-1); -- Linear Algebra A
insert SCJunction(studentId,courseId,term,attendance,grade) values (2,5,100,-1,-1); -- World of Chaucer

-- Have Shy Guy (studentId=3) enrolled in one course only. He is shy
insert SCJunction(studentId,courseId,term,attendance,grade) values (3,5,100,-1,-1); -- Early Roman Empire, term 100 (made up), unknow attendance/grade
-- note if you run that line again, the Error 1062 Duplicate entry happens. Can't take same course more than once per term

Några enkla frågor.

Vilken kurs finns på vilken avdelning?

visa alla, använder tabellalias (förkortningar) för att göra skrivning mindre, läsbarheten (ibland) bättre

select c.courseId,c.courseName,d.deptId,d.deptName
from course c
join dept d
on c.deptId=d.deptId
order by d.deptName,c.courseName -- note the order
+----------+-----------------------+--------+----------+
| courseId | courseName            | deptId | deptName |
+----------+-----------------------+--------+----------+
|        5 | World of Chaucer      |      3 | English  |
|        1 | Early Roman Empire    |      1 | History  |
|        2 | Italian Nation States |      1 | History  |
|        3 | Calculus 1            |      2 | Math     |
|        4 | Linear Algebra A      |      2 | Math     |
+----------+-----------------------+--------+----------+

Vem går kursen World of Chaucer den här terminen?

(att känna till courseId=5)

Nedanstående drar nytta av ett av våra sammansatta index i SCJunction. En sammansatt är ett index på mer än en kolumn.

select s.StudentId,s.FullName
from SCJunction j
join student s
on j.studentId=s.studentId
where j.courseId=5 and j.term=100
+-----------+--------------+
| StudentId | FullName     |
+-----------+--------------+
|         2 | Kim Billings |
|         3 | Shy Guy      |
+-----------+--------------+

Vad är Kim Billings inskriven i denna termin?

select s.StudentId,s.FullName,c.courseId,c.courseName
from SCJunction j
join student s
on j.studentId=s.studentId
join course c
on j.courseId=c.courseId
where s.studentId=2 and j.term=100
order by c.courseId DESC -- descending, just for the fun of it
+-----------+--------------+----------+--------------------+
| StudentId | FullName     | courseId | courseName         |
+-----------+--------------+----------+--------------------+
|         2 | Kim Billings |        5 | World of Chaucer   |
|         2 | Kim Billings |        4 | Linear Algebra A   |
|         2 | Kim Billings |        1 | Early Roman Empire |
+-----------+--------------+----------+--------------------+

Kim är överväldigad, så släpp matteklassen

delete from SCJunction
where studentId=2 and courseId=4 and term=100

kör det valda uttalandet ovan som visar vad Kim tar:

+-----------+--------------+----------+--------------------+
| StudentId | FullName     | courseId | courseName         |
+-----------+--------------+----------+--------------------+
|         2 | Kim Billings |        5 | World of Chaucer   |
|         2 | Kim Billings |        1 | Early Roman Empire |
+-----------+--------------+----------+--------------------+

Ah, mycket lättare term. Pappa kommer dock inte att vara glad.

Notera sådant som SCJunction.term. Mycket kan skrivas om det, jag hoppar över det för tillfället mest, annat än att säga att det också borde vara i ett FK någonstans. Du kanske vill att din termin ska se ut mer som VÅR 2015 och inte en int.

Och vad gäller id. Det är så här jag skulle göra det. Det är personlig preferens. Det skulle kräva att man känner till id-nummer och letar upp dem. Andra kan välja att ha ett kurs-ID något som HIST101 och inte 17. De är mycket mer läsbara (men långsammare i indexet (knappt). Så gör det som är bäst för dig.

Note Composite Index

Ett sammansatt index (INDEX betyder NYCKEL och vice versa) är ett som kombinerar flera kolumner för snabb datahämtning. Beställningarna vänds för de två kompositerna i SCJunction-tabellen så att, beroende på universum av frågor som går efter dina data, kan db-motorn välja vilket index som ska användas för snabbast hämtning baserat på kolumnen längst till vänster du går efter .

När det gäller den unika nyckeln, #1, är kommentaren bredvid den som säger att inga dubbletter (vilket betyder skräpdata) är ganska självförklarande. Exempelvis kan elev 1 kurs 1 termin 1 inte finnas två gånger i den tabellen.

Ett avgörande begrepp att förstå är begreppet left-most ordning av kolumnnamn i ett index.

För frågor som går efter studentId endast , sedan nyckeln som har studentId listad först (left-most ) är använd. I frågor som går efter courseId endast , sedan nyckeln som har courseId längst till vänster används. I frågor som går efter både studentId och courseId kan db-motorn bestämma vilken sammansatt nyckel som ska användas.

När jag säger "gå efter" menar jag i on clause eller where clause skick.

Om man inte skulle ha dessa två sammansatta nycklar (med kolumn 1 och 2 i dem vända), då i frågor där den sökta kolumnen inte är left-most indexeras, skulle du inte gynnas av nyckelanvändning och drabbas av en långsam tabellsökning för data att returnera.

Så dessa två index kombinerar följande två begrepp

  • Snabb datahämtning baserad på längst till vänster eller båda (kolumnerna studentId och CourseId)
  • Tvinga till icke-duplicering av data i den tabellen baserat på studentId, CourseId och termvärden

The Takeaway

Den viktiga hämtningen är att Junction-tabeller möjliggör snabb indexhämtning, och förnuftig hantering av data kontra kommaavgränsade data (array-tänkesätt) som är inpackade i en kolumn, och allt elände med att använda en sådan konstruktion.



  1. DATEPART() Exempel i SQL Server

  2. Fel när databas släpps (Kan inte skicka '.test\', felnr:17)

  3. PostgreSQL lastbalansering i molnet på ett enkelt sätt

  4. Funktion kontra lagrad procedur i SQL Server