Đây là nội dung Markdown đầy đủ, được gộp lại từ hai phần logic của bạn: **Dashboard tổng quát (theo lượt giao bài)** và **Báo cáo chi tiết theo Học viên (Unique Students)** cho giai đoạn tháng 3.

---

# SQL Query – Báo cáo Dashboard BTVN / BKT & Tỉ lệ Học viên Hoàn thành

Tài liệu này tổng hợp các truy vấn SQL phục vụ báo cáo Dashboard, bao gồm tiến độ hoàn thành Section (BTVN type 2, BKT type 3), điểm số trung bình và thống kê tỉ lệ hoàn thành dựa trên số lượng học viên duy nhất.

## 1. Dashboard Tổng quát (Theo lượt giao bài)

Truy vấn này thống kê dựa trên **lượt giao bài (Assignments)** để theo dõi hiệu suất và khối lượng học tập tổng thể trên hệ thống.

### Logic tóm tắt
1.  **QuizCountPerSection**: Đếm số quiz visible trong mỗi section.
2.  **MaxScores**: Điểm cao nhất mỗi quiz cho từng học viên (chỉ quiz visible).
3.  **SectionScores**: Trung bình điểm section khi học viên có đủ dữ liệu cho tất cả quiz của section.
4.  **AssignmentsDetail**: Lượt giao bài theo section, hoàn thành khi tất cả assignment của section có `usrasi_completion_state = 1`.

### Truy vấn SQL
```sql
WITH QuizCountPerSection AS (
    -- Bước 1: Đếm số lượng Quiz visible của mỗi Section
    SELECT cou_parent_id AS section_id, COUNT(*) AS total_quizzes
    FROM lcms_courses
    WHERE cou_type = 'quiz' AND cou_visible = 1
    GROUP BY cou_parent_id
),
MaxScores AS (
    -- Bước 2: Lấy điểm cao nhất của từng Quiz cho từng học viên
    SELECT
        ss.stusco_student_id AS student_id,
        c.cou_id AS quiz_id,
        c.cou_parent_id AS section_id,
        MAX(CAST(ss.stusco_overall_score AS DECIMAL(10,2))) AS highest_score
    FROM lcms_student_scores ss
    JOIN lcms_courses c ON ss.stusco_course_id = c.cou_id
    WHERE c.cou_type = 'quiz' AND c.cou_visible = 1
    GROUP BY ss.stusco_student_id, c.cou_id, c.cou_parent_id
    HAVING highest_score >= 0
),
SectionScores AS (
    -- Bước 3: Tính điểm trung bình Section
    SELECT
        ms.student_id,
        ms.section_id,
        AVG(ms.highest_score) AS avg_score
    FROM MaxScores ms
    JOIN QuizCountPerSection qc ON ms.section_id = qc.section_id
    GROUP BY ms.student_id, ms.section_id, qc.total_quizzes
    HAVING COUNT(ms.quiz_id) >= qc.total_quizzes
),
AssignmentsDetail AS (
    -- Bước 4: Lấy danh sách lượt giao bài
    SELECT
        ua.usrasi_student_id AS student_id,
        ua.usrasi_course_id AS course_id,
        ua.usrasi_section_id AS section_id,
        c.cou_section_type AS section_type,
        CASE WHEN MIN(ua.usrasi_completion_state) = 1 THEN 1 ELSE 0 END AS is_section_completed
    FROM lcms_user_assignments ua
    JOIN lcms_courses c ON ua.usrasi_section_id = c.cou_id
    WHERE c.cou_visible = 1
      AND c.cou_section_type IN (2, 3)
      AND ua.usrasi_course_id IN (346, 563, 595, 1084)
    GROUP BY ua.usrasi_student_id, ua.usrasi_course_id, ua.usrasi_section_id, c.cou_section_type
)
-- Bước 5: Tổng hợp Dashboard (Đã fix lỗi ambiguous)
SELECT
    COUNT(DISTINCT ad.student_id) AS total_students,
    COUNT(DISTINCT ad.course_id) AS total_courses,

    -- BTVN (Type 2)
    SUM(CASE WHEN ad.section_type = 2 THEN 1 ELSE 0 END) AS btvn_total_assigned,
    SUM(CASE WHEN ad.section_type = 2 AND ad.is_section_completed = 1 THEN 1 ELSE 0 END) AS btvn_completed,
    SUM(CASE WHEN ad.section_type = 2 AND ad.is_section_completed = 0 THEN 1 ELSE 0 END) AS btvn_not_completed,
    ROUND((SUM(CASE WHEN ad.section_type = 2 AND ad.is_section_completed = 1 THEN 1 ELSE 0 END) / NULLIF(SUM(CASE WHEN ad.section_type = 2 THEN 1 ELSE 0 END), 0)) * 100, 2) AS btvn_completion_ratio,
    ROUND(AVG(CASE WHEN ad.section_type = 2 THEN ss.avg_score ELSE NULL END), 2) AS btvn_avg_score,

    -- BKT (Type 3)
    SUM(CASE WHEN ad.section_type = 3 THEN 1 ELSE 0 END) AS bkt_total_assigned,
    SUM(CASE WHEN ad.section_type = 3 AND ad.is_section_completed = 1 THEN 1 ELSE 0 END) AS bkt_completed,
    SUM(CASE WHEN ad.section_type = 3 AND ad.is_section_completed = 0 THEN 1 ELSE 0 END) AS bkt_not_completed,
    ROUND((SUM(CASE WHEN ad.section_type = 3 AND ad.is_section_completed = 1 THEN 1 ELSE 0 END) / NULLIF(SUM(CASE WHEN ad.section_type = 3 THEN 1 ELSE 0 END), 0)) * 100, 2) AS bkt_completion_ratio,
    ROUND(AVG(CASE WHEN ad.section_type = 3 THEN ss.avg_score ELSE NULL END), 2) AS bkt_avg_score
FROM AssignmentsDetail ad
LEFT JOIN SectionScores ss ON ad.student_id = ss.student_id AND ad.section_id = ss.section_id;
```

