Thuta Learning
How Databases Work
ProjectsData & Databasesbeginner

ပရောဂျက် - သင်ယူရေးပလက်ဖောင်း Database ဒီဇိုင်းဆွဲခြင်း

ဒီခန်းပြီးရင် ဘာတတ်သွားမလဲ

  • ပရောဂျက် - သင်ယူရေးပလက်ဖောင်း Database ဒီဇိုင်းဆွဲခြင်း concept ကို နားလည်ရှင်းပြနိုင်ရန်
  • Diagram/table ကို ဖတ်ပြီး data model/schema/architecture ဘယ်လို ပုံသဏ္ဌာန်ရှိသလဲ ခြေရာခံနိုင်ရန်
  • ကိုယ့် project အတွက် database concept/system ကို ဘယ်လို အသုံးချသင့်သလဲ ရှင်းပြနိုင်ရန်

နားလည်ထားရမယ့် အချက်

ဒီပရောဂျက်ဟာ ယခင်သင်ခန်းစာသုံးခုကို တစ်ပြိုင်နက်တည်း အသုံးချစေတဲ့ design exercise တစ်ခုဖြစ်ပါတယ် - entity တစ်ခုချင်းစီ ဘယ်လိုချိတ်ဆက်မလဲဆိုတာ ဆုံးဖြတ်ဖို့ tables၊ keys နှင့် relationships; data ပွားများခြင်းမရှိအောင် ထိန်းသိမ်းဖို့ constraints နှင့် normalization; design က query လိုအပ်ချက်တွေကို တကယ်ဖြေရှင်းနိုင်ရဲ့လား စစ်ဆေးဖို့ schema diagram နှင့် access pattern တို့ ပါဝင်ပါတယ်။

Thuta Learning ကဲ့သို့ သင်ယူရေးပလက်ဖောင်းတစ်ခုက ဒီ tool အားလုံးကို လိုအပ်လောက်အောင် data model ကြွယ်ဝပေမယ့် တစ်ပြိုင်နက်တည်း စဉ်းစားနိုင်လောက်လောက် ငယ်တာကြောင့် ရွေးချယ်ထားခြင်းဖြစ်ပါတယ်။

  • Users - platform အသုံးပြုသူများ
  • Courses - သင်တန်းများ
  • Lessons - သင်ခန်းစာများ
  • Enrollments - user ↔ course ချိတ်ဆက်ချက်
  • Progress - user ↔ lesson ပြီးစီးမှု
  • Bookmarks - user သိမ်းထားသော lesson

Lesson တစ်ခုက Course တစ်ခုကို foreign key နဲ့ ပိုင်ဆိုင်တာကြောင့် lesson row တိုင်းမှာ course_id တစ်ခု ပါဝင်ပြီး title ကို ထပ်ခါထပ်ခါ မရေးဘဲ ဖြစ်စေပါတယ် - ဒါက normalization သင်ခန်းစာက သတိပေးထားတဲ့ ပွားများမှုအမျိုးအစားပါပဲ။

Enrollments နှင့် Progress တို့က Users ကို Courses နှင့် Users ကို Lessons နှင့် ချိတ်ဆက်ပေးတဲ့ join-style table တွေဖြစ်ပြီး၊ ယခင် relationships သင်ခန်းစာမှာ မိတ်ဆက်ခဲ့တဲ့ many-to-many pattern အတိုင်းပါပဲ။

Unique constraint က ဘာလုပ်ပေးလဲ

Enrollments ထဲက (user_id, course_id) အပေါ် unique constraint တစ်ခုဟာ constraints သင်ခန်းစာကနေ တိုက်ရိုက်ယူထားတာဖြစ်ပါတယ် - user တစ်ဦးကို course တစ်ခုထဲကို နှစ်ကြိမ် enroll မလုပ်နိုင်အောင် database ကိုယ်တိုင်က တားဆီးပေးပါတယ်။

Bookmarks ကလည်း အလားတူ shape ကို ပြန်သုံးထားပြီး user တစ်ဦးနှင့် သူပြန်ကြည့်ချင်တဲ့ lesson တစ်ခုကို ချိတ်ဆက်ပေးပါတယ်။

Schema တစ်ခုကို ပြီးပြည့်စုံပြီလို့ မဆိုမီ real access pattern တွေနှင့် တိုက်စစ်ရပါမယ် - စက္ကူပေါ်မှာ သန့်ရှင်းနေပေမယ့် app က တကယ်မေးမယ့် မေးခွန်းတွေကို ဖြေမပေးနိုင်ရင် ဒီ schema က မပြီးသေးပါဘူး။

text
LEARNING PLATFORM SCHEMA
------------------------
LEARNING PLATFORM SCHEMA
-------------------------
USERS
  user_id       PK
  name
  email

COURSES
  course_id     PK
  title

LESSONS
  lesson_id     PK
  course_id     FK -> COURSES.course_id
  title

