Developer တွေအတွက် SQL အခြေခံ

SQL ဆိုတာ Relational Database Management System တွေရဲ့ API လို့ပြောလို့ရတယ်။ ဘာလို့လဲဆိုတော့ consumer (တနည်း database user) တ​ယောက်အနေနဲ့ RDBMS ထဲ data ဖတ်တာ၊ ပြင်တာ၊ သိမ်းတာ၊ ဖျက်တာတွေကို ဒီ SQL ကတဆင့်ပဲ လုပ်ရမှာမို့လို့ပါ။ SQL ကိုနားလည်ချင်ရင် အကောင်းဆုံးက ကိုယ်တိုင်သုံးကြည့်တာပဲ။ ဒါကြောင့် ဖတ်ရင်းနဲ့ တခါတည်း လိုက်လုပ်ကြည့်ဖို့ အရင်ဆုံး Virtual Machine တခုဆောက်။ ဘယ် distro ဖြစ်ဖြစ် အရေးမကြီးဘူး။ ဒါပေမယ့် ဒီစာကိုတော့ Ubuntu 24.04.3 LTS သုံးပြီး ရေးထားတယ်။

VM ရပြီဆိုရင် ကျွန်တော်တို့ PostgreSQL install ကြမယ်။ PostgreSQL က RDBMS product တွေထဲကတခု။ တခြား MySQL, Oracle, SQL Server, IBM DB2 စသဖြင့် flavor တွေအများကြီး ရှိပေမယ့် PostgreSQL က open source လည်းဖြစ်၊ ကျန်တာတွေထက်စာရင် innovative လည်းအများကြီး ပိုဖြစ်တာမို့လို့ အခုဒီနေ့သင်ခန်းစာအတွက် PostgreSQL သုံးကြမယ်။ PostgreSQL ရဲ့ official website ထဲမှာ install လို့ရတဲ့နည်းတွေ ပြထားတယ်။ အလွယ်ဆုံးကတော့ ကိုယ့် distro ရဲ့ package manager ကတဆင့် install လုပ်တာပဲ။

sudo apt install curl ca-certificates
sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
. /etc/os-release

sudo sh -c "echo 'deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $VERSION_CODENAME-pgdg main' > /etc/apt/sources.list.d/pgdg.list"

sudo apt update
sudo apt -y install postgresql-17

install လို့ပြီးရင် cluster ရဲ့ status ကိုစစ်ကြည့်ပါ။

controlplane:~$ pg_lsclusters
Ver Cluster Port Status Owner    Data directory              Log file
17  main    5432 online postgres /var/lib/postgresql/17/main /var/log/postgresql/postgresql-17-main.log

စစ်ကြည့်လို့ online ဖြစ်မနေဘူးဆိုရင် service template unit ကိုသုံးပြီး ကိုယ်သုံးမယ့် PostgreSQL server ကို start လုပ်ပေးပါ။

controlplane:~$ sudo systemctl start postgresql@17-main

ဒါဆို SQL စရေးရအောင်။ ဒီနေ့ ကျွန်တော်တို့လုပ်မှာက တက္ကသိုလ်တခုရဲ့ management system အတွက် database system တခုဆောက်ပြီး SQL ကိုလေ့ကျင့်ကြမှာဖြစ်တယ်။ ဒီ system မှာကျောင်းသားတွေ၊ ဆရာတွေ၊ ဌာနတွေ၊ အတန်းတွေနဲ့ ကျောင်းသားတယောက်ချင်းစီရဲ့ တက်ရောက်နေတဲ့ အတန်းနဲ့ ဆရာတယောက်ချင်းစီရဲ့ သူတို့သင်နေတဲ့ အတန်းတွေရှိမယ်။

