คลังข้อมูล & การสืบค้นเชิงลึกด้วย SQL
WHERE · AND/OR/NOT · AS · ORDER BY · MIN/MAX/AVG/COUNT · DISTINCT · DATE · LIKE · IN · BETWEEN
WHERE + ตรรกะ AND/OR/NOT และจัดการ NULL ได้ถูกต้องAS · ORDER BY · DISTINCT · aggregates · LIKE/IN/BETWEEN/DATE ได้ (CLO2)healthinfo.db ต่อจากสัปดาห์ 5 — ไม่ต้องเริ่มใหม่ระบบหน้างาน (OLTP) เน้นเขียนเร็วทีละแถว — คลังข้อมูล (DW) เน้นอ่านเร็วเชิงวิเคราะห์ย้อนหลัง
| มิติ | OLTP | Warehouse |
|---|---|---|
| คำถาม | HN-54001 อยู่ไหน? | HbA1c เฉลี่ยเดือนนี้? |
| สคีมา | 3NF normalized | Star/Snowflake |
| เขียน | แถวเดี่ยวบ่อย | bulk load |
| เครื่องมือ | SQLite/Postgres | SQLite(เล็ก)/DuckDB/BigQuery |
คาบนี้ใช้ healthinfo.db เป็น DW ย่อส่วน
ตารางกลาง fact เก็บเหตุการณ์ · รอบนอก dimension อธิบายบริบท — WHERE/GROUP BY จะวิ่งบน fact
Lab สัปดาห์ 5–6 ใช้ patients รวม fact+dim แบบย่อ — พอฝึก WHERE/aggregates โดยไม่ต้อง JOIN
PRAGMA table_info(patients)birth_date ไม่ใช่ 'Birth Date'SELECT sql FROM sqlite_master WHERE type='table';สเปก: CREATE TABLE · OMOP CDM v5.4
WHERE ทำงานก่อน SELECT (pipeline สไลด์ 12) — ค่าข้อความต้องครอบด้วย '
-- หญิงเท่านั้น SELECT * FROM patients WHERE gender='หญิง' LIMIT 5; -- เบาหวานควบคุมไม่ดี SELECT hn, hba1c FROM patients WHERE hba1c > 7.0; -- ความดันสูง SELECT hn, systolic_bp FROM patients WHERE systolic_bp >= 140;
ตัวดำเนินการ: = != < <= > >=
= NULL → 0 แถว! (สไลด์ 04)ผสมเงื่อนไข + วงเล็บให้ชัด (ลำดับ NOT→AND→OR) · NULL ใช้ IS NULL เท่านั้น
-- หญิง + HbA1c สูง SELECT hn,hba1c FROM patients WHERE gender='หญิง' AND hba1c > 7 LIMIT 5; -- ชาย หรือ ดันสูงมาก SELECT hn,systolic_bp FROM patients WHERE gender='ชาย' OR systolic_bp >= 180 LIMIT 5; -- วงเล็บเปลี่ยนความหมาย! WHERE (g='หญิง' AND a1c>7) OR bp>180 WHERE g='หญิง' AND (a1c>7 OR bp>180)
| นิพจน์ | ผล |
|---|---|
| hba1c = NULL | UNKNOWN → 0 แถว |
| hba1c IS NULL | TRUE ✓ |
| NOT(gender='หญิง') | ไม่รวม gender IS NULL |
ตั้งชื่อใหม่ให้คอลัมน์/ตารางในผลลัพธ์ — ใช้ใน ORDER BY ได้ แต่ใช้ใน WHERE ไม่ได้
SELECT hn AS hospital_no,
hba1c AS a1c
FROM patients LIMIT 5;
-- นิพจน์
SELECT systolic_bp AS sbp,
hba1c*10 AS a1c_x10
FROM patients LIMIT 5;
-- ตาราง (ใช้เยอะตอน JOIN สัปดาห์ 7)
SELECT p.hn FROM patients AS p;
SELECT p.hn FROM patients p; -- ละ AS ได้
| hn | hba1c |
|---|---|
| HN-54001 | 8.1 |
| hospital_no | a1c |
|---|---|
| HN-54001 | 8.1 |
SELECT hn AS x … WHERE x='HN-54001' → Error: no such column (WHERE มาก่อน SELECT)ASC=น้อย→มาก(default) · DESC=มาก→น้อย · เรียงหลายคอลัมน์ได้ · NULL: ASC มาก่อน / DESC มาหลัง
-- HbA1c สูงสุด 5 คน SELECT hn,hba1c FROM patients ORDER BY hba1c DESC LIMIT 5; -- 2 ชั้น: เพศ ↑ แล้ว a1c ↓ SELECT gender,hn,hba1c FROM patients ORDER BY gender ASC, hba1c DESC LIMIT 8; -- ใช้ alias ได้ SELECT hn AS hospital_no, hba1c FROM patients ORDER BY hospital_no LIMIT 5; -- เลขลำดับ (สั้นแต่เปราะ) ORDER BY 2 DESC
ไทย: SQLite เทียบ Unicode binary ('ชาย' < 'หญิง')
Aggregate ยุบหลายแถว→ค่าเดียว · ทุกตัวข้าม NULL ยกเว้น COUNT(*) · DISTINCT ตัดซ้ำ
SELECT COUNT(*) AS n_total, COUNT(hba1c) AS n_measured, AVG(hba1c) AS avg_a1c, MIN(hba1c) AS min_a1c, MAX(hba1c) AS max_a1c FROM patients; SELECT COUNT(DISTINCT gender) AS n_genders FROM patients; -- → 2 -- นับรวม NULL เป็น 0 AVG(COALESCE(hba1c,0))
SELECT gender, AVG(hba1c) ไม่มี GROUP BY → error/ผลสุ่ม (สัปดาห์ 7)ไม่มี DATE type — เก็บ TEXT ISO8601 แล้วใช้ date()/strftime()/julianday()
-- ปี/เดือน
SELECT hn, strftime('%Y',birth_date) y,
strftime('%m',birth_date) m
FROM patients LIMIT 5;
-- อายุ ณ วันนี้
SELECT hn,birth_date,
CAST((julianday('now')-julianday(birth_date))/365.25 AS INT) age
FROM patients LIMIT 5;
-- ครึ่งปีแรก 2025
WHERE birth_date BETWEEN '2025-01-01' AND '2025-06-30'
LIKE '____-__-__' ก่อนคำนวณเสมอ% = กี่ตัวก็ได้ · _ = 1 ตัว · ใช้กับ TEXT
-- ขึ้นต้น HN-54 WHERE hn LIKE 'HN-54%' -- เดือน 02 (ISO) WHERE birth_date LIKE '____-02-__' -- ตัดวันที่ผิดรูปแบบ WHERE birth_date NOT LIKE '%/%' -- escape % จริง WHERE note LIKE '%100\%%' ESCAPE '\'
%…% บนตารางใหญ่ไม่มี index → scan ทั้งตารางIN (a,b,c) = OR ยาวแบบอ่านง่าย · NOT IN+NULL = กับดัก
WHERE gender IN ('ชาย','หญิง')
WHERE hn IN ('HN-54001','HN-54002')
WHERE hba1c IN (7.0,7.5,8.0)
-- subquery (สัปดาห์ 8)
WHERE patient_id IN (SELECT …)
DISTINCT ช่วยสร้างลิสต์: SELECT DISTINCT gender FROM patients
NOT IN (…, NULL) → 0 แถวเสมอ — กรอง NULL ก่อนBETWEEN a AND b ≡ >=a AND <=b — ใช้ได้ทั้งเลขและวันที่ ISO
-- เฝ้าระวัง 6.5–7.0 WHERE hba1c BETWEEN 6.5 AND 7.0 -- pre-hypertension WHERE systolic_bp BETWEEN 120 AND 139 -- ครึ่งปีแรก WHERE birth_date BETWEEN '2025-01-01' AND '2025-06-30'
CAST(x AS REAL) BETWEEN 120 AND 139เขียน SELECT…FROM…WHERE…ORDER BY…LIMIT แต่เครื่องทำตาม pipeline นี้ — จำไว้ใช้ทั้งคอร์ส
-- ✅ alias ใน ORDER BY SELECT hn AS hospital_no, hba1c FROM patients WHERE hba1c > 7 ORDER BY hospital_no LIMIT 5; -- ❌ alias ใน WHERE WHERE hospital_no = 'HN-54001' -- → no such column
GROUP BY → HAVING หลัง WHEREไม่สร้าง DB ใหม่ — เปิดไฟล์จากสัปดาห์ 5 (ถ้าลบ รัน week05 ipynb ใหม่ก่อน)
uv run jupyter lab
import sqlite3, pandas as pd
con=sqlite3.connect("healthinfo.db")
pd.read_sql("""
SELECT hn,hba1c FROM patients
WHERE hba1c BETWEEN 6.5 AND 7
ORDER BY hba1c DESC LIMIT 5""",con)
scoop install sqlitebrowser Open Database → healthinfo.db Execute SQL → Runsqlitebrowser.org/about
scoop install sqlite sqlite3 healthinfo.db sqlite> SELECT COUNT(*) ...> FROM patients WHERE hba1c>7; sqlite> .quitsqlite.org/cli
เปิด week06-sql-queries.ipynb ด้วย uv run jupyter lab
| # | ภารกิจ | § | สไลด์ |
|---|---|---|---|
| 1 | WHERE + AND/OR | §1–2 | 03–04 |
| 2 | AS + alias ใน ORDER BY / WHERE | §3 | 05 |
| 3 | ORDER BY + NULL order | §4 | 06 |
| 4 | COUNT/AVG/MIN/MAX/DISTINCT | §5 | 07 |
| 5 | DATE strftime/julianday + ตัด format ผิด | §6 | 08 |
| 6 | LIKE / IN / BETWEEN | §7–9 | 09–11 |
sqlite3 healthinfo.db "SELECT COUNT(*) FROM patients WHERE hba1c BETWEEN 6.5 AND 7;"| งาน | SQL |
|---|---|
| กรอง | WHERE x>7 AND (y='a' OR z IS NULL) |
| เรียง | ORDER BY a DESC, b ASC |
| สรุป | COUNT/AVG/MIN/MAX(col) |
| ตัดซ้ำ | SELECT DISTINCT gender … |
| แพทเทิร์น | LIKE 'HN-54%' / '____-02-__' |
| หลายค่า | IN (…) / NOT IN (กรอง NULL ก่อน) |
| ช่วง | BETWEEN 120 AND 139 / '2025-01-01' AND … |
| วันที่ | strftime('%Y',d) · julianday(d) |
ดร.วิชิต สมบัติ — staff page · wichit.s@ubu.ac.th