0
0
Lập trình
Admin Team
Admin Teamtechmely

Hiểu sự khác biệt giữa `WHERE` và `HAVING` trong SQL

Đăng vào 5 tháng trước

• 4 phút đọc

Giới thiệu

Khi chuẩn bị cho các buổi phỏng vấn SQL, một trong những câu hỏi phổ biến nhất xoay quanh sự khác biệt giữa WHEREHAVING. Mặc dù chúng có vẻ tương tự ban đầu, nhưng chúng phục vụ các mục đích khác nhau trong việc lọc dữ liệu. Việc hiểu khi nào và tại sao sử dụng mỗi câu lệnh là điều quan trọng để viết các truy vấn SQL chính xác và hiệu quả.

Sự khác biệt cơ bản

  • WHERE lọc các hàng đơn lẻ trước khi bất kỳ việc nhóm nào xảy ra. Bạn không thể sử dụng các hàm tổng hợp như SUM, AVG, hoặc COUNT trong mệnh đề WHERE.
  • HAVING lọc các nhóm hàng sau khi tổng hợp. Đây là nơi bạn sử dụng các hàm tổng hợp để lọc dữ liệu đã được nhóm.

Ví dụ hình ảnh

Hãy tưởng tượng dữ liệu của bạn như một chồng giấy:

  • WHERE = “Tôi chỉ muốn những giấy tờ mà nhân viên kiếm được hơn \$60k.” ✅ Lọc hàng đơn lẻ
  • GROUP BY = “Bây giờ tôi sẽ xếp chồng giấy theo phòng ban.”
  • HAVING = “Tôi chỉ muốn những chồng giấy mà tổng hoặc trung bình lương vượt quá \$60k.” ✅ Lọc nhóm

Thiết lập cơ sở dữ liệu ví dụ

Hãy làm việc với một ví dụ thực tế sử dụng cơ sở dữ liệu công ty đơn giản:

sql Copy
CREATE DATABASE CompanyDB;
GO

USE CompanyDB;
GO

CREATE TABLE Employees (
    EmployeeId INT IDENTITY(1,1) PRIMARY KEY,
    FirstName NVARCHAR(50) NOT NULL,
    LastName NVARCHAR(50) NOT NULL,
    Email NVARCHAR(100) UNIQUE NOT NULL,
    HireDate DATE NOT NULL,
    DepartmentId INT,
    Salary DECIMAL(10,2) NOT NULL
);
GO

CREATE TABLE Departments (
    DepartmentId INT IDENTITY(1,1) PRIMARY KEY,
    DepartmentName NVARCHAR(50) NOT NULL,
    ManagerId INT NULL
);
GO

CREATE TABLE Projects (
    ProjectId INT IDENTITY(1,1) PRIMARY KEY,
    ProjectName NVARCHAR(100) NOT NULL,
    StartDate DATE NOT NULL,
    EndDate DATE NULL,
    Budget DECIMAL(12,2) NOT NULL
);
GO

CREATE TABLE EmployeeProjects (
    EmployeeId INT NOT NULL,
    ProjectId INT NOT NULL,
    AssignedDate DATE NOT NULL,
    Role NVARCHAR(50) NULL,
    PRIMARY KEY (EmployeeId, ProjectId),
    FOREIGN KEY (EmployeeId) REFERENCES Employees(EmployeeId),
    FOREIGN KEY (ProjectId) REFERENCES Projects(ProjectId)
);
GO

CREATE TABLE Cities (
    CityId INT IDENTITY(1,1) PRIMARY KEY,
    CityName NVARCHAR(100) NOT NULL,
    Country NVARCHAR(100) NOT NULL
);
GO

ALTER TABLE Employees
ADD CityId INT NULL,
FOREIGN KEY (CityId) REFERENCES Cities(CityId);
GO

INSERT INTO Departments (DepartmentName) VALUES
('Engineering'), ('HR'), ('Marketing'), ('Finance'), ('Operations');

INSERT INTO Cities (CityName, Country) VALUES
('New York', 'USA'), ('London', 'UK'), ('Berlin', 'Germany'), ('Tokyo', 'Japan'), ('Madrid', 'Spain');

INSERT INTO Employees (FirstName, LastName, Email, HireDate, DepartmentId, Salary, CityId) VALUES
('Alice','Smith','alice.smith@example.com','2020-03-15',1,70000,1),
('Bob','Johnson','bob.johnson@example.com','2019-07-22',2,50000,2),
('Charlie','Brown','charlie.brown@example.com','2021-01-10',1,80000,3),
('Diana','King','diana.king@example.com','2018-11-05',3,60000,4),
('Ethan','White','ethan.white@example.com','2022-06-20',4,55000,5);

