Creating a user gives them the ability to connect to MySQL. But by default, a new user has zero permissions β they can't even see a SHOW DATABASES list. GRANT and REVOKE let you give users exactly the access they need β no more, no less.
GRANT: Gives a specific privilege (or set of privileges) to a user on a specific database, table, or column.REVOKE: Removes a previously granted privilege from a user.- DCL (Data Control Language): The category of SQL commands that controls who can do what in the database.
Privileges can be granted at 4 different scopes, from broadest to most specific:
| Level | Syntax | What it Controls |
|---|---|---|
| Global | ON *.* |
All databases and all tables on the server. Use only for DBAs. |
| Database | ON mydb.* |
All tables within one specific database. |
| Table | ON mydb.users |
One specific table in one specific database. |
| Column | ON mydb.users (Name, Email) |
Specific columns within a table (most granular). |
| Privilege | What it Allows |
|---|---|
SELECT |
Reading / querying data |
INSERT |
Adding new rows |
UPDATE |
Modifying existing rows |
DELETE |
Removing rows |
CREATE |
Creating new tables/databases |
DROP |
Deleting tables/databases |
INDEX |
Creating and dropping indexes |
ALTER |
Modifying table structure |
EXECUTE |
Running stored procedures and functions |
GRANT OPTION |
Passing privileges to other users |
ALL PRIVILEGES |
Every privilege (root-level access) |
-- Grant specific privileges on a specific table
GRANT SELECT, INSERT, UPDATE ON company_db.Customers TO 'api_user'@'localhost';
-- Grant read-only access to an entire database
GRANT SELECT ON company_db.* TO 'reporter'@'%';
-- '%' means the user can connect from ANY host
-- Grant all privileges on one database (app admin level)
GRANT ALL PRIVILEGES ON company_db.* TO 'app_admin'@'localhost';
-- Grant column-level access (hide sensitive data)
GRANT SELECT (Employee_ID, First_Name, Department) ON company_db.Employees TO 'hr_viewer'@'localhost';
-- hr_viewer can see names but NOT salary or personal details!
-- Allow a user to grant their permissions to others (rare, use carefully!)
GRANT SELECT ON company_db.* TO 'team_lead'@'localhost' WITH GRANT OPTION;
-- Revoke a specific permission
REVOKE DELETE ON company_db.* FROM 'api_user'@'localhost';
-- Revoke all permissions from a user
REVOKE ALL PRIVILEGES ON *.* FROM 'old_employee'@'localhost';
-- Check what privileges a user currently has
SHOW GRANTS FOR 'api_user'@'localhost';
-- Apply changes immediately (usually auto-applied, but good practice)
FLUSH PRIVILEGES;MySQL stores all privilege information in special system tables inside the mysql database:
mysql.userβ global-level grantsmysql.dbβ database-level grantsmysql.tables_privβ table-level grantsmysql.columns_privβ column-level grants
When a user tries to execute a query, MySQL checks these tables in order (global β database β table β column). The first matching grant at any level determines access.
This is why FLUSH PRIVILEGES is sometimes needed after direct table edits β it reloads these tables into memory. When using GRANT/REVOKE, MySQL auto-flushes, so it's rarely needed.
Example 1 β The Three-Role Pattern (Most Production Systems):
-- Role 1: Read-only reporter (data analytics, dashboards)
GRANT SELECT ON analytics_db.* TO 'dashboard_user'@'%';
-- Role 2: Application API user (CRUD but no structure changes)
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'api_user'@'app_server_ip';
-- Role 3: DBA (everything, but NOT on production during business hours!)
GRANT ALL PRIVILEGES ON *.* TO 'dba_user'@'localhost' WITH GRANT OPTION;Example 2 β Hiding Salary Data from HR Staff:
-- HR can see employee name, department, start date β but NOT salary
GRANT SELECT (Emp_ID, First_Name, Last_Name, Department, Start_Date)
ON company_db.Employees
TO 'hr_staff'@'localhost';
-- HR can still UPDATE only the department field
GRANT UPDATE (Department) ON company_db.Employees TO 'hr_staff'@'localhost';Always give users the minimum permissions needed to do their job.
| Role | Appropriate Privileges |
|---|---|
| Frontend App | SELECT, INSERT, UPDATE (never DROP/CREATE/DELETE) |
| Reporting Tool | SELECT only |
| Backend API | SELECT, INSERT, UPDATE, DELETE (on specific tables only) |
| DBA / Admin | ALL PRIVILEGES (limited to internal network only) |
| Read-only Analyst | SELECT only |
Never use the root account in application code. If your application's connection credentials are leaked, the attacker should have the minimum possible access.
- Granting
ALL PRIVILEGES ON *.*to application users: If the app is compromised, the attacker becomes a database admin. Grant only what the app needs. - Using
root@%(root accessible from any host): This means root is accessible from the internet. Always restrict sensitive users tolocalhostor a specific IP. - Forgetting to REVOKE when employees leave: Create an offboarding checklist that includes
REVOKE ALL PRIVILEGESandDROP USERfor departing team members.
- Task 1: Create a user
reporting_botthat can connect from anywhere (%) and can onlySELECTfrom all tables in thesales_dbdatabase. - Task 2: An employee named Priya has left the company. she was user
priya@localhost. Write the commands to: (a) revoke all her privileges, and (b) drop her user account. - Task 3: Why is column-level
GRANTuseful in a healthcare database? Give a specific scenario with patient data.