0

Phân biệt whereDate() và where() trong Laravel Query Builder với MySQL

Khi làm việc với các cột kiểu dữ liệu thời gian (TIMESTAMP hoặc DATETIME) trong Laravel, việc lọc dữ liệu theo ngày tháng là một nhu cầu cực kỳ phổ biến. Tuy nhiên, rất nhiều lập trình viên thường vô tình dùng nhầm giữa hai phương thức where()whereDate(), dẫn đến những lỗi khó hiểu không tìm ra kết quả.

Dưới đây là bức tranh chi tiết về sự khác biệt cốt lõi giữa hai phương thức này.


1. Bản chất & Cách hoạt động

whereDate()

  • Cách hoạt động: Phương thức này sẽ chủ động ép kiểu cột dữ liệu trong cơ sở dữ liệu về dạng Ngày (chỉ lấy phần YYYY-MM-DD, tự động bỏ qua phần Giờ:Phút:Giây) rồi mới tiến hành so sánh với giá trị truyền vào.
  • Câu lệnh SQL tương đương (MySQL):
    SELECT * FROM posts WHERE DATE(created_at) = '2024-12-18';
    

where()

  • Cách hoạt động: So sánh trực tiếp giá trị của cột với giá trị bạn truyền vào dựa trên cơ chế ép kiểu tự động của MySQL.
  • Hệ quả: Nếu cột created_at có kiểu DATETIME hoặc TIMESTAMP (ví dụ lưu giá trị 2024-12-18 14:30:00) mà bạn dùng where('created_at', '2024-12-18'), MySQL sẽ ngầm hiểu hoặc ép giá trị bên phải về 2024-12-18 00:00:00. Kết quả là nó sẽ đi tìm chính xác các bản ghi được tạo đúng vào lúc 0h00 phút 00 giây của ngày hôm đó, khiến câu lệnh trả về mảng rỗng ([]).

2. Trường hợp thực tế: Lấy toàn bộ bài viết trong "ngày hôm nay"

Giả sử bạn cần viết một câu lệnh để lấy tất cả các bài viết được tạo ra trong ngày hôm nay, bất kể là lúc sáng sớm hay nửa đêm.

❌ Trường hợp KHÔNG NÊN DÙNG: where()

use App\Models\Post;
use Carbon\Carbon;

// Kết quả thường sẽ là mảng rỗng []
$posts = Post::where('created_at', Carbon::today())->get();
  • Lý do lỗi: Carbon::today() khi ép sang dạng chuỗi sẽ trả về 2024-12-18 00:00:00. Khi kết hợp với hàm where(), câu lệnh SQL sinh ra sẽ là WHERE created_at = '2024-12-18 00:00:00', hoàn toàn bỏ lỡ các bài viết tạo vào buổi chiều hay buổi tối.

✅ Trường hợp NÊN DÙNG: whereDate()

use App\Models\Post;
use Carbon\Carbon;

// Lấy toàn bộ bài viết trong ngày hôm nay một cách chuẩn xác
$posts = Post::whereDate('created_at', Carbon::today())->get();
  • Lý do chính xác: Laravel sẽ tự động cắt phần giờ giấc của cột created_at đi để so sánh thuần túy phần ngày tháng (YYYY-MM-DD), giúp bạn gom trọn vẹn tất cả bài viết từ 00:00:00 đến 23:59:59 trong ngày.

3. Lưu ý quan trọng về Hiệu năng (Performance) khi dùng whereDate()

Mặc dù whereDate() mang lại sự tiện lợi và chính xác về mặt logic nghiệp vụ, bạn cần lưu ý một điểm về hiệu năng cơ sở dữ liệu:

  • Khi bạn dùng hàm DATE(created_at) trong MySQL, MySQL sẽ không thể sử dụng Index (chỉ mục) đã tạo trên cột created_at đó nữa (hiện tượng này gọi là Index Scans disabled).
  • Nếu bảng posts của bạn có hàng triệu bản ghi, câu lệnh sử dụng whereDate() sẽ khiến MySQL phải quét toàn bộ bảng (Full Table Scan), làm giảm hiệu năng hệ thống.

💡 Giải pháp tối ưu cho bảng dữ liệu lớn:

Thay vì dùng whereDate(), nếu muốn tận dụng Index của cơ sở dữ liệu để query siêu nhanh, bạn nên dùng khoảng thời gian (whereBetween):

$posts = Post::whereBetween('created_at', [
    Carbon::today()->startOfDay(), // 2024-12-18 00:00:00
    Carbon::today()->endOfDay()    // 2024-12-18 23:59:59
])->get();

Cách viết này vừa đảm bảo lấy chính xác dữ liệu trong ngày, vừa giúp MySQL tận dụng được Index trên cột created_at.


Tổng kết

  • Dùng where() khi bạn muốn so sánh chính xác tuyệt đối cả thời gian (ngày, giờ, phút, giây).
  • Dùng whereDate() khi bạn chỉ muốn so sánh phần Ngày tháng năm, cực kỳ phù hợp cho các bài toán lọc theo ngày, tháng, năm sinh hoặc lấy dữ liệu trong ngày.
  • Đối với các bảng dữ liệu cực lớn, hãy ưu tiên dùng whereBetween() kết hợp với khoảng thời gian đầu ngày và cuối ngày để tối ưu hiệu năng Database.

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.