ENROLLMENTS
  enrollment_id PK
  user_id       FK -> USERS.user_id
  course_id     FK -> COURSES.course_id
  UNIQUE(user_id, course_id)

PROGRESS
  progress_id   PK
  user_id       FK -> USERS.user_id
  lesson_id     FK -> LESSONS.lesson_id
  completed

BOOKMARKS
  bookmark_id   PK
  user_id       FK -> USERS.user_id
  lesson_id     FK -> LESSONS.lesson_id

RELATIONSHIPS
  COURSES  1---* LESSONS
  USERS    1---* ENROLLMENTS  *---1 COURSES
  USERS    1---* PROGRESS     *---1 LESSONS
  USERS    1---* BOOKMARKS    *---1 LESSONS

လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်

SQL syntax ကို တန်းရိုက်မယ့်အစား ဒီအစီအစဉ်အတိုင်း သင်ယူရေးပလက်ဖောင်း schema ကို တည်ဆောက်ကြည့်ပါ -

Entity များကို စာရင်းလုပ်ပါ

App ပြောနေတဲ့ noun ခြောက်ခု - User, Course, Lesson, Enrollment, Progress, Bookmark - ကို ရေးချပါ။ တစ်ခုစီက table တစ်ခုစီ ဖြစ်လာမည်။

Primary key ရွေးပါ

Entity တစ်ခုစီကို ရိုးရှင်းသော auto-incrementing id ပေးပါ - user_id, course_id, lesson_id အစရှိသည်ဖြင့်။

Foreign key များ ချိတ်ဆက်ပါ

Lessons မှာ course_id FK ရှိမည်။ Enrollments မှာ user_id နှင့် course_id နှစ်ခုလုံး ရှိမည်။ Progress နှင့် Bookmarks တို့တွင် user_id နှင့် lesson_id နှစ်ခုလုံး ရှိမည်။

အရေးကြီးသော constraint ထည့်ပါ

Enrollments (user_id, course_id) အပေါ် unique index တစ်ခုထည့်ပြီး enrollment တစ်ခုတည်းကို ထပ်ခါထပ်ခါ insert မလုပ်နိုင်အောင် လုပ်ပါ။

Access pattern များနှင့် တိုက်စစ်ပါ

Access pattern တစ်ခုစီအတွက် ဘယ် table၊ ဘယ် key က ဖြေနိုင်လဲ နာမည်တပ်ပါ။ key path မရှိရင် schema မှားနေခြင်းဖြစ်သည်။

Draft အများစု ဒီနေရာမှာ မှားတတ်သည်

Noun တွေကို မှန်ကန်စွာ model လုပ်ပြီးသားဖြစ်ပေမယ့် homepage က load တိုင်း အမှန်တကယ် ဖြေဆိုပေးရမယ့် မေးခွန်းကို မေ့နေတတ်ပါတယ်။

အတူတူ စမ်းရေးကြည့်မယ်

javascript
const schema = {
  Users: { pk: "user_id", fields: ["user_id", "name", "email"] },
  Courses: { pk: "course_id", fields: ["course_id", "title"] },
  Lessons: {
    pk: "lesson_id",
    fields: ["lesson_id", "course_id", "title"],
    fk: [{ field: "course_id", references: "Courses.course_id" }]
  },
  Enrollments: {
    pk: "enrollment_id",
    fields: ["enrollment_id", "user_id", "course_id"],
    fk: [
      { field: "user_id", references: "Users.user_id" },
      { field: "course_id", references: "Courses.course_id" }
    ],
    unique: [["user_id", "course_id"]]
  },
  Progress: {
    pk: "progress_id",
    fields: ["progress_id", "user_id", "lesson_id", "completed"],
    fk: [
      { field: "user_id", references: "Users.user_id" },
      { field: "lesson_id", references: "Lessons.lesson_id" }
    ]
  },
  Bookmarks: {
    pk: "bookmark_id",
    fields: ["bookmark_id", "user_id", "lesson_id"],
    fk: [
      { field: "user_id", references: "Users.user_id" },
      { field: "lesson_id", references: "Lessons.lesson_id" }
    ]
  }
};

const accessPatterns = [
  { name: "Get all lessons for a course", table: "Lessons", via: "course_id" },
  { name: "Get a user's progress across all courses", table: "Progress", via: "user_id" },
  { name: "Get all courses a user is enrolled in", table: "Enrollments", via: "user_id" },
  { name: "Get all bookmarks for a user", table: "Bookmarks", via: "user_id" },
  { name: "Check if a user is already enrolled in a course", table: "Enrollments", via: "user_id+course_id" }
];

function checkAccessPattern(pattern) {
  const table = schema[pattern.table];
  if (!table) return { pattern: pattern.name, supported: false, reason: "table not found" };

  if (pattern.via === "user_id+course_id") {
    const hasUnique = table.unique && table.unique.some(
      (u) => u.includes("user_id") && u.includes("course_id")
    );
    return {
      pattern: pattern.name,
      supported: !!hasUnique,
      reason: hasUnique
        ? "supported via unique constraint on (user_id, course_id)"
        : "no supporting constraint"
    };
  }

  const fkFields = (table.fk || []).map((f) => f.field);
  const supported = fkFields.includes(pattern.via) || table.fields.includes(pattern.via);
  return {
    pattern: pattern.name,
    supported,
    reason: supported
      ? `supported via foreign key "${pattern.via}" on ${pattern.table}`
      : "no supporting key"
  };
}

