{
  "assessmentTests": {
    "sql_test": {
      "name": "SQL Test",
      "desc": "30 คำถามสถานการณ์เกี่ยวกับการกรอง NULL semantics การรวมตาราง การทำรวม ฟังก์ชันหน้าต่าง และข้อจำกัด — ค้นหาว่า SQL ของคุณตรงกับความหมายของ \"ความชำนาญ SQL\" ในประกาศรับสมัครหรือไม่",
      "recommendation": "โปรไฟล์ทักษะ SQL ของคุณ",
      "results": {
        "beginner": {
          "name": "ผู้เริ่มต้น",
          "desc": "คุณสามารถเขียน SELECT statement ที่ใช้งานได้และกรองตามเงื่อนไขสองสามข้อ แต่คำถามที่คุณเสียคะแนนไปจะรวมกลุ่มกันไปที่การจัดการ NULL และการทำงานของ join มากกว่า syntax — การเปรียบเทียบคอลัมน์กับ NULL ด้วย = สิ่งที่ LEFT JOIN ยังคงเก็บไว้จริงๆ ขอบเขตของ BETWEEN แต่ละอย่างรวมเข้าไหม ไม่มีสิ่งใดที่เกี่ยวกับการไม่ดีในฐานข้อมูล สิ่งเหล่านี้เป็นกฎเฉพาะที่ทำให้คนที่เรียน SQL ผ่านการลองผิดลองถูกติดขัด พวกมันมีความสำคัญในการทำงานเพราะแต่ละอย่างคือจุดที่แบบสอบถาม (query) จะส่งคืนจำนวนแถวที่ดูเหมือนจริง แต่ผิด",
          "recommendation": "เริ่มจากสามสิ่งตามลำดับนี้: ทำไม WHERE col = NULL ถึงไม่ตรงกับอะไรเลย (และต้องใช้ IS NULL แทน) LEFT JOIN ทำให้แถวที่ไม่ตรงกันหล่นออกไปอย่างไรขณะที่ LEFT JOIN ยังคงเก็บไว้ และขอบเขตที่แน่นอนของ BETWEEN คือเท่าไร Mode Analytics' SQL tutorial และ PostgreSQL documentation ทั้งคู่ครอบคลุมทั้งสามอย่างด้วยตัวอย่างที่รันได้"
        },
        "intermediate": {
          "name": "ระดับกลาง",
          "desc": "คุณจัดการแบบสอบถาม (query) รายงานประจำวันได้อย่างสบาย ๆ — join GROUP BY การกรองตรงไปตรงมา — และไม่จะช้าลงจากงาน dashboard หรืองานวิเคราะห์แบบ ad-hoc ทั่วไป ช่องว่างระหว่างที่นี่กับ Advanced ส่วนใหญ่อยู่ในสิ่งที่เกิดขึ้นเมื่อ clause โต้ตอบกับกันและกัน: WHERE clause บนตาราง join ทาง right ที่เงียบๆ เปลี่ยน LEFT JOIN ให้กลับเป็น INNER JOIN join ที่แฟนออกจำนวนแถวก่อนที่จะใช้ aggregate NOT IN ที่เงียบๆ คืนอะไรเลยเพราะ NULL เข้ามาลงในแบบสอบถาม (subquery) พวกนี้เป็นชนิดของบัค (bug) ที่ผ่านการตรวจสอบอย่างรวดเร็วได้และเฉพาะเมื่อมีคนเปรียบเทียบจำนวนรวมกับรายงานอื่นเท่านั้นจึงปรากฏขึ้น",
          "recommendation": "มุ่งเน้นไปที่วิธีที่ clause โต้ตอบกันมากกว่าสิ่งที่แต่ละอย่างทำเพียงลำพัง: WHERE filtering บน RIGHT-hand columns ของ LEFT JOIN จำนวนแถวที่เพิ่มจาก one-to-many join ก่อนที่ aggregate จะถูกนำไปใช้กับพวกมัน และทำไม NOT IN ถึงหักหลังเมื่อ NULL เข้ามา (EXISTS มักจะไม่) จากนั้น HAVING vs WHERE เพราะการแยกแบบนี้ทำให้คนที่รู้ clause ทั้งสองแล้วติดขัด"
        },
        "advanced": {
          "name": "ขั้นสูง",
          "desc": "นี่คือระดับที่ประกาศรับสมัครส่วนใหญ่หมายถึงจาก \"Strong SQL\" คุณอ่านแบบสอบถาม (query) ที่มี join หลายตัวและสามารถคาดการณ์จำนวนแถวก่อนที่จะรัน คุณรู้ว่าทำไมชุดผลลัพธ์ (result set) จึงเปลี่ยน ไม่ใช่เพียงแค่ว่ามันเปลี่ยนไปแล้ว และคุณหันไปใช้ CTE หรือ window function แทน nested subquery เมื่อนั่นเป็นเครื่องมือที่ชัดเจนกว่า สิ่งที่แยก band นี้จากด้านบน คือด้านป้องกันของงาน: รู้ว่าฟังก์ชันการจัดอันดับ (ranking function) ที่จะใช้เมื่อ ties มี ความสำคัญเท่าไร สิ่งที่ COMMIT ทำให้เห็นจริงและมีคนเห็นจริง และว่ากฎ ON DELETE ที่หายไปจริงๆ ทำอะไรที่ฐานข้อมูล",
          "recommendation": "ผลักดันไปยังส่วนที่ปกป้องข้อมูลที่คนอื่นแตะต้องเช่นกัน: ความแตกต่างระหว่าง RANK DENSE_RANK และ ROW_NUMBER ภายใต้ ties สิ่งที่ COMMIT จริงๆ ทำให้มองเห็นและให้ใครแล้ว และพฤติกรรม foreign key บน DELETE ใช้ The Index newsletter หรือ SQL for Devs' deep dive บน window function และ transaction isolation เป็นจุดหยุดถัดไปสำหรับทั้ง"
        },
        "expert": {
          "name": "ผู้เชี่ยวชาญ",
          "desc": "คุณได้คะแนนสูงสุดในทุกส่วน — การกรองและ NULL semantics join และ set operation การทำรวมและ subquery และ window function CTE และข้อจำกัด ในทางปฏิบัติ นั่นหมายความว่าคุณสามารถรับแบบสอบถาม (query) รายงานของคนแปลกหน้าแล้วอธิบายว่าทำไมจึงส่งคืนจำนวนแถวที่เป็นเช่นนี้ ไม่ใช่แค่สิ่งที่ syntax บอกว่าควรจะเป็น ซึ่งเป็นทักษะที่ยากกว่าและมีค่ามากกว่า ที่ระดับนี้ query language ไม่ค่อยเป็นปัจจัยจำกัด ปัจจัยจำกัดมักจะเป็นการออกแบบ schema หรือขนาดของข้อมูลที่อยู่ใต้มัน",
          "recommendation": "ผลตอบแทนอยู่ในแผนการดำเนินการ (execution plan) และการออกแบบแล้ว: อ่าน EXPLAIN output ก่อนที่จะสมมติว่าแบบสอบถามช้า กลยุทธ์การสร้างดัชนี (indexing) เป็น tradeoff เทียบกับต้นทุนการเขียน ไม่ใช่ win ฟรี และการตัดสินใจทำให้ปกติ (normalization) ที่ยืดหยุ่นเมื่อ schema ขยาย ถ้าคุณกำลังถูกคัดกรองสำหรับบทบาท ให้อธิบายบาค (bug) เช่น join fan-out หรือ NOT IN/NULL trap ที่คุณพบในการผลิต (production) มากกว่าการตั้งชื่อ SQL feature — มันแสดงเหตุผล ไม่ใช่คำศัพท์"
        }
      },
      "questions": [
        {
          "question": "ตารางมีคอลัมน์ age ที่สามารถเป็น NULL ได้ คุณรัน SELECT * FROM users WHERE age = NULL จะส่งคืนกี่แถว แม้ว่าหลายแถวมี age ตั้งไว้เป็น NULL",
          "options": [
            {
              "icon": "",
              "label": "ทุกแถวที่ age เป็น NULL เพราะ = ตรงกับ NULL เหมือน value อื่นใด"
            },
            {
              "icon": "",
              "label": "Zero — การเปรียบเทียบสิ่งใดกับ NULL ด้วย = ให้ UNKNOWN ไม่ใช่ TRUE ดังนั้นไม่มีแถวตรงกัน IS NULL จึงจำเป็น"
            },
            {
              "icon": "",
              "label": "Syntax error — NULL ไม่สามารถปรากฏบนด้านขวาของ ="
            },
            {
              "icon": "",
              "label": "ทุกแถวในตาราง เพราะการเปรียบเทียบ NULL ตั้งเป็น TRUE โดยค่าเริ่มต้น"
            }
          ]
        },
        {
          "question": "คุณรัน SELECT DISTINCT department, role FROM employees DISTINCT ลบรายซ้ำของอะไร",
          "options": [
            {
              "icon": "",
              "label": "การรวมกัน department และ role ด้วยกัน — แถวจะถูกลบก็ต่อเมื่อทั้งสองคอลัมน์ตรงกับแถวอื่นอย่างแน่นอน"
            },
            {
              "icon": "",
              "label": "ค่า department ซ้ำเท่านั้น เก็บทุก role"
            },
            {
              "icon": "",
              "label": "ค่า role ซ้ำเท่านั้น เก็บทุก department"
            },
            {
              "icon": "",
              "label": "ไม่มีอะไร — DISTINCT ทำงานกับคอลัมน์เดียวเท่านั้น"
            }
          ]
        },
        {
          "question": "คอลัมน์ name ถูกกรองด้วย WHERE name LIKE 'A_' ค่าใดต่อไปนี้ตรงกัน: 'A', 'Al', 'Ana', 'Ally'",
          "options": [
            {
              "icon": "",
              "label": "'A' — ขีดล่าง (underscore) เป็นตัวเลือก และตรงกับศูนย์ตัวอักษรหรือมากกว่า"
            },
            {
              "icon": "",
              "label": "'Al' — ขีดล่าง (underscore) ตรงกับอักษรตัวเดียวพอดี ดังนั้น LIKE 'A_' ตรงกับสตริงสองตัวอักษรใด ๆ ที่เริ่มต้นด้วย A"
            },
            {
              "icon": "",
              "label": "'Ana' และ 'Ally' — ขีดล่าง (underscore) ตรงกับอักษรต่ออักษรจำนวนใด ๆ"
            },
            {
              "icon": "",
              "label": "ทั้งสี่ค่า ตรงกันหมด เพราะ LIKE ไม่สนใจความยาว"
            }
          ]
        },
        {
          "question": "คอลัมน์ price ถูกกรองด้วย WHERE price BETWEEN 10 AND 20 แถวที่มี price ตรง 10 หรือตรง 20 รวมอยู่หรือไม่",
          "options": [
            {
              "icon": "",
              "label": "ไม่ — BETWEEN ไม่รวมจุดสิ้นสุดทั้งสองจุด"
            },
            {
              "icon": "",
              "label": "ใช่ — BETWEEN รวมทั้งสองปลาย เทียบเท่า price >= 10 AND price <= 20"
            },
            {
              "icon": "",
              "label": "เพียง price = 10 เท่านั้นที่รวมขีดจำกัดบน exclusive"
            },
            {
              "icon": "",
              "label": "เพียง price = 20 เท่านั้นที่รวมขีดจำกัดล่าง exclusive"
            }
          ]
        },
        {
          "question": "ตาราง orders มี 10 แถว และ 3 แถวมี NULL shipped_date SELECT COUNT(*) FROM orders ส่งคืนอะไร และ SELECT COUNT(shipped_date) FROM orders ส่งคืนอะไร",
          "options": [
            {
              "icon": "",
              "label": "10 จากนั้น 10 — COUNT นับแถวเสมอโดยไม่คำนึงถึง NULL"
            },
            {
              "icon": "",
              "label": "7 จากนั้น 7 — ทั้งสองรูปแบบข้ามแถว NULL"
            },
            {
              "icon": "",
              "label": "10 จากนั้น 7 — COUNT(*) นับทุกแถว COUNT(column) นับเฉพาะแถวที่คอลัมน์นั้นไม่ใช่ NULL"
            },
            {
              "icon": "",
              "label": "10 จากนั้น 3 — COUNT(column) นับเฉพาะค่า NULL"
            }
          ]
        },
        {
          "question": "คุณรัน SELECT name, salary FROM employees ORDER BY 2 DESC 2 หมายถึงอะไร",
          "options": [
            {
              "icon": "",
              "label": "Syntax error — ORDER BY ยอมรับเฉพาะชื่อคอลัมน์ไม่ใช่ตัวเลข"
            },
            {
              "icon": "",
              "label": "แถวที่สองของผลลัพธ์"
            },
            {
              "icon": "",
              "label": "ค่า literal 2 ใช้เป็น tiebreaker"
            },
            {
              "icon": "",
              "label": "คอลัมน์ที่สองในรายการ SELECT salary — ORDER BY ยอมรับ position number ของคอลัมน์เป็น shorthand"
            }
          ]
        },
        {
          "question": "ใน SQL มาตรฐาน 'Ana' || ' ' || 'Lee' ประเมินค่าเป็นอะไร",
          "options": [
            {
              "icon": "",
              "label": "Syntax error — SQL ไม่มีตัวดำเนินการต่อสตริง"
            },
            {
              "icon": "",
              "label": "3 — || ถูกตีความเป็น boolean OR และส่งคืน count"
            },
            {
              "icon": "",
              "label": "'AnaLee' — || ต่อสตริงโดยไม่เก็บช่องว่าง"
            },
            {
              "icon": "",
              "label": "'Ana Lee' — || เป็นตัวดำเนินการต่อสตริง SQL มาตรฐาน"
            }
          ]
        },
        {
          "question": "ข้อใดต่อไปนี้เทียบเท่าอย่างแน่นอนกับ WHERE status IN ('open', 'pending', 'review')",
          "options": [
            {
              "icon": "",
              "label": "WHERE status = 'open' AND status = 'pending' AND status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status != 'open' OR status != 'pending' OR status != 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status = 'open' OR status = 'pending' OR status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status LIKE 'open,pending,review'"
            }
          ]
        },
        {
          "question": "customers มี 100 แถว 20 แถวไม่เคยสั่งซื้อเลย คุณรัน SELECT c.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id ลูกค้า 20 คนที่ไม่มีคำสั่งซื้อปรากฏในผลลัพธ์หรือไม่",
          "options": [
            {
              "icon": "",
              "label": "ใช่ ครั้งเดียว โดยแสดง o.id เป็น NULL"
            },
            {
              "icon": "",
              "label": "ใช่ แต่เฉพาะเมื่อพวกเขาปรากฏในข้อ WHERE เท่านั้น"
            },
            {
              "icon": "",
              "label": "ไม่ — INNER JOIN ส่งคืนเฉพาะแถวที่มีการจับคู่ในตารางทั้งสอง ดังนั้นลูกค้าที่มีศูนย์คำสั่งซื้อจะหล่นออกทั้งหมด"
            },
            {
              "icon": "",
              "label": "ใช่ แต่ซ้ำครั้งเดียวต่อคอลัมน์ใน orders"
            }
          ]
        },
        {
          "question": "คุณต้องการลูกค้าทั้งหมด ไม่ว่าจะมีคำสั่งซื้อหรือไม่ ดังนั้นคุณจึงเขียน SELECT c.id, o.total FROM customers c LEFT JOIN orders o ON c.id = o.customer_id WHERE o.total > 100 สิ่งนี้ยังคงส่งคืนลูกค้าที่มีศูนย์คำสั่งซื้อหรือไม่",
          "options": [
            {
              "icon": "",
              "label": "ไม่ — การกรอง o.total ใน WHERE clause ทำให้แถว NULL ที่ LEFT JOIN สร้างขึ้นสำหรับลูกค้าที่ไม่จับคู่หล่นไป ดังนั้นจึงทำตัวเหมือน INNER JOIN"
            },
            {
              "icon": "",
              "label": "ใช่ — LEFT JOIN มักจะเก็บทุกแถวจาก customers ไม่ว่าอะไรจะตามมา"
            },
            {
              "icon": "",
              "label": "ใช่ โดยแสดง o.total เป็น 0 สำหรับลูกค้าที่ไม่มีคำสั่งซื้อ"
            },
            {
              "icon": "",
              "label": "ไม่ — LEFT JOIN เงียบๆ แปลงตัวเองเป็น RIGHT JOIN เมื่อมีการเพิ่มข้อ WHERE"
            }
          ]
        },
        {
          "question": "ตาราง employees มีคอลัมน์ id และคอลัมน์ manager_id ที่ชี้ไปที่ id ของแถวอื่น เพื่อแสดงรายชื่อพนักงานแต่ละคนข้างเคียงชื่อผู้จัดการของพวกเขา คุณเชื่อมโยงตารางกับตัวเอง: SELECT e.name, m.name FROM employees e JOIN employees m ON e.manager_id = m.id รูปแบบนี้เรียกว่าอะไร",
          "options": [
            {
              "icon": "",
              "label": "A cross join — ทุกพนักงานจะจับคู่กับทุกผู้จัดการ"
            },
            {
              "icon": "",
              "label": "A recursive join — มันดึงสายการจัดการเต็มทั้งหมด"
            },
            {
              "icon": "",
              "label": "A self-join — ตารางเดียวกันจะเชื่อมโยงกับตัวเอง ใช้นามแฝง (alias) สองตัวที่แตกต่างกัน"
            },
            {
              "icon": "",
              "label": "สิ่งนี้เป็น SQL ที่ไม่ถูกต้อง — ตารางไม่สามารถเชื่อมโยงกับตัวเอง"
            }
          ]
        },
        {
          "question": "แบบสอบถาม SELECT สองแบบที่มีคอลัมน์เดียวกันจะรวมกับ UNION ถ้าแบบสอบถามทั้งสองส่งคืนแถวเดียวกัน สำเนาของแถวนั้นกี่สำเนาปรากฏในผลลัพธ์ขั้นสุดท้าย",
          "options": [
            {
              "icon": "",
              "label": "One — UNION ลบแถวซ้ำเข้าในผลลัพธ์รวม UNION ALL จะเก็บสำเนาทั้งสอง"
            },
            {
              "icon": "",
              "label": "Two — UNION เก็บทุกแถวจากแบบสอบถามทั้งสอง"
            },
            {
              "icon": "",
              "label": "Zero — UNION ลบแถวใด ๆ ที่ปรากฏในแบบสอบถามทั้งสอง"
            },
            {
              "icon": "",
              "label": "มันขึ้นอยู่กับว่าแบบสอบถามใดแสดงแถวก่อน"
            }
          ]
        },
        {
          "question": "ตารางขนาด (sizes) มี 3 แถว สีสัน (colors) มี 4 แถว SELECT * FROM sizes CROSS JOIN colors จะส่งคืนกี่แถว",
          "options": [
            {
              "icon": "",
              "label": "7 — CROSS JOIN บวกจำนวนแถว"
            },
            {
              "icon": "",
              "label": "12 — CROSS JOIN จะส่งคืนการรวมกันของแถวจากทั้งสองตาราง (3 x 4)"
            },
            {
              "icon": "",
              "label": "3 — CROSS JOIN จะส่งคืนแถวเดียวต่อแถวในตารางแรก"
            },
            {
              "icon": "",
              "label": "0 — CROSS JOIN ต้อง ON condition หรือจะส่งคืนไม่มี"
            }
          ]
        },
        {
          "question": "orders มี 1 แถวสำหรับคำสั่ง #100 และ order_items มี 3 แถวสำหรับคำสั่ง #100 (หนึ่งต่อรายการ) คุณรัน SELECT o.id, o.total FROM orders o JOIN order_items i ON o.id = i.order_id WHERE o.id = 100 คำสั่ง #100 จะส่งคืนกี่แถว",
          "options": [
            {
              "icon": "",
              "label": "3 — join ให้ผลลัพธ์แถวหนึ่งต่อแถว matching order_items ดังนั้นแถวเดียวของคำสั่ง #100 จึงซ้ำซ้อนครั้งต่อรายการ"
            },
            {
              "icon": "",
              "label": "1 — orders มีแถวหนึ่งเท่านั้นสำหรับคำสั่ง #100 ดังนั้น join ไม่สามารถให้ผลลัพธ์มากกว่า"
            },
            {
              "icon": "",
              "label": "4 — แถวหนึ่งสำหรับคำสั่งบวกแถวหนึ่งต่อรายการ"
            },
            {
              "icon": "",
              "label": "0 — join ตารางแถวเดียวกับตารางแถวสามตัวบน non-unique key ล้มเหลว"
            }
          ]
        },
        {
          "question": "SELECT c.name, o.id FROM customers c RIGHT JOIN orders o ON c.id = o.customer_id ส่งคืนแถวเดียวกับข้อใดต่อไปนี้",
          "options": [
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o LEFT JOIN customers c ON c.id = o.customer_id — สลับลำดับตารางและใช้ LEFT JOIN แทน RIGHT JOIN เทียบเท่า"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c LEFT JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o CROSS JOIN customers c"
            }
          ]
        },
        {
          "question": "ใน SQL มาตรฐาน คุณรัน SELECT department, name, AVG(salary) FROM employees GROUP BY department นี่เป็น query ที่ถูกต้องหรือไม่",
          "options": [
            {
              "icon": "",
              "label": "ใช่ — GROUP BY ต้องรวมเฉพาะ department เพราะมันอยู่เป็นอันดับแรก"
            },
            {
              "icon": "",
              "label": "ไม่ — name ถูกเลือก แต่ไม่ได้รวมและไม่อยู่ใน GROUP BY และ SQL มาตรฐานต้องการทุกคอลัมน์ที่ไม่รวมเลือกให้ปรากฏใน GROUP BY clause"
            },
            {
              "icon": "",
              "label": "ใช่ — SQL อัตโนมัติเลือก name ตามใจชอบหนึ่งรายการต่อ department"
            },
            {
              "icon": "",
              "label": "ไม่ — AVG() ไม่สามารถรวมกับ GROUP BY ในแบบสอบถามเดียวกัน"
            }
          ]
        },
        {
          "question": "คุณต้องการ department ที่มีเงินเดือนเฉลี่ยเกิน 80000 Clause ใดต่อไปนี้ที่กรองค่าที่รวมแล้ว เช่น AVG(salary) — WHERE หรือ HAVING",
          "options": [
            {
              "icon": "",
              "label": "WHERE — HAVING ใช้เฉพาะกับแบบสอบถาม UNION"
            },
            {
              "icon": "",
              "label": "ทั้งสองอย่างทำงานเหมือนกันกับฟังก์ชัน aggregate"
            },
            {
              "icon": "",
              "label": "ไม่มีอะไร — การกรอง aggregate ต้องการ subquery"
            },
            {
              "icon": "",
              "label": "HAVING — WHERE กรองแถวแต่ละแถวก่อนที่ grouping เกิดขึ้น HAVING กรองกลุ่มหลังจาก aggregation"
            }
          ]
        },
        {
          "question": "คุณเขียน SELECT salary * 1.1 AS new_salary FROM employees WHERE new_salary > 50000 สิ่งนี้รัน",
          "options": [
            {
              "icon": "",
              "label": "ใช่ — aliases ที่กำหนดใน SELECT มักมีให้ WHERE ในแบบสอบถามเดียวกัน"
            },
            {
              "icon": "",
              "label": "ใช่ แต่เฉพาะสำหรับ aliases เชิงตัวเลข"
            },
            {
              "icon": "",
              "label": "ไม่ — AS ไม่ได้รับอนุญาตในแบบสอบถามที่กรองด้วย WHERE"
            },
            {
              "icon": "",
              "label": "ไม่ — WHERE ถูกประเมินก่อนที่ SELECT จะกำหนด alias new_salary ดังนั้น alias ยังไม่มีอยู่จริง"
            }
          ]
        },
        {
          "question": "SELECT name FROM employees e WHERE salary > (SELECT AVG(salary) FROM employees WHERE department = e.department) ทำไมสิ่งนี้จึงเรียกว่า correlated subquery",
          "options": [
            {
              "icon": "",
              "label": "เพราะมันใช้ JOIN แทน WHERE clause"
            },
            {
              "icon": "",
              "label": "Subquery ด้านในอ้างอิง e.department จาก outer query ดังนั้นต้องประเมินใหม่สำหรับทุกแถวที่ outer query พิจารณา"
            },
            {
              "icon": "",
              "label": "เพราะมันส่งคืนมากกว่าหนึ่งคอลัมน์"
            },
            {
              "icon": "",
              "label": "เพราะมันรัน exactly once ก่อนที่ outer query เริ่มต้น"
            }
          ]
        },
        {
          "question": "Subquery SELECT manager_id FROM employees จะส่งคืนค่า NULL บางค่าพร้อมกับ id จริง คุณรัน SELECT name FROM employees WHERE id NOT IN (SELECT manager_id FROM employees) จะเกิดอะไรขึ้น",
          "options": [
            {
              "icon": "",
              "label": "มันส่งคืนทุกพนักงานที่ไม่ใช่ผู้จัดการ ไม่สนใจ NULL"
            },
            {
              "icon": "",
              "label": "มันส่งคืน zero แถว — NULL ตัวเดียวในรายการ NOT IN ทำให้ทุกการเปรียบเทียบเป็น UNKNOWN ดังนั้นไม่มีแถวสามารถสนใจเงื่อนไข"
            },
            {
              "icon": "",
              "label": "มันยกตัวอักษร error เพราะ NOT IN ไม่สามารถใช้กับ subquery"
            },
            {
              "icon": "",
              "label": "มันส่งคืนทุกพนักงาน เพราะ NULL ถูกมองว่าเป็น wildcard"
            }
          ]
        },
        {
          "question": "SELECT name FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id) สิ่งนี้ส่งคืนอะไร",
          "options": [
            {
              "icon": "",
              "label": "ทุกลูกค้าที่มีอย่างน้อยหนึ่งแถวใน orders — EXISTS ตรวจสอบเฉพาะว่า subquery จะส่งคืนแถวใด ๆ ไม่ใช่ค่าที่มี"
            },
            {
              "icon": "",
              "label": "ทุกลูกค้า เพราะ SELECT 1 มักจะส่งคืน true"
            },
            {
              "icon": "",
              "label": "An error เพราะ subquery เลือกตัวเลขแทนชื่อคอลัมน์"
            },
            {
              "icon": "",
              "label": "เฉพาะลูกค้าที่มีคำสั่งซื้อเพียงหนึ่งแบบ"
            }
          ]
        },
        {
          "question": "คุณเขียน SELECT name, (SELECT order_id FROM orders WHERE customer_id = c.id) AS last_order FROM customers c และลูกค้าที่ให้มานั้นมี 3 แถวใน orders จะเกิดอะไรขึ้นเมื่อแบบสอบถามนี้รัน",
          "options": [
            {
              "icon": "",
              "label": "มันส่งคืน order_id matching แรกและ silently ไม่สนใจอีกสองแถว"
            },
            {
              "icon": "",
              "label": "มันส่งคืนรายการ comma-separated ของทั้งสาม order_id"
            },
            {
              "icon": "",
              "label": "มันส่งคืน 3 แถวสำหรับลูกค้านั้น หนึ่งต่อคำสั่ง"
            },
            {
              "icon": "",
              "label": "มันยกตัวอักษร error ที่รัน — scalar subquery ในรายการ SELECT ต้องส่งคืน at most แถวเดียว และนี่ส่งคืนสาม"
            }
          ]
        },
        {
          "question": "สี่แถวผูกพันกันเพื่อให้ได้คะแนนสูงสุด ใช้ RANK() ORDER BY score DESC ทั้งสี่ได้รับ rank 1 แถวถัดไปจะได้ rank อะไร",
          "options": [
            {
              "icon": "",
              "label": "5 — RANK() ปล่อยช่องว่างเท่ากับจำนวนแถวที่ผูกพันก่อนจะทำต่อ"
            },
            {
              "icon": "",
              "label": "2 — RANK() มักจะเพิ่มขึ้นทีละหนึ่งหลังจากการผูกพัน"
            },
            {
              "icon": "",
              "label": "1 — ทุกแถวที่เหลือก็ได้ rank 1"
            },
            {
              "icon": "",
              "label": "4 — RANK() เริ่มนับใหม่จากจำนวนการผูก"
            }
          ]
        },
        {
          "question": "สี่แถวผูกพันกันเพื่อให้ได้คะแนนสูงสุด ใช้ DENSE_RANK() ORDER BY score DESC ทั้งสี่ได้รับ rank 1 แถวถัดไปจะได้ rank อะไร",
          "options": [
            {
              "icon": "",
              "label": "5 — DENSE_RANK() ทำงานเหมือน RANK()"
            },
            {
              "icon": "",
              "label": "1 — DENSE_RANK() กำหนด rank เดียวกันให้กับทุกแถวที่เหลือ"
            },
            {
              "icon": "",
              "label": "2 — DENSE_RANK() ไม่มีช่องว่าง ดังนั้นค่า distinct ถัดไปมักจะได้ rank ถัดไปต่อเนื่อง"
            },
            {
              "icon": "",
              "label": "3 — DENSE_RANK() ข้ามหนึ่ง rank ต่อกลุ่มผูก"
            }
          ]
        },
        {
          "question": "SUM(amount) OVER (PARTITION BY region ORDER BY amount) ถูกเพิ่มเข้ามาในแบบสอบถาม PARTITION BY region ทำให้อะไรที่นี่",
          "options": [
            {
              "icon": "",
              "label": "มันเริ่มต้น running sum ใหม่แยกสำหรับแต่ละ region แทนการสะสมทั่ว result set ทั้งหมด"
            },
            {
              "icon": "",
              "label": "มันกรองผลให้เป็น region เดียว"
            },
            {
              "icon": "",
              "label": "มันจัดกลุ่มและยุบแถวลงในหนึ่ง region เหมือน GROUP BY"
            },
            {
              "icon": "",
              "label": "มันเรียง region ตามตัวอักษรก่อนการสะสม"
            }
          ]
        },
        {
          "question": "ROW_NUMBER() OVER (ORDER BY score DESC) ถูกนำไปใช้กับห้าแถว สองแถวมี score ที่ผูกพันกันทุกประการ สามารถสองแถวได้รับ row number เดียวกัน",
          "options": [
            {
              "icon": "",
              "label": "ใช่ — แถวที่ผูกพันมักจะแบ่ง row number เดียวกัน"
            },
            {
              "icon": "",
              "label": "เฉพาะเมื่อ PARTITION BY ถูกใช้เช่นกัน"
            },
            {
              "icon": "",
              "label": "มันขึ้นอยู่กับว่าการผูกพันอยู่ตำแหน่งแรกหรือตำแหน่งสุดท้ายหรือไม่"
            },
            {
              "icon": "",
              "label": "ไม่ — ROW_NUMBER() มักจะกำหนดจำนวนเต็มที่เพิ่มขึ้นแบบเคร่งครัดให้กับทุกแถว แม้เมื่อค่าผูกพัน"
            }
          ]
        },
        {
          "question": "WITH high_earners AS (SELECT * FROM employees WHERE salary > 100000) SELECT department, COUNT(*) FROM high_earners GROUP BY department high_earners คืออะไร",
          "options": [
            {
              "icon": "",
              "label": "A common table expression (CTE) — ชื่อชั่วคราว result set ที่ rest ของแบบสอบถาม (query) สามารถอ้างถึงเหมือนตาราง"
            },
            {
              "icon": "",
              "label": "ตารางถาวรที่สร้างขึ้นในฐานข้อมูล"
            },
            {
              "icon": "",
              "label": "view ที่คงอยู่หลังจากแบบสอบถาม (query) จบสิ้น"
            },
            {
              "icon": "",
              "label": "stored procedure ที่ต้องเรียกแยกต่างหาก"
            }
          ]
        },
        {
          "question": "คอลัมน์ถูกประกาศ PRIMARY KEY คุณสามารถแทรกแถวที่คอลัมน์นั้นเป็น NULL",
          "options": [
            {
              "icon": "",
              "label": "ใช่ — PRIMARY KEY มักจะบังคับใจเพียงเอกลักษณ์ ไม่ใช่ NULL-ness"
            },
            {
              "icon": "",
              "label": "ใช่ แต่เฉพาะแถว NULL ตัวเดียวถูกอนุญาต เหมือนกับ UNIQUE constraint"
            },
            {
              "icon": "",
              "label": "ไม่ — คอลัมน์ PRIMARY KEY เป็น implicitly NOT NULL ดังนั้นการแทรก NULL เข้าไปจะถูกปฏิเสธ"
            },
            {
              "icon": "",
              "label": "มันขึ้นอยู่กับว่าคอลัมน์มี default value หรือไม่"
            }
          ]
        },
        {
          "question": "ภายในธุรกรรมเปิดที่ คุณรัน UPDATE แต่ยังไม่ได้รัน COMMIT จากการเชื่อมต่อที่สอง แยกกัน ไปยังฐานข้อมูล การอัปเดตนั้นเห็นได้",
          "options": [
            {
              "icon": "",
              "label": "ใช่ — การเชื่อมต่อทั้งหมดเห็นทุกการเขียนทันที"
            },
            {
              "icon": "",
              "label": "ใช่ แต่เฉพาะเมื่อการเชื่อมต่อที่สองเปิด transaction เช่นกัน"
            },
            {
              "icon": "",
              "label": "มันขึ้นอยู่กับตารางเท่านั้นที่ได้รับการอัปเดต"
            },
            {
              "icon": "",
              "label": "ไม่ — การเปลี่ยนแปลงที่ยังไม่ได้ commit เห็นได้เฉพาะภายในธุรกรรมที่ทำให้มัน จนกว่า COMMIT จะทำให้ยั่งยืนและเห็นได้กับคนอื่น"
            }
          ]
        },
        {
          "question": "products.category_id มี FOREIGN KEY constraint อ้างอิง categories.id คุณพยายาม DELETE แถวจาก categories ที่ยังมี products ชี้ไปที่มัน ไม่มี ON DELETE rule ที่ระบุ จะเกิดอะไรขึ้น",
          "options": [
            {
              "icon": "",
              "label": "แถว category จะถูก delete และค่า products.category_id ที่ตรงกันจะถูกตั้งเป็น NULL อัตโนมัติ"
            },
            {
              "icon": "",
              "label": "แถว category จะถูก delete และทุกผลิตภัณฑ์ที่อ้างอิงไปที่มันจะถูก delete เช่นกัน"
            },
            {
              "icon": "",
              "label": "DELETE ถูกปฏิเสธ — พฤติกรรมคีย์ต่างประเทศเริ่มต้นจะบล็อก delete row ที่อ้างอิงขณะแถวอ้างอิงยังชี้ไปที่มัน"
            },
            {
              "icon": "",
              "label": "DELETE ประสบความสำเร็จเงียบๆ ออกจาก products.category_id ชี้ไปที่ category ที่ไม่มีอยู่"
            }
          ]
        }
      ],
      "optionOrderVersion": "e0466e547cfd8a45"
    }
  }
}