ကျောင်းသားတယောက်မှာ သူတို့ကို identify လုပ်ဖို့ id, firstname နဲ့ lastname, email address နဲ့ ကျောင်းလာအပ်ထားတဲ့ ရက်စွဲ ဒီ information တွေကို သိမ်းကြမယ်။ NoSQL Database တွေနဲ့မတူတဲ့တချက်က RDBMS တွေက schema enforce လုပ်တာဖြစ်လို့ data မရေးခင် မဖတ်ခင်မှာ ဘယ်လို data မျိုးနဲ့ အလုပ်လုပ်ရမယ်၊ ဒီ data တွေက ဘယ်လို structure ရှိတယ်ဆိုတာကို ပြောပြပေးရပါတယ်။ ဒီတော့ PostgreSQL ကို ကျောင်းသားတွေက ဘယ်လို structure ရှိလဲ အသိပေးဖို့အတွက် Students table တခုကို CREATE TABLE ဆိုတဲ့ SQL statement သုံးပြီး ဆောက်ရမယ်။ နာမည်ကို Students လို့ပေးမှ မဟုတ်ပါဘူး။ ကိုယ့်အတွက် ဖတ်ကြည့်လို့ အဓိပ္ပါယ်ရှိမယ့်ဟာဖြစ်ရင် ရပါပြီ။

နောက်တခု သိထားရမှာက RDBMS မှာ data တခု ဥပမာ ကျောင်းသားတယောက်ရဲ့ အချက်အလက်၊ ဌာနရဲ့ အချက်အလက်ဆိုတာမျိုးတွေကို row တခုအနေနဲ့ သိမ်းပါတယ်။ နောက်ပြီး အဲဒီ row ရှိမယ့် table ရဲ့ structure ကိုဖွဲ့စည်းထားမယ့် data type ကိုအတိအကျ ကြေညာဖို့လိုတယ်။ ဥပမာ ကိုယ်က -32768 ကနေ 32767 ရှိတဲ့ 2 bytes integer ပဲသိမ်းချင်တာဆိုရင် SMALLINT column ဖြစ်ကြောင်း ကြေညာရမယ်။ ကိုယ်သုံးမယ့် integer က 4 bytes ရှိမယ်လို့ထင်ရင် INTEGER column သုံးလို့ရတယ်။ တကယ်လို့ စာလုံးတွေ သိမ်းချင်တာဆိုရင်လည်း character ဘယ်နှလုံးရှိမှာလဲဆိုတဲ့အပေါ် မူတည်ပြီး VARCHAR(2), VARCHAR(n) စသဖြင့် သုံးလို့ရတယ်။ အဲဒီတော့ row တခုထဲက အချက်အလက် အသေးစိတ် (ဥပမာ ကျောင်းသားရဲ့ Date of Birth) ကိုဖတ်တဲ့အခါ programming context နဲ့ပြောရရင် Array တွေလို O(1) access ရှိသွားလိမ့်မယ်။ LinkedList လို Graph လို Tree လို traversal လုပ်ဖို့မလိုဘူး။

CREATE TABLE IF NOT EXISTS Students (
  id BIGSERIAL PRIMARY KEY,
  first_name TEXT NOT NULL,
  last_name TEXT NOT NULL,
  email TEXT UNIQUE NOT NULL,
  enrollment_date DATE NOT NULL CHECK (enrollment_date <= CURRENT_DATE)
);

SQL က declarative ဖြစ်ပေမယ့် database system ဖြစ်နေတဲ့အတွက် လူသားရဲ့ intent ကို as is ယူပါတယ်။ တနည်းပြောရရင် ဖြည့်တွေးပေးတာမျိုး သိပ်မလုပ်ထားဘူး။ ဒါကြောင့် CREATE TABLE ကိုနှစ်ခါ run မိရင် ရှိပြီးသား Table ကိုပြန်ဆောက်တယ်ထင်လို့ error တက်ပါလိမ့်မယ်။ IF NOT EXISTS ကဒီအတွက် safeguard ဖြစ်တယ်။ ရှိပြီးရင် ပြန်မဆောက်နဲ့ဆိုတဲ့သဘော။ နောက်ပြီး BIGSERIAL က BIGINT နဲ့တူတယ်။ တခုပဲ။ BIGSERIAL က stateful ဖြစ်တယ်။ ဘယ် integer တန်ဖိုးကိုရောက်နေလဲဆိုတာကို ဖတ်နိုင်တယ်။ increment လုပ်နိုင်တယ်။ ဒီတော့ id သတ်မှတ်ခိုင်းတဲ့နေရာမှာ အဆင်ပြေတယ်ပေါ့။ implementation ကတော့ BIGSERIAL သုံးရင် နောက်ကွယ်မှာ function တခုဆောက်ပြီး BIGINT တွေတိုးတိုးသွားတယ်။