---

## 2. Thống kê Học viên Hoàn thành (Giai đoạn 01/03 - 22/03)

Truy vấn này tập trung vào mục tiêu xác định tỷ lệ **Học viên duy nhất** hoàn thành bài tập về nhà trong một khoảng thời gian cụ thể.

### Mục tiêu báo cáo
* Lấy tổng số học viên duy nhất được giao ít nhất 1 BTVN.
* Đếm số học viên đã hoàn thành **tất cả** các bài được giao (100%).
* Tính tỷ lệ % hoàn thành trên đầu người.

### Truy vấn SQL
```sql
SELECT 
    /* 1. Tổng số học viên duy nhất được giao ít nhất 1 BTVN trong thời gian này */
    COUNT(DISTINCT sub.usrasi_student_id) AS "Tổng học viên được giao",
    
    /* 2. Số học viên đã hoàn thành TOÀN BỘ các BTVN được giao trong thời gian này */
    SUM(CASE WHEN sub.total_sections = sub.completed_sections THEN 1 ELSE 0 END) AS "Số học viên hoàn thành 100%",
    
    /* 3. Số học viên đã hoàn thành ít nhất 1 BTVN (Có tiến độ học tập) */
    SUM(CASE WHEN sub.completed_sections > 0 THEN 1 ELSE 0 END) AS "Số học viên đã làm bài",
    
    /* 4. Tỉ lệ % học viên hoàn thành so với tổng số học viên được giao */
    ROUND(
        (SUM(CASE WHEN sub.total_sections = sub.completed_sections THEN 1 ELSE 0 END) / 
         NULLIF(COUNT(DISTINCT sub.usrasi_student_id), 0)) * 100, 
        2
    ) AS "Tỉ lệ hoàn thành (%)"
FROM (
    /* Bước 1: Gom nhóm theo từng học viên để đếm số lượng bài tập */
    SELECT 
        ua.usrasi_student_id,
        COUNT(ua.usrasi_section_id) AS total_sections,
        SUM(CASE WHEN status.is_section_completed = 1 THEN 1 ELSE 0 END) AS completed_sections
    FROM lcms_user_assignments ua
    JOIN lcms_courses c ON ua.usrasi_section_id = c.cou_id
    LEFT JOIN (
        /* Sub-query xác định trạng thái hoàn thành thực tế của từng Section */
        SELECT usrasi_student_id, usrasi_section_id, 
                CASE WHEN MIN(usrasi_completion_state) = 1 THEN 1 ELSE 0 END AS is_section_completed
        FROM lcms_user_assignments
        GROUP BY usrasi_student_id, usrasi_section_id
    ) status ON ua.usrasi_student_id = status.usrasi_student_id 
            AND ua.usrasi_section_id = status.usrasi_section_id
    WHERE c.cou_section_type IN (2) 
      AND ua.usrasi_course_id IN (346, 563, 595, 1084)
      /* Lọc theo thời gian từ 01/03/2026 đến 22/03/2026 */
      AND ua.usrasi_created_at >= '2026-03-01 00:00:00'
      AND ua.usrasi_created_at <= '2026-03-22 23:59:59'
    GROUP BY ua.usrasi_student_id
) AS sub;
```

---

## 3. Ghi chú Kỹ thuật
* **Xác định hoàn thành**: Một Section được coi là hoàn thành khi giá trị nhỏ nhất của `usrasi_completion_state` bằng 1 (nghĩa là không còn bài nào ở trạng thái chưa xong).
* **NULLIF**: Được sử dụng để tránh lỗi chia cho 0 trong trường hợp không có dữ liệu học viên nào được giao bài trong thời gian lọc.
* **Filter thời gian**: Sử dụng trên cột `usrasi_created_at` để xác định các bài tập được phát sinh trong giai đoạn cần báo cáo.

---