accessPatterns.forEach((p) => {
  const result = checkAccessPattern(p);
  console.log(`${result.supported ? "OK" : "MISSING"} - ${result.pattern}: ${result.reason}`);
});
You should see
OK - Get all lessons for a course: supported via foreign key "course_id" on Lessons
OK - Get a user's progress across all courses: supported via foreign key "user_id" on Progress
OK - Get all courses a user is enrolled in: supported via foreign key "user_id" on Enrollments
OK - Get all bookmarks for a user: supported via foreign key "user_id" on Bookmarks
OK - Check if a user is already enrolled in a course: supported via unique constraint on (user_id, course_id)

၅ မိနစ် စမ်းကြည့်

Certificates table တစ်ခု ထပ်ထည့်ပါ - user တစ်ဦးသည် course တစ်ခုရှိ lesson အားလုံးကို completed ဖြစ်သွားလျှင် certificate ရရှိသည်ဟု ယူဆပါ။ ၎င်းရဲ့ primary key၊ foreign key များကို ဆုံးဖြတ်ပြီး access-pattern list ထဲသို့ ထည့်ကာ 'user တစ်ဦး၏ certificate အားလုံး ရယူခြင်း' ဆိုတဲ့ pattern အသစ်ကို validation function နှင့် ပြန်စစ်ကြည့်ပါ။

သတိလေးတစ်ချက်

App က တကယ်ဘယ်လို query တွေ လုပ်မယ်ဆိုတာ မစစ်ဆေးမီ table တွေကို ဒီဇိုင်းဆွဲလိုက်ပြီး၊ နောက်မှ report အရေးကြီးတစ်ခုက unindexed join သုံးခု လိုအပ်နေကြောင်း တွေ့ရသည်

Enrollments ပေါ်ရှိ unique constraint ကို ကျော်ပြီး duplicate enrollment ကို app code ကသာ ကာကွယ်ဖို့ မှီခိုထားခြင်း - request နှစ်ခု race ဖြစ်တာနှင့် ချက်ချင်း ပျက်စီးသွားနိုင်သည်

PostgreSQL Documentation: ConstraintsHow Databases Work

ဒီနေရာမှာ လူအများမှားတတ်တယ်

  • App က တကယ်ဘယ်လို query တွေ လုပ်မယ်ဆိုတာ မစစ်ဆေးမီ table တွေကို ဒီဇိုင်းဆွဲလိုက်ပြီး၊ နောက်မှ report အရေးကြီးတစ်ခုက unindexed join သုံးခု လိုအပ်နေကြောင်း တွေ့ရသည်
  • Enrollments ပေါ်ရှိ unique constraint ကို ကျော်ပြီး duplicate enrollment ကို app code ကသာ ကာကွယ်ဖို့ မှီခိုထားခြင်း - request နှစ်ခု race ဖြစ်တာနှင့် ချက်ချင်း ပျက်စီးသွားနိုင်သည်
  • ဒီ course က database concept/landscape ကို framework-neutral level မှာသာ သင်ပေးပါတယ် — SQL syntax, PostgreSQL, MongoDB, Redis ကို နက်နက်ရှိုင်းရှိုင်း လေ့လာချင်ရင် SQL, PostgreSQL, MongoDB, Redis tutorial တွေဆီ ဆက်သွားပါ။

လေ့ကျင့်ခန်း

Certificates table တစ်ခု ထပ်ထည့်ပါ - user တစ်ဦးသည် course တစ်ခုရှိ lesson အားလုံးကို completed ဖြစ်သွားလျှင် certificate ရရှိသည်ဟု ယူဆပါ။ ၎င်းရဲ့ primary key၊ foreign key များကို ဆုံးဖြတ်ပြီး access-pattern list ထဲသို့ ထည့်ကာ 'user တစ်ဦး၏ certificate အားလုံး ရယူခြင်း' ဆိုတဲ့ pattern အသစ်ကို validation function နှင့် ပြန်စစ်ကြည့်ပါ။

You'll know it worked when: OK - Get all lessons for a course: supported via foreign key "course_id" on Lessons OK - Get a user's progress across all courses: supported via foreign key "user_id" on Progress OK - Get all courses a user is enrolled in: supported via foreign key "user_id" on Enrollments OK - Get all bookmarks for a user: supported via foreign key "user_id" on Bookmarks OK - Check if a user is already enrolled in a course: supported via unique constraint on (user_id, course_id)

ပရောဂျက် - သင်ယူရေးပလက်ဖောင်း Database ဒီဇိုင်းဆွဲခြင်း | Thuta Learning