first_name နဲ့ last_name အတွက် TEXT ဆိုတဲ့ data type ကိုသုံးသွားတာလည်း တွေ့ရမယ်။ TEXT က VARCHAR ကိုမှ character အရေအတွက် limit မရှိတဲ့ type ပါ။ ဒါဆို စောစောက ပြောခဲ့သလို O(1) access ရပါ့ဦးမလား တွေးစရာရှိတယ်။ PostgreSQL က TEXT အတွက် null bitmap, TOAST စတဲ့ internal mechanism တွေသုံးပြီး O(1) ထွက်အောင် လုပ်ထားတာမို့ စိတ်ပူဖို့မလိုပါဘူး။ နောက် NOT NULL ဆိုတာက တန်ဖိုးရှိရမယ် အလွတ်ဖြစ်နေလို့မရဘူးလို့ ဆိုလိုတယ်။ email column ကိုလည်း UNIQUE constraint သုံးပြီး row တွေမှာ email ပြန်မထပ်အောင် လုပ်ထားတယ်။ ဒီတော့ registration လုပ်တဲ့အခါ duplicate email တွေကို application level မှာ check စရာမလိုပဲ database ဆီကို offload လို့ရမယ်။ enrollment_date ကိုလည်း CHECK သုံးပြီး CURRENT_DATE ထက် အနာဂတ်ကို ကြိုရောက်နေတဲ့ date မျိုးတွေမဖြစ်ဖို့ စစ်ထားတယ်။ CURRENT_DATE ဆိုတာက SQL standard special function ဖြစ်ပြီး လက်ရှိအချိန်ကိုသိရဖို့ သုံးတယ်။ ဒါပေမယ့် လက်ရှိအချိန်ဆိုတာက database server ကို install လုပ်ထားတဲ့ VM ရဲ့ wall clock ကိုဆိုလိုတယ်။

ဒါပြီးရင် ဌာနတွေအကြောင်း ဆက်ကြမယ်။ Departments table ကိုတော့ id နဲ့ name ပဲပေးပြီး ရိုးရိုးရှင်းရှင်းထားကြမယ်။ email တွေလိုပဲ ဌာနနာမည်တွေကို မထပ်စေချင်တဲ့အတွက် UNIQUE ကိုသုံးထားတယ်။

CREATE TABLE IF NOT EXISTS Departments (
  id SERIAL PRIMARY KEY,
  name TEXT UNIQUE NOT NULL
);

အတန်းတွေကိုတော့ Courses table နဲ့သတ်မှတ်ကြမယ်။ ထုံးစံအတိုင်း id, name တွေပါမယ်။ credits ဆိုတဲ့ column ရှိမယ်။ အတန်းတခုအတွက် credit က 0 ထက်ပိုပြီး 10 ထက်မကျော်ရဘူးဆိုတဲ့ CHECK ထည့်ထားတယ်။ ဒီနေရာမှာ တခုပြောစရာရှိတာက အတန်းတခုက ဘယ် department ရဲ့အတန်းလဲဆိုတာသိရဖို့ ဌာနနာမည်ကို hardcode မလုပ်ထားပဲ Departments table ထဲက row တခုချင်းစီရဲ့ id ​ကိုပဲ foreign key အဖြစ် reference ယူသုံးထားတာ တွေ့ရမယ်။ ဒါကို normalization လို့ခေါ်တယ်။ normalization နဲ့ပတ်သက်ရင် 1NF, 2NF စသဖြင့် theory တွေရှိပေမယ့် လက်တွေ့မှာ စာအုပ်ကြီးဆန်ဆန် ဘယ် NF ဆိုတာမျိုးက အလုပ်မဖြစ်ပါဘူး။ အရေးကြီးတာက access pattern က efficient ဖြစ်နေဖို့ရယ်၊ maintainable integrity ရှိနေဖို့ရယ်ပါပဲ။

CREATE TABLE IF NOT EXISTS Courses (
  id BIGSERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  department_id INT NOT NULL,
  credits SMALLINT NOT NULL CHECK (credits > 0 AND credits <= 10),
  FOREIGN KEY(department_id) REFERENCES Departments(id)
);

နောက်တဆင့်အနေနဲ့ Instructors table ဆောက်ကြမယ်။ ဒီမှာတော့ ထူးထူးခြားခြား ထပ်ပြောစရာမရှိပါဘူး။ id, name, department id နဲ့ hire date ရှိမယ်။ hire date ကိုလည်း အနာဂတ်ကို ကြိုရောက်မနေဖို့ CHECK ထားတယ်။ department id ကိုလည်း ထုံးစံအတိုင်း foreign key သုံးပြီးချိတ်ထားတယ်။

CREATE TABLE IF NOT EXISTS Instructors (
  id BIGSERIAL PRIMARY KEY,
  first_name TEXT NOT NULL,
  last_name TEXT NOT NULL,
  department_id INT NOT NULL REFERENCES Departments(id),
  hire_date DATE NOT NULL CHECK (hire_date <= CURRENT_DATE)
);

RDBMS မှာသိရမယ့် relationship 3 ခုရှိတယ်။ one to one, one to many နဲ့ many to many relationship တွေပေါ့။ one to one ကတော့ ရှင်းတယ်။ Instructors table နဲ့ Courses Table ဆိုပြီးရှိတဲ့အထဲက ဆရာတယောက်က သူသီးသန့် သင်ရိုးဆွဲထားတဲ့ အတန်းတခုပဲ သင်တယ်။ one instructor to one course ပေါ့။ one to many ကကျ ဆရာတယောက်က အတန်းတွေ အများကြီးကို သင်မှာ။ ဒါပေမယ့် အတန်းတတန်း ဥပမာ Computer Science ဌာနက Data Structures အတန်းကိုဆိုရင် ဆရာတယောက်ကပဲ သင်တယ်။ နောက်တယောက် ဒီအတန်းကိုပဲ သင်နေတာမျိုး မရှိဘူး။ ဒီ relationship ကိုပဲ inverse ပြန်ကြည့်ရင် course တခုတည်းကိုပဲ ဆရာတွေ အများကြီးက သင်တာဖြစ်နိုင်တယ်။ ဒါပေမယ့် ဆရာတယောက်ကတော့ အတန်းတတန်းပဲ ရှိမယ်ပေါ့။ တကယ်လို့ ဆရာ​တယောက်က course တွေ အများကြီးကိုသင်မယ်။ course တခုကိုလည်း ဆရာတွေအများကြီးက ကိုယ်စီအတန်းတွေနဲ့ သင်ကြမယ်ဆိုရင်ကျ many to many relationship ဖြစ်သွားပြီ။

အခု system မှာ ဆရာတယောက်စီက course တွေအများကြီး သင်လို့ရသလို course တခုကိုလည်း ဆရာတွေ အများကြီးက ကိုယ်ပိုင်အတန်းတွေနဲ့ သင်နိုင်တဲ့ သဘော​ဖြစ်တဲ့အတွက် many to many relationship ဖြစ်တယ်။ ဒါ့အပြင် ကျောင်းသားတယောက်ကလည်း course တွေအများကြီး ယူလို့ရသလို course တခုကိုလည်း ကျောင်းသားတွေ အများကြီးက တက်နိုင်တဲ့အတွက် ဒါကလည်း many to many ပဲဖြစ်တယ်။ many to many relationship ကိုတော့ RDBMS မှာ junction table အနေနဲ့ implement လုပ်ကြပါတယ်။ ဒါကြောင့် CourseEnrollments table (Students နဲ့ Courses table တွေရဲ့ junction table)၊ CourseInstructors table (Instructors နဲ့ Courses table တွေရဲ့ junction table) နှစ်ခုကို ထပ်ဆောက်ဖို့လိုတယ်။

CREATE TABLE IF NOT EXISTS CourseEnrollments (
  student_id BIGINT NOT NULL,
  course_id BIGINT NOT NULL,
  enrollment_date DATE NOT NULL CHECK (enrollment_date <= CURRENT_DATE),
  grade CHAR(2) CHECK (grade IN ('A', 'A+', 'A-', 'B', 'B+', 'B-', 'C', 'D', null)),
  PRIMARY KEY (student_id, course_id),
  FOREIGN KEY(student_id) REFERENCES Students(id) ON DELETE CASCADE,
  FOREIGN KEY(course_id) REFERENCES Courses(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS CourseInstructors (
  instructor_id BIGINT NOT NULL,
  course_id BIGINT NOT NULL,
  PRIMARY KEY (instructor_id, course_id),
  FOREIGN KEY(instructor_id) REFERENCES Instructors(id) ON DELETE CASCADE,
  FOREIGN KEY(course_id) REFERENCES Courses(id) ON DELETE CASCADE
);

CourseEnrollments ရဲ့ schema ထဲမှာ grade CHAR(2) က NOT NULL မပါတာကို တွေ့ရလိမ့်မယ်။ အဲဒါက ဘာကြောင့်ဖြစ်ရတာလဲဆိုရင် ကျောင်းသားတယောက်က Course ပြီးဆုံးအောင် တက်ပြီးမှ A+ ရလား B ရလား စသဖြင့် သိနိုင်မှာဖြစ်ပြီး တက်နေတဲ့ကာလမှာတော့ grade က null ပဲရှိနေမှာမို့လို့ဖြစ်တယ်။ grade ရဲ့တန်ဖိုးကိုလည်း character 2 လုံးထိ ဖြစ်နိုင်တဲ့ CHECK (IN (set)) နဲ့ check ထားတာတွေ့ရလိမ့်မယ်။ ဒီတော့ မေးစရာတခုက ဘယ်အချိန်မှာ cte (stored procedure) တွေသုံးပြီး validation လုပ်ရမလဲ။ ဘယ်အချိန်မှာ inline CHECK သုံးရမလဲ။ တကယ်လို့ ကိုယ့် validation logic က ဒီ row တခုတည်းက တန်ဖိုးတွေ (same column ဒါမှမဟုတ် cross column တွေ) ကိုပဲ မှီခိုထားတယ်ဆိုရင် inline CHECK နဲ့တင်လုံလောက်ပါတယ်။ အဲလိုမဟုတ်ပဲ table ထဲက data ကို ဖတ်ရမယ်ဆိုရင်တော့ stored procedure တွေလိုပါတယ်။ stored procedure နဲ့ window function တွေအကြောင်းကိုတော့ နောက်တခေါက်မှပဲ ရှင်းပြတော့မယ်။

PostgreSQL က sensitive server တခုဖြစ်တဲ့အတွက် server ကို လာ connect လုပ်မယ့် connection တွေက authentication (ဘယ်သူလဲ) ရော၊ authorization (လုပ်ခွင့်ရှိလား) ဆိုတာကိုပါ အတည်ပြုပေးနိုင်ရတယ်။ ဒီအတွက် PostgreSQL server မှာ user တွေ permission တွေ identity information တွေကို ထည့် configure လုပ်ပေးဖို့လိုတယ်။ ဒါပေမယ့် SQL အကြောင်းပြောတဲ့ article မှာ PostgreSQL ရဲ့ detail တွေပါလာရင် ရှုပ်သွားမှာစိုးလို့ postgresql-common package က installation မှာ ဆောက်သွားပေးတဲ့ postgres user ကိုပဲ သုံးကြမယ်။ အဲဒီ user က database server အတွက်တော့ root user ဖြစ်တယ်။ postgres user အဖြစ်ချိန်းဖို့အတွက် Host OS ပေါ်မှာ sudo permission တော့ရှိဖို့လိုမယ်။

schema တွေကို /usr/share/migrations/ directory ထဲမှာ create_tables.sql လို့ခေါ်တဲ့ file ထဲထည့်သိမ်းလိုက်ပါ။

controlplane:~$ mkdir -p /usr/share/migrations
controlplane:~$ touch /usr/share/migrations/create_tables.sql

ပြီးရင် အပေါ်မှာပြောထားသလို postgres user ဆီချိန်းပြီး create_tables.sql ထဲက SQL statement တွေကို run လိုက်မယ်။ ဘာ error မှမရှိဘူးဆိုရင် ပုံထဲကလို output ရလာလိမ့်မယ်။

controlplane:~$ sudo -iu postgres
postgres@controlplane:~$ psql
psql (17.6 (Ubuntu 17.6-1.pgdg24.04+1))
Type "help" for help.

postgres=# \i /usr/share/migrations/create_tables.sql
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
postgres=#

ဆောက်လိုက်တဲ့ table တွေကို list လုပ်ပြီး မှန်မမှန် စစ်​ကြည့်ပါ။

postgres=# \dt
               List of relations
 Schema |       Name        | Type  |  Owner   
--------+-------------------+-------+----------
 public | courseenrollments | table | postgres
 public | courseinstructors | table | postgres
 public | courses           | table | postgres
 public | departments       | table | postgres
 public | instructors       | table | postgres
 public | students          | table | postgres
(6 rows)

postgres=#

table တခုချင်းစီရဲ့ schema ကိုလည်း \d သုံးပြီး ပြန်ကြည့်လို့ရတယ်။

postgres=# \d Courses
                                Table "public.courses"
    Column     |   Type   | Collation | Nullable |               Default               
---------------+----------+-----------+----------+-------------------------------------
 id            | bigint   |           | not null | nextval('courses_id_seq'::regclass)
 name          | text     |           | not null | 
 department_id | integer  |           | not null | 
 credits       | smallint |           | not null | 
Indexes:
    "courses_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "courses_credits_check" CHECK (credits > 0 AND credits <= 10)
Foreign-key constraints:
    "courses_department_id_fkey" FOREIGN KEY (department_id) REFERENCES departments(id)
Referenced by:
    TABLE "courseenrollments" CONSTRAINT "courseenrollments_course_id_fkey" FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE CASCADE
    TABLE "courseinstructors" CONSTRAINT "courseinstructors_course_id_fkey" FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE CASCADE

postgres=#

query တွေကို Beginner, Intermediate, Advanced နဲ့ Expert ဆိုပြီး level 4 ခုခွဲ ရေးကြည့်ရအောင်။ Beginner level မှာတော့ ရိုးရှင်းတဲ့ SELECT တွေကို လေ့ကျင့်ကြမယ်။ SQL မှာ SELECT ဆိုတာ CRUD (Create, Read, Update, Delete) ဆိုပြီးရှိကြတဲ့အထဲက Read အတွက် သုံးရတဲ့ keyword ဖြစ်တယ်။

Question 1 (Beginner level)

ကျောင်းသားတွေကို သူတို့ရဲ့ ID, နာမည်နဲ့ enrollment date တွေပါအောင် list ပေးပါ။

ဒီမေးခွန်းက ကျောင်းသားတွေကို list ခိုင်းရုံဖြစ်လို့ Students table ကိုပဲ query ဖို့လိုပါတယ်။ တခြားဘာ join မှမလိုအပ်ပါဘူး။ SELECT columns FROM table နဲ့တင် လုံလောက်ပါတယ်။

Answer:

SELECT id, first_name, last_name, enrollment_date FROM Students;

Question 2 (Beginner level)

Computer Science ဌာနက offer လုပ်တဲ့ course တွေကို list ပေးပါ။

ဒီမေးခွန်းက Courses table က course တွေကို list ခိုင်းရုံဖြစ်ပေမယ့် course တွေက Computer Science ဌာနက ဖြစ်ရမယ်ဆိုတဲ့ ကန့်သတ်ချက် ပါနေပါတယ်။ ဒါပေမယ့် course တခုချင်းစီမှာ ဌာန ID ပဲပါပြီး နာမည်မပါတဲ့အတွက် ဌာနနာမည် သိရဖို့ဆိုရင် JOIN table2 ON table2.c = table.c ပုံစံကို သုံးပြီး Departments table နဲ့ join ဖို့လိုပါတယ်။ ဒါ့အပြင် WHERE clause သုံးပြီး ဌာနနာမည်က Computer Science ဖြစ်တဲ့ row တွေပဲကျန်အောင် စစ်ထုတ်ဖို့လည်း လိုပါမယ်။ နောက်ပြီး readability အတွက် AS course, AS department စသဖြင့် AS alias စတိုင်လ် သုံးထားတာလည်း တွေ့ရမယ်။

Answer:

SELECT course.id, course.name
FROM Courses AS course
JOIN Departments AS department ON course.department_id = department.id
WHERE department.name = 'Computer Science';

Question 3 (Beginner level)

Instructor တွေကို နာမည်နဲ့ သူတို့ဌာနရဲ့ နာမည်တွဲပြီး list ပေးပါ။

ဒီမေးခွန်းက Question 2 နဲ့ခပ်ဆင်ဆင်ပါ။ Instructors table က instructor တွေမှာ ဌာန ID ပဲပါပြီး နာမည်မပါတဲ့အတွက် ဌာနနာမည် သိရဖို့ဆိုရင် JOIN table2 ON table2.c = table.c ပုံစံကို သုံးပြီး Instructors table နဲ့ Departments table ကို join ဖို့လိုပါတယ်။

Answer:

SELECT instructor.id, instructor.first_name, instructor.last_name, department.name
FROM Instructors AS instructor
JOIN Departments AS department ON department.id = instructor.department_id;

Question 1 (Immediate level)

Introduction to Programming course ကို enroll လုပ်ထားတဲ့ ကျောင်းသားနာမည်တွေကို enrollment date နဲ့တကွ list ပေးပါ။

ဒီ query က ကျောင်းသားနာမည်၊ course နာမည် နှစ်ခုလိုတာ တွေ့ရမယ်။ ဒီတော့ Students နဲ့ Courses table တွေကို query ရမယ့်သဘော။ ဒါပေမယ့် ဒီ table နှစ်ခုရဲ့ ချိတ်ဆက်မှုက junction table ကတဆင့် ချိတ်ထားတာဖြစ်လို့ ဘယ်ကျောင်းသားတွေက Introduction to Programming course ကို enroll လုပ်ထားသလဲ သိချင်ရင် CourseEnrollments table ကိုပါ query ပြီး WHERE clause နဲ့ filter ဖို့လိုတယ်။

Answer:

SELECT student.first_name, student.last_name, enrollment.enrollment_date
FROM Students AS student 
JOIN CourseEnrollments AS enrollment ON student.id = enrollment.student_id 
JOIN Courses AS course ON enrollment.course_id = course.id 
WHERE course.name = 'Intro to Programming';

Question 2 (Immediate level)

course တွေကို သင်မယ့်ဆရာနာမည်နဲ့တွဲပြီး list လုပ်ပေးပါ။

ဒီ query က course နာမည်၊ instructor နာမည် နှစ်ခုလိုတာ တွေ့ရမယ်။ ဒီတော့ Instructors နဲ့ Courses table ရယ်၊ junction table ဖြစ်တဲ့ CourseInstructors ရယ်ကို query ရုံပါပဲ။

Answer:

SELECT course.name, instructor.first_name, instructor.last_name 
FROM Courses AS course 
JOIN CourseInstructors ON CourseInstructors.course_id = course.id 
JOIN Instructors AS instructor ON CourseInstructors.instructor_id = instructor.id;

Question 3 (Immediate level)

course တွေကို သူတို့တခုချင်းစီမှာ စာရင်းပေးထားတဲ့ ကျောင်းသားအရေအတွက်နဲ့အတူ list ပေးပါ။

course အကြောင်းကို Courses table ကရမယ်။ စာရင်းသွင်းထားတာနဲ့ပတ်သက်လို့က CourseEnrollments table ကရမယ်။ အဲနှစ်ခုကို ထုံးစံအတိုင်း course id ကနေချိတ်မယ်။ တခုပိုလာတာက အရေအတွက်ပဲ။ အဲဒီအတွက် concept အသစ် 3 ခုကိုသိဖို့လိုမယ်။

ပထမတခုက COUNT(columns) ဆိုတဲ့ aggregate function ဖြစ်တယ်။ COUNT က left table နဲ့ right table ကို JOIN လိုက်မယ်ဆိုရင် ဘယ်ဖက် table က 1 row အတွက် match ဖြစ်တဲ့ ညာဖက် table က n row(s) ကို column တခု သို့မဟုတ် တခုထက်ပိုတဲ့ column တွေရဲ့ nullness အပေါ်အခြေပြုပြီး ရေတွက်ပေးတယ်။ ဒီမှာဆိုရင် Courses table ထဲက course တခုစီအတွက် match ဖြစ်တဲ့ CourseEnrollments table ထဲက enrollment တွေကို ရေတွက်ရမယ်။

ဒုတိယတခုက match လိုက်လို့ရလာတဲ့ intermediate table ထဲက row တွေကို bucket အသေးလေးတွေအဖြစ် GROUP ရမယ်။ ဘာလို့လဲဆိုတော့ အဲဒီ intermediate table ကြီးတခုလုံးက ရှိရှိသမျှ enrollment တိုင်းကို ဖော်ပြနေတယ်။ တကယ်လိုချင်တာက per course enrollment ဖြစ်တဲ့အတွက် course.id နဲ့ group ပစ်ပြီး intermediate table ကြီးကို bucket အသေးလေးတွေအဖြစ် ခွဲထုတ်ဖို့လိုအပ်တယ်။ ဒါမှသာ COUNT နဲ့ပြန်ရေတွက်လိုက်တဲ့အခါ course တခုစီအတွက် အရေအတွက် ရလာမယ်။ တခုရှိတာက ဒီမေးခွန်းရဲ့ logic အတွက် GROUP BY မှာ course.id နဲ့တင်လုံလောက်ပေမယ့် SELECT ထဲ course.name ကိုထည့်ရင် error ပြလိမ့်မယ်။ ဘာလို့လဲဆိုတော့ id တူရင် name ကလည်း တူမယ်ဆိုတဲ့ အာမခံချက်မျိုး database level မှာပေးမထားတဲ့အတွက် id အတူတူချင်းကို name မတူရင် ဘယ် course name ကို ရွေးပြရမလဲဆိုတဲ့ ဖြေရှင်းချက်မျိုး PostgreSQL ကိုကြိုပြောပြထားဖို့လိုတယ်။ အဲဒီတော့ course.name ကိုပါ GROUP BY ထဲထည့်ပြီး course.id နဲ့ course.name အတွဲတခုကို bucket တခုဆောက်ခိုင်းလိုက်နိုင်တယ်။ ဒါပေမယ့် လက်တွေ့မှာတော့ id တူရင် name တူမှာဖြစ်လို့ GROUP BY တခုအတွက် bucket တခုပဲထွက်မှာပါ။

နံပါတ်သုံးက INNER JOIN အစား LEFT JOIN ကိုသုံးရမယ်။ INNER JOIN က ဘယ်နဲ့ညာမှာ match ဖြစ်တဲ့ row တွေကိုပဲ intermediate table ထဲထည့်တာဖြစ်ပြီး LEFT JOIN ကတော့ ဘယ်ဖက် table ထဲက row အကုန်လုံးကို intermediate table ထဲထည့်ဆောက်တယ်။ ညာဖက် table က ရလာမယ့် column တွေကတော့ NULL ဖြစ်နေမှာပေါ့လေ။ အခုမေးခွန်းမှာ တယောက်မှ စာရင်းပေးမထားတဲ့ course ရှိနေရင်လည်း အဲဒီ course ကို final list ထဲ enrolled count 0 နဲ့ပါစေချင်တဲ့အတွက် LEFT JOIN ကိုသုံးရမယ်။

Answer:

SELECT course.name, COUNT(enrollment.course_id)
FROM Courses AS course
LEFT JOIN CourseEnrollments AS enrollment ON enrollment.course_id = course.id
GROUP BY course.id, course.name;

Question 1 (Advanced level)

course နှစ်ခုထက်ပိုတက်နေတဲ့ ကျောင်းသားတွေကို list ပေးပါ။

Question 2 (Advanced level)

Philosophy ဌာနက course တွေချည်း သီးသန့်တက်နေတဲ့ ကျောင်းသားတွေကို list ပေးပါ။

Question 3 (Advanced level)

ကျောင်းသားတယောက်မှ စာရင်းမပေးရသေးတဲ့ course တွေကို သူတို့ကို သင်မယ့်ဆရာနာမည်နဲ့တွဲပြီး list ပေးပါ။

Question 4 (Advanced level)

ကျောင်းသားတယောက်ချင်းစီအတွက် သူတို့ရထားတဲ့ credit စုစုပေါင်းကို တွက်ပြီး list ပေးပါ။



ကိုယ့်ဟာကိုယ် ဖြေရှင်းကြည့်ပါ။ အဖြေတွေကို ဒီ article မှာပဲ update လုပ်ပြီး ရှင်းပြပါ့မယ်။