INSERT INTO Projects (ProjectName, StartDate, EndDate, Budget) VALUES
('Project Apollo','2023-01-01','2023-12-31',500000),
('Project Zeus','2022-05-01','2023-04-30',300000),
('Project Hera','2023-03-01',NULL,200000),
('Project Poseidon','2023-06-15',NULL,150000);

INSERT INTO EmployeeProjects (EmployeeId, ProjectId, AssignedDate, Role) VALUES
(1,1,'2023-01-05','Lead Developer'),
(1,2,'2022-05-10','Developer'),
(2,2,'2022-05-12','HR Support'),
(3,3,'2023-03-10','Developer'),
(4,4,'2023-06-20','Marketing Lead'),
(5,1,'2023-01-15','Finance Analyst');
GO

Lọc hàng với WHERE

Nếu bạn muốn tìm nhân viên có lương hơn \$60k:

sql Copy
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Salary > 60000;

Giải thích:

  • Lọc các hàng đơn lẻ trước khi bất kỳ việc nhóm nào xảy ra.
  • Kết quả: Alice (70k) và Charlie (80k), giả sử dữ liệu mẫu của chúng ta.

Nhóm dữ liệu với HAVING

Giả sử bạn muốn tìm các phòng ban với hơn một nhân viên có lương trên \$60k:

sql Copy
SELECT DepartmentId, COUNT(EmployeeId) AS NumEmployees
FROM Employees
WHERE Salary > 60000
GROUP BY DepartmentId
HAVING COUNT(EmployeeId) > 1;

Giải thích:

  • WHERE Salary > 60000 → lọc những nhân viên đơn lẻ.
  • GROUP BY DepartmentId → nhóm nhân viên theo phòng ban.
  • HAVING COUNT(EmployeeId) > 1 → chỉ lọc những phòng ban có hơn 1 nhân viên thỏa mãn điều kiện.

Mẹo: Nếu truy vấn này không trả về hàng nào, có thể đơn giản là không có phòng ban nào có hơn 1 nhân viên trên \$60k.


Ví dụ tổng hợp với HAVING

Tìm các phòng ban mà trung bình lương vượt quá \$60k:

sql Copy
SELECT DepartmentId, AVG(Salary) AS AvgSalary
FROM Employees
GROUP BY DepartmentId
HAVING AVG(Salary) > 60000;

Điểm chính:

  • HAVING là cần thiết vì bộ lọc phụ thuộc vào AVG(Salary) (một hàm tổng hợp).
  • Sử dụng WHERE AVG(Salary) > 60000 sẽ thất bại vì việc tổng hợp chưa xảy ra.

Bài tập thực hành: Tổng lương theo phòng ban

Tìm các phòng ban mà tổng lương vượt quá \$200k và hiển thị cả tổng lương và số lượng nhân viên:

sql Copy
SELECT DepartmentId, SUM(Salary) AS TotalSalary, COUNT(EmployeeId) AS NumEmployees
FROM Employees
GROUP BY DepartmentId
HAVING SUM(Salary) > 200000;

Tại sao sử dụng HAVING:

  • Bộ lọc phụ thuộc vào SUM(Salary), được tính toán sau khi nhóm.
  • WHERE không thể được sử dụng ở đây vì nó chỉ nhìn thấy các hàng đơn lẻ.

Kiểm tra nhanh cho các buổi phỏng vấn

  1. Tôi đang lọc hàng đơn lẻ hay nhóm?
  • Hàng → WHERE
  • Nhóm → HAVING
  1. Tôi có đang sử dụng các hàm tổng hợp không?
  • Có → có thể là HAVING
  • Không → WHERE
  1. Luồng của SQL:
    1. Bắt đầu với tất cả các hàng
    2. Áp dụng WHERE → lọc hàng
    3. GROUP BY → tạo nhóm
    4. Áp dụng HAVING → lọc nhóm

Hiểu WHEREHAVING không chỉ là về cú pháp. Nó còn về biết khi nào mỗi bộ lọc áp dụng trong thứ tự xử lý SQL. Sử dụng những khái niệm này một cách chính xác sẽ giúp bạn giải quyết các truy vấn thực tế và gây ấn tượng trong các buổi phỏng vấn kỹ thuật.

Cảm ơn bạn đã đọc bài viết này, và hãy nhớ tham gia GoConnect.dev nơi bạn có thể học hỏi thêm và kết nối với các nhà phát triển khác.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào