2026-08-08 · Power BI & Dashboard
Sếp hỏi một câu rất bình thường: "tháng này so với cùng kỳ năm ngoái thế nào, lũy kế từ đầu năm được bao nhiêu rồi?". Bạn mở Power BI ra và phát hiện mình không có sẵn hai con số đó, dù dữ liệu bán hàng thì có đủ từ mấy năm nay.
Nhóm hàm giải quyết đúng câu hỏi này trong DAX gọi là time intelligence. Nó tồn tại vì so sánh theo thời gian là việc lặp đi lặp lại ở mọi báo cáo quản trị, và tự viết tay bằng bộ lọc ngày thì vừa dài vừa dễ sai.
Điều ít người nói rõ: các hàm này chỉ chạy đúng khi mô hình được dựng đúng trước đó. Rất nhiều trường hợp "công thức không sai mà số vẫn lệch" đều quay về cùng một nguyên nhân ở phần chuẩn bị. Bài này đội ngũ EzTech đi theo đúng thứ tự đó — chuẩn bị trước, công thức sau, rồi tới ba cái bẫy khiến báo cáo nhìn thì đẹp mà số thì sai.
Điều kiện bắt buộc: một bảng lịch riêng
Đây là phần bị bỏ qua nhiều nhất, và cũng là nguyên nhân của phần lớn kết quả sai.
Time intelligence hoạt động bằng cách mở rộng hoặc dịch chuyển tập ngày đang được lọc. Muốn dịch về cùng kỳ năm trước, nó cần biết năm trước có những ngày nào. Nếu bạn dùng thẳng cột ngày trên bảng bán hàng, những ngày không phát sinh giao dịch đơn giản là không tồn tại — và phép dịch chuyển sẽ hụt đúng những ngày đó.
Vì vậy mô hình cần một bảng lịch riêng, thỏa ba điều kiện:
- Liên tục, không thiếu ngày nào trong toàn bộ khoảng thời gian có dữ liệu — kể cả ngày nghỉ, ngày không bán được hàng.
- Phủ trọn năm: bắt đầu từ 1/1 của năm sớm nhất, kết thúc ở 31/12 của năm muộn nhất. Bảng lịch dừng giữa năm sẽ làm hỏng các phép tính lũy kế.
- Được nối quan hệ với bảng giao dịch qua cột ngày, và được đánh dấu bằng Mark as date table trong Power BI Desktop.
Cách nhanh nhất để tạo là dùng một bảng mới với công thức CALENDAR(DATE(2022,1,1), DATE(2026,12,31)), rồi bổ sung các cột Năm, Quý, Tháng, Số tháng. Lưu ý các cột này là thuộc tính của từng dòng ngày nên phải là calculated column chứ không phải measure — nếu bạn chưa chắc về ranh giới đó, bài Measure và Calculated Column giải thích kỹ phần này.
Một chi tiết nhỏ nhưng hay bị quên: luôn dùng cột ngày của bảng lịch làm trục biểu đồ và làm slicer, đừng dùng cột ngày của bảng bán hàng. Dùng nhầm thì các tháng không phát sinh doanh thu sẽ biến mất khỏi biểu đồ thay vì hiện giá trị bằng không.
Lũy kế từ đầu năm (YTD)
Giả sử bạn đã có measure Doanh thu = SUM('Bán hàng'[ThanhTien]). Lũy kế từ đầu năm viết như sau:
Doanh thu YTD = TOTALYTD([Doanh thu], 'Lịch'[Ngày])
Hàm này lấy ngày muộn nhất trong ngữ cảnh hiện tại, quay ngược về 1/1 cùng năm, rồi tính measure trên toàn bộ khoảng đó. Đặt trên một bảng theo tháng, bạn sẽ thấy con số cộng dồn qua từng tháng.
Nếu doanh nghiệp dùng năm tài chính không trùng năm dương lịch, thêm tham số ngày kết thúc năm vào cuối. Ví dụ năm tài chính kết thúc 31/3:
Doanh thu YTD = TOTALYTD([Doanh thu], 'Lịch'[Ngày], "31/3")
Đây là chỗ đáng kiểm tra kỹ với kế toán trước khi phát hành báo cáo, vì hai cách hiểu cho ra hai con số khác hẳn nhau mà không có cảnh báo nào.
So với cùng kỳ năm trước
Có hai cách viết, chọn cách nào cũng được nhưng nên biết khác biệt.
Cách quen thuộc nhất:
Doanh thu cùng kỳ = CALCULATE([Doanh thu], SAMEPERIODLASTYEAR('Lịch'[Ngày]))
Cách linh hoạt hơn, đổi được cả đơn vị thời gian:
Doanh thu cùng kỳ = CALCULATE([Doanh thu], DATEADD('Lịch'[Ngày], -1, YEAR))
DATEADD cho phép đổi YEAR thành QUARTER, MONTH hay DAY, nên chỉ cần nhớ một hàm là dựng được cả bộ so sánh: so với tháng trước, quý trước, năm trước. SAMEPERIODLASTYEAR thì chỉ làm được đúng một việc, bù lại tên hàm đọc là hiểu ngay.
Có hai con số rồi thì phần tăng trưởng chỉ còn là một phép chia — và luôn dùng DIVIDE thay vì dấu chia, để kỳ không có dữ liệu trả về trống thay vì báo lỗi:
Tăng trưởng = DIVIDE([Doanh thu] - [Doanh thu cùng kỳ], [Doanh thu cùng kỳ])
Ba measure này là bộ khung dùng lại được cho gần như mọi chỉ tiêu. Doanh thu, sản lượng, chi phí, số khách hàng mới — chỉ cần thay measure gốc ở trong, phần vỏ giữ nguyên. Đó cũng là cách các chỉ tiêu so sánh trong báo cáo phân tích tài chính được dựng.
Ba cái bẫy khiến số nhìn đúng mà vẫn sai
1. Đường lũy kế kéo ngang qua các tháng chưa tới
Bạn dựng biểu đồ YTD theo 12 tháng, dữ liệu mới có tới tháng 8. Nhưng biểu đồ vẫn vẽ tiếp một đường thẳng nằm ngang từ tháng 9 đến tháng 12, giữ nguyên giá trị của tháng 8.
Không phải lỗi công thức. Bảng lịch có đủ 12 tháng, nên tháng 12 vẫn là một ngữ cảnh hợp lệ, và lũy kế từ đầu năm tới tháng 12 đúng bằng lũy kế tới tháng 8 vì sau đó không có giao dịch nào. Vấn đề là người xem sẽ đọc đường ngang đó thành "doanh thu đứng im bốn tháng".
Cách xử lý là chặn measure lại khi ngữ cảnh đã vượt quá ngày cuối cùng có dữ liệu:
Doanh thu YTD = IF(MIN('Lịch'[Ngày]) <= MAX('Bán hàng'[Ngày]), TOTALYTD([Doanh thu], 'Lịch'[Ngày]))
2. So cả tháng năm nay với cả tháng năm ngoái khi tháng chưa hết
Hôm nay là ngày 8, bạn so doanh thu tháng này với cùng kỳ năm trước và thấy giảm hơn 70%. Thực ra bạn đang so 8 ngày với 30 ngày.
Bẫy này đặc biệt hay gặp trên các thẻ số tổng ở đầu báo cáo, nơi không hiện rõ khoảng thời gian. Có hai hướng xử lý: hoặc ghi rõ nhãn "tính đến ngày 8" ngay cạnh con số, hoặc cắt kỳ so sánh cho bằng số ngày bằng DATESINPERIOD tính lùi đúng số ngày đã trôi qua. Chọn hướng nào cũng được, miễn là người xem biết mình đang so cái gì với cái gì.
3. Bảng lịch chưa được đánh dấu hoặc chưa nối quan hệ
Biểu hiện điển hình: measure cùng kỳ trả về trống toàn bộ, hoặc trả về đúng bằng measure gốc ở mọi dòng. Trước khi sửa công thức, hãy kiểm tra theo đúng thứ tự này — kiểm tra mô hình luôn nhanh hơn đọc lại DAX:
- Bảng lịch đã Mark as date table chưa?
- Quan hệ giữa bảng lịch và bảng giao dịch đã có chưa, và có đang ở trạng thái hoạt động không?
- Bảng lịch có thiếu ngày nào không, có phủ trọn từ 1/1 tới 31/12 không?
- Trục biểu đồ và slicer đang lấy cột ngày của bảng lịch hay của bảng bán hàng?
Câu hỏi thường gặp
Bảng lịch nên tạo bằng DAX hay bằng Power Query?
Cả hai đều chạy được. Tạo bằng Power Query gọn hơn về dung lượng và dùng lại được cho nhiều mô hình khác nhau, nên nếu bạn đã quen tầng này thì ưu tiên. Tạo bằng DAX với CALENDAR thì nhanh và không cần rời khỏi Power BI Desktop, phù hợp lúc mới bắt đầu.
Vì sao measure cùng kỳ của tôi trả về trống hết?
Phần lớn là do bảng lịch chưa phủ hết năm trước. Nếu dữ liệu bắt đầu từ tháng 6/2024 và bảng lịch cũng bắt đầu từ đó, thì kỳ so sánh của tháng 6/2024 rơi vào tháng 6/2023 — một khoảng không tồn tại trong bảng lịch. Hãy kéo bảng lịch lùi về 1/1 của năm sớm nhất.
Năm tài chính lệch năm dương lịch thì xử lý ra sao?
Với các hàm lũy kế, truyền thêm tham số ngày kết thúc năm như ví dụ ở trên. Ngoài ra nên thêm hẳn cột "Năm tài chính" và "Tháng tài chính" vào bảng lịch để dùng làm trục và slicer, thay vì mỗi lần lại xử lý trong công thức.
Tôi cần so với tháng trước chứ không phải năm trước, dùng hàm gì?
Dùng DATEADD('Lịch'[Ngày], -1, MONTH) trong CALCULATE. Cấu trúc measure giữ nguyên như phần trên, chỉ đổi đúng đơn vị thời gian.
Dựng một lần, dùng cho mọi chỉ tiêu
Điểm đáng giá nhất của nhóm hàm này là bạn chỉ phải làm phần khó đúng một lần. Bảng lịch dựng tử tế, ba measure khung viết xong, sau đó mỗi chỉ tiêu mới chỉ tốn vài phút để có đủ bộ lũy kế, cùng kỳ và tăng trưởng.
Muốn thấy bộ khung này chạy trên báo cáo thật, bạn có thể mở các báo cáo phân tích bán hàng hoặc báo cáo quản trị lãi lỗ trong thư viện của EzTech — tất cả đều xem demo trực tiếp được, không cần cài đặt gì. Còn nếu bạn muốn học từ mô hình dữ liệu, DAX cho tới lúc dựng ra một hệ báo cáo quản trị hoàn chỉnh, khóa DATA BUSINESS đi đúng lộ trình đó. Chưa rõ nên bắt đầu từ đâu thì bạn cứ liên hệ với đội ngũ EzTech để được tư vấn.