The SELECT statement is the most frequently used SQL command. It's how you ask questions to your database and retrieve answers in the form of data.
-- Minimum required components:
SELECT column1, column2, ...
FROM table_name;
-- With all optional components:
SELECT [DISTINCT] column1 [AS alias1], column2 [AS alias2], ...
FROM table1 [AS table_alias]
[WHERE conditions]
[GROUP BY columns]
[HAVING group_conditions]
[ORDER BY columns [ASC|DESC]]
[LIMIT number];
-- Select one column
SELECT product_name FROM products;
-- Select multiple columns
SELECT product_name, price, category FROM products;
-- Select all columns (asterisk)
SELECT * FROM employees;
Let's create a sample database we'll use for all our examples:
-- Create a new database
CREATE DATABASE company_db;
USE company_db; -- Switch to using this database
-- Create employees table
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
department VARCHAR(50),
salary DECIMAL(10,2),
hire_date DATE,
is_active BOOLEAN DEFAULT true
);
-- Create departments table
CREATE TABLE departments (
department_id INT PRIMARY KEY,
department_name VARCHAR(50) NOT NULL,
manager_id INT,
location VARCHAR(100)
);
-- Create projects table
CREATE TABLE projects (
project_id INT PRIMARY KEY,
project_name VARCHAR(100) NOT NULL,
department_id INT,
start_date DATE,
end_date DATE,
budget DECIMAL(12,2)
);
-- Insert into employees table
INSERT INTO employees VALUES
(101, 'John', 'Smith', 'john.smith@company.com', 'Sales', 55000.00, '2021-03-15', true),
(102, 'Sarah', 'Johnson', 'sarah.j@company.com', 'Marketing', 62000.00, '2020-07-22', true),
(103, 'Michael', 'Chen', 'michael.chen@company.com', 'IT', 75000.00, '2019-11-30', true),
(104, 'Emily', 'Williams', 'emily.w@company.com', 'HR', 48000.00, '2022-01-10', true),
(105, 'David', 'Brown', 'david.brown@company.com', 'Sales', 51000.00, '2021-09-05', true),
(106, 'Lisa', 'Garcia', 'lisa.g@company.com', 'IT', 72000.00, '2020-05-18', true),
(107, 'Robert', 'Miller', 'robert.m@company.com', 'Marketing', 58000.00, '2023-02-28', false),
(108, 'Maria', 'Davis', 'maria.davis@company.com', 'Finance', 68000.00, '2018-08-12', true),
(109, 'James', 'Wilson', 'james.w@company.com', 'IT', 80000.00, '2017-06-25', true),
(110, 'Jennifer', 'Taylor', 'jennifer.t@company.com', 'Sales', 53000.00, '2022-11-14', true);
-- Insert into departments table
INSERT INTO departments VALUES
(1, 'Sales', 101, 'New York'),
(2, 'Marketing', 102, 'Chicago'),
(3, 'IT', 109, 'San Francisco'),
(4, 'HR', 104, 'Boston'),
(5, 'Finance', 108, 'New York');
-- Insert into projects table
INSERT INTO projects VALUES
(1001, 'Website Redesign', 3, '2024-01-15', '2024-06-30', 50000.00),
(1002, 'Product Launch', 2, '2024-02-01', '2024-05-31', 75000.00),
(1003, 'Sales Expansion', 1, '2024-03-10', '2024-12-31', 120000.00),
(1004, 'HR System Upgrade', 4, '2024-04-01', '2024-09-30', 30000.00),
(1005, 'Mobile App Development', 3, '2024-02-15', '2024-08-31', 90000.00);
-- Employees table sample data:
+-------------+------------+-----------+------------------------------+------------+----------+------------+-----------+
| employee_id | first_name | last_name | email | department | salary | hire_date | is_active |
+-------------+------------+-----------+------------------------------+------------+----------+------------+-----------+
| 101 | John | Smith | john.smith@company.com | Sales | 55000.00 | 2021-03-15 | true |
| 102 | Sarah | Johnson | sarah.j@company.com | Marketing | 62000.00 | 2020-07-22 | true |
| 103 | Michael | Chen | michael.chen@company.com | IT | 75000.00 | 2019-11-30 | true |
| 104 | Emily | Williams | emily.w@company.com | HR | 48000.00 | 2022-01-10 | true |
| 105 | David | Brown | david.brown@company.com | Sales | 51000.00 | 2021-09-05 | true |
| 106 | Lisa | Garcia | lisa.g@company.com | IT | 72000.00 | 2020-05-18 | true |
| 107 | Robert | Miller | robert.m@company.com | Marketing | 58000.00 | 2023-02-28 | false |
| 108 | Maria | Davis | maria.davis@company.com | Finance | 68000.00 | 2018-08-12 | true |
| 109 | James | Wilson | james.w@company.com | IT | 80000.00 | 2017-06-25 | true |
| 110 | Jennifer | Taylor | jennifer.t@company.com | Sales | 53000.00 | 2022-11-14 | true |
+-------------+------------+-----------+------------------------------+------------+----------+------------+-----------+
-- Departments table:
+---------------+-----------------+------------+---------------+
| department_id | department_name | manager_id | location |
+---------------+-----------------+------------+---------------+
| 1 | Sales | 101 | New York |
| 2 | Marketing | 102 | Chicago |
| 3 | IT | 109 | San Francisco |
| 4 | HR | 104 | Boston |
| 5 | Finance | 108 | New York |
+---------------+-----------------+------------+---------------+
-- Projects table:
+------------+-----------------------+---------------+------------+------------+-----------+
| project_id | project_name | department_id | start_date | end_date | budget |
+------------+-----------------------+---------------+------------+------------+-----------+
| 1001 | Website Redesign | 3 | 2024-01-15 | 2024-06-30 | 50000.00 |
| 1002 | Product Launch | 2 | 2024-02-01 | 2024-05-31 | 75000.00 |
| 1003 | Sales Expansion | 1 | 2024-03-10 | 2024-12-31 | 120000.00 |
| 1004 | HR System Upgrade | 4 | 2024-04-01 | 2024-09-30 | 30000.00 |
| 1005 | Mobile App Development| 3 | 2024-02-15 | 2024-08-31 | 90000.00 |
+------------+-----------------------+---------------+------------+------------+-----------+
The asterisk (*) is a wildcard character that means "all columns" in SQL.
-- Select ALL columns from employees table
SELECT * FROM employees;
-- This is equivalent to explicitly listing every column:
SELECT employee_id, first_name, last_name, email, department, salary, hire_date, is_active
FROM employees;
-- When you first encounter a new table
SELECT * FROM customers LIMIT 10; -- See first 10 rows with all columns
-- Check what data you just inserted
INSERT INTO products VALUES (101, 'Laptop', 999.99);
SELECT * FROM products WHERE product_id = 101; -- Verify all fields
-- Small tables where you need all information
SELECT * FROM departments; -- Only 5 columns anyway
-- BAD: Returns unnecessary data, slower
SELECT * FROM orders WHERE customer_id = 123;
-- GOOD: Returns only needed data, faster
SELECT order_id, order_date, total_amount
FROM orders
WHERE customer_id = 123;
-- If 'users' has 50 columns including large profile pictures
SELECT * FROM users WHERE user_id = 456; -- BAD: Includes blob data
SELECT username, email, join_date FROM users WHERE user_id = 456; -- GOOD
-- If you rely on column positions, * can be dangerous
-- Table structure might change, breaking your code
-- Get complete overview of all employees
SELECT * FROM employees;
-- View all details of IT department employees
SELECT * FROM employees WHERE department = 'IT';
-- See all employees sorted by hire date
SELECT * FROM employees ORDER BY hire_date DESC;
-- See first 5 employees
SELECT * FROM employees LIMIT 5;
-- Get only names and emails
SELECT first_name, last_name, email FROM employees;
-- Get department and salary info
SELECT department, salary, hire_date FROM employees;
-- Rename columns for readability
SELECT
first_name AS "First Name",
last_name AS "Last Name",
salary AS "Annual Salary",
salary/12 AS "Monthly Salary" -- Calculate monthly salary
FROM employees;
-- See all unique departments
SELECT DISTINCT department FROM employees;
-- See unique department and location combinations
SELECT DISTINCT department, location FROM employees
JOIN departments ON employees.department = departments.department_name;
-- Calculate with columns
SELECT
first_name,
last_name,
salary,
salary * 0.10 AS "Bonus Amount",
salary + (salary * 0.10) AS "Total Compensation"
FROM employees;
-- Combine first and last name
SELECT
first_name || ' ' || last_name AS full_name, -- PostgreSQL, SQLite
CONCAT(first_name, ' ', last_name) AS full_name, -- MySQL
first_name + ' ' + last_name AS full_name, -- SQL Server
department
FROM employees;
-- Generate a clean employee directory
SELECT
employee_id AS "ID",
first_name AS "First Name",
last_name AS "Last Name",
UPPER(department) AS "Department",
FORMAT(salary, 2) AS "Salary", -- MySQL
TO_CHAR(hire_date, 'MM/DD/YYYY') AS "Hire Date"
FROM employees
WHERE is_active = true
ORDER BY last_name, first_name;
-- Combine employees and departments
SELECT
e.first_name,
e.last_name,
e.department,
d.location,
d.department_name
FROM employees e
JOIN departments d ON e.department = d.department_name
WHERE e.salary > 60000;
-- Analyze salary distribution
SELECT
department,
COUNT(*) AS "Employee Count",
MIN(salary) AS "Lowest Salary",
MAX(salary) AS "Highest Salary",
AVG(salary) AS "Average Salary",
SUM(salary) AS "Total Department Salary"
FROM employees
WHERE is_active = true
GROUP BY department
ORDER BY "Average Salary" DESC;
-- Get random sample of data
SELECT * FROM employees ORDER BY RAND() LIMIT 3;
-- Get every nth row (e.g., every 3rd employee)
SELECT * FROM employees WHERE employee_id % 3 = 0;
-- Check for data quality issues
SELECT * FROM employees
WHERE email IS NULL
OR department IS NULL
OR hire_date IS NULL;
-- Employees hired this year
SELECT * FROM employees
WHERE YEAR(hire_date) = YEAR(CURRENT_DATE);
-- Employees with upcoming work anniversaries
SELECT first_name, last_name, hire_date,
DATEDIFF(CURRENT_DATE, hire_date) AS "Days Employed"
FROM employees;
-- SLOW: Returns all columns, processes all rows
SELECT * FROM large_table WHERE condition;
-- FAST: Returns only needed columns
SELECT id, name, date FROM large_table WHERE condition;
-- FASTER: Add limits
SELECT id, name, date FROM large_table
WHERE condition
LIMIT 1000;
-- Use indexes effectively
SELECT * FROM employees
WHERE department = 'IT' -- Good if department is indexed
AND hire_date > '2020-01-01'; -- Good if hire_date is indexed
* in productionLIMIT for testingSELECT * for exploration, then refineTry these with our demo database:
-- Exercise 1: Get all employees in Sales department
-- Your code here:
-- Exercise 2: Show only first names and salaries of active employees
-- Your code here:
-- Exercise 3: Create a report showing employee full name and years of service
-- Your code here:
-- Exercise 4: Find employees earning more than $70,000
-- Your code here:
-- Exercise 1
SELECT * FROM employees WHERE department = 'Sales';
-- Exercise 2
SELECT first_name, salary FROM employees WHERE is_active = true;
-- Exercise 3
SELECT
CONCAT(first_name, ' ', last_name) AS full_name,
DATEDIFF(CURRENT_DATE, hire_date)/365 AS years_of_service
FROM employees;
-- Exercise 4
SELECT first_name, last_name, salary
FROM employees
WHERE salary > 70000;
Remember: The SELECT statement is your primary tool for interacting with database data. Start with SELECT * to explore, then refine to specific columns for better performance. Every query you write should answer a specific question about your data!