SQL CRUD, Queries, and Joins

You can read this note directly if you know tables, records, fields, and keys. SQL is the language used to work with relational databases.

SQL lets us describe what data we want to create, read, update, or delete. The database management system then carries out the operation on the stored tables.

The main beginner skill is not memorising isolated commands. It is learning how to read a query carefully:

Which table or tables are used?
Which rows are selected?
Which fields are returned?
Are rows grouped, joined, sorted, updated, or deleted?

How to Read SQL

SQL is usually read as a request to the database.

SELECT title
FROM Book
WHERE available = 1;

Read it as:

From the Book table, return the title field for rows where available is 1.

CRUD means:

LetterOperationSQL idea
CcreateCREATE, INSERT
RreadSELECT
UupdateUPDATE
DdeleteDELETE

Beginner warning: CREATE can mean creating a table structure, while INSERT creates a new stored record inside a table. In many school examples, both are placed under the broader idea of create.

Example Schema

We will use a small library database throughout this note.

CREATE TABLE Student (
    student_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    class_id TEXT NOT NULL
);
 
CREATE TABLE Book (
    book_id INTEGER PRIMARY KEY,
    title TEXT NOT NULL,
    author TEXT NOT NULL,
    available INTEGER NOT NULL
);
 
CREATE TABLE Loan (
    loan_id INTEGER PRIMARY KEY,
    student_id INTEGER NOT NULL,
    book_id INTEGER NOT NULL,
    loan_date TEXT NOT NULL,
    FOREIGN KEY (student_id) REFERENCES Student(student_id),
    FOREIGN KEY (book_id) REFERENCES Book(book_id)
);

This schema has three tables:

TableStoresPrimary key
Studentstudent recordsstudent_id
Bookbook recordsbook_id
Loanborrowing recordsloan_id

The Loan table also contains foreign keys:

Foreign keyReferencesPurpose
Loan.student_idStudent.student_ididentifies the student who borrowed a book
Loan.book_idBook.book_ididentifies the book that was borrowed

Sample Data Used in This Note

The examples below use this small dataset.

Student

student_idnameclass_id
101Aisha24S01
102Ben24S01
103Chen24S02
104Devi24S03

Book

book_idtitleauthoravailable
1Data TrailsM Tan1
2Networks FirstA Lee0
3Python BasicsS Kumar1

Loan

loan_idstudent_idbook_idloan_date
50110122026-07-08
50210212026-07-08
50310132026-07-09

Notice that Chen and Devi have no loan records in this sample data. This will be useful when we discuss LEFT JOIN.

SQL Clause Order and Thinking Order

SQL has a usual written order:

SELECT ...
FROM ...
WHERE ...
GROUP BY ...
ORDER BY ...;

A beginner can think through it in this order:

StepQuestion to askSQL clause
1Which table or tables are used?FROM, JOIN
2Which rows should remain?WHERE
3Should rows be grouped?GROUP BY
4What should be shown in the output?SELECT
5How should the output be sorted?ORDER BY

This thinking order helps avoid a common mistake: writing SQL by guessing the syntax without understanding what the database is being asked to do.

Caption: SQL written order starts with SELECT, but trace order starts with FROM: identify tables, filter rows, group rows, choose output fields, then sort.

Insert Records

INSERT adds new rows to a table.

INSERT INTO Student VALUES (101, 'Aisha', '24S01');
INSERT INTO Book VALUES (1, 'Data Trails', 'M Tan', 1);

Use quotes for text values. Do not quote numeric values unless the field is meant to store text.

After these inserts, the tables contain:

Student
101 | Aisha | 24S01
 
Book
1 | Data Trails | M Tan | 1

A more explicit form names the fields being inserted:

INSERT INTO Student (student_id, name, class_id)
VALUES (105, 'Ethan', '24S02');

This is often clearer because the reader can see which value belongs to which field.

Select Records

SELECT reads data and returns selected fields from matching rows.

SELECT title, author
FROM Book;

Possible result:

titleauthor
Data TrailsM Tan
Networks FirstA Lee
Python BasicsS Kumar

To filter rows, use WHERE:

SELECT title
FROM Book
WHERE available = 1;

Possible result:

title
Data Trails
Python Basics

Here, the database reads the Book table, keeps only rows where available is 1, and returns only the title field.

Common Comparison Operators

WHERE conditions often use comparison operators.

OperatorMeaningExample
=equalsavailable = 1
<>not equalclass_id <> '24S01'
>greater thanbook_id > 1
<less thanbook_id < 3
>=greater than or equal tobook_id >= 2
<=less than or equal tobook_id <= 2

Example with text:

SELECT title
FROM Book
WHERE author = 'M Tan';

Text values such as 'M Tan' must be quoted.

Example with a number:

SELECT title
FROM Book
WHERE book_id > 1;

Numeric values such as 1 usually do not need quotes.

Sorting Results

ORDER BY sorts the output.

SELECT title, author
FROM Book
ORDER BY title ASC;

ASC means ascending order. DESC means descending order.

SELECT title, author
FROM Book
ORDER BY title DESC;

ORDER BY changes the order of the displayed result. It does not change the stored order of rows in the table.

Update and Delete

UPDATE changes existing rows.

UPDATE Book
SET available = 0
WHERE book_id = 1;

This changes only the book whose book_id is 1.

DELETE removes existing rows.

DELETE FROM Loan
WHERE loan_id = 5;

Always check the WHERE condition. Without WHERE, an UPDATE or DELETE may affect every row in the table.

Dangerous example:

UPDATE Book
SET available = 0;

This sets every book as unavailable.

Dangerous example:

DELETE FROM Loan;

This deletes every loan record.

A good habit is to test the matching rows with SELECT first:

SELECT *
FROM Book
WHERE book_id = 1;

Then, if the selected row is correct, run the UPDATE.

Aggregate Functions and Grouping

Aggregate functions calculate a value from multiple rows.

FunctionMeaning
COUNT()count rows
SUM()add values
AVG()average values
MIN()smallest value
MAX()largest value

Example:

SELECT class_id, COUNT(*) AS number_of_students
FROM Student
GROUP BY class_id;

GROUP BY forms groups first. COUNT(*) counts rows inside each group.

Using the sample Student table, the output is:

class_idnumber_of_students
24S012
24S021
24S031

Beginner trace:

class_id grouprows in groupCOUNT(*)
24S01Aisha, Ben2
24S02Chen1
24S03Devi1

AS number_of_students gives the output column a clearer name.

Filtering Before Grouping

WHERE filters rows before grouping.

SELECT class_id, COUNT(*) AS number_of_students
FROM Student
WHERE class_id <> '24S03'
GROUP BY class_id;

This excludes students from 24S03 before counting the remaining groups.

Joins

A join combines related rows from two or more tables.

Example: show which student borrowed which book.

SELECT Student.name, Book.title, Loan.loan_date
FROM Loan
INNER JOIN Student ON Loan.student_id = Student.student_id
INNER JOIN Book ON Loan.book_id = Book.book_id;

The join conditions matter:

Loan.student_id = Student.student_id
Loan.book_id = Book.book_id

Without meaningful join conditions, the result may pair unrelated rows.

Beginner trace:

Loan fieldMatching tableWhy
Loan.student_idStudent.student_idfind the student name
Loan.book_idBook.book_idfind the book title

The join does not invent new data. It reconnects rows that were deliberately split into separate tables.

Caption: A join matches Loan.student_id to Student.student_id and Loan.book_id to Book.book_id, then combines fields from the matched rows.

Why Field Qualification Matters

In a join, different tables may contain fields with similar or identical names. It is often clearer to write the table name before the field name.

SELECT Student.name, Book.title
FROM Loan
INNER JOIN Student ON Loan.student_id = Student.student_id
INNER JOIN Book ON Loan.book_id = Book.book_id;

Student.name means the name field from the Student table.

Book.title means the title field from the Book table.

This makes the query easier to read and prevents confusion when more than one table has a field with the same name.

Wrong Join Versus Correct Join

Wrong or weak query:

SELECT Student.name, Book.title
FROM Student, Book;

This does not say how a student is related to a book. It may produce unrelated combinations, such as pairing every student with every book.

Better query:

SELECT Student.name, Book.title
FROM Loan
INNER JOIN Student ON Loan.student_id = Student.student_id
INNER JOIN Book ON Loan.book_id = Book.book_id;

This uses the Loan table to connect the correct student to the correct borrowed book.

Inner Join and Left Join

An inner join returns only matching rows.

Example:

SELECT Student.name, Loan.loan_date
FROM Student
INNER JOIN Loan ON Student.student_id = Loan.student_id;

This shows students who have matching loan records. A student with no loans will not appear.

A left join keeps every row from the left table, even if there is no matching row on the right.

Example:

SELECT Student.name, Loan.loan_date
FROM Student
LEFT JOIN Loan ON Student.student_id = Loan.student_id
ORDER BY Student.name, Loan.loan_date;

This can show students even if they have no loans.

Using the sample data, Chen and Devi have no loan records, but they can still appear in the output of the LEFT JOIN. Their loan_date values would be empty or shown as NULL, depending on the database tool.

nameloan_date
Aisha2026-07-08
Aisha2026-07-09
Ben2026-07-08
ChenNULL
DeviNULL

Beginner model:

Join typeWhat appears?
INNER JOINonly rows with matches on both sides
LEFT JOINall rows from the left table, with matching right-side data where available

Use LEFT JOIN when the question asks for all records from one table, including those without matching records in another table.

Aliases

Aliases give shorter temporary names to tables or output fields.

SELECT S.name, B.title, L.loan_date
FROM Loan AS L
INNER JOIN Student AS S ON L.student_id = S.student_id
INNER JOIN Book AS B ON L.book_id = B.book_id;

Here:

AliasTable
SStudent
BBook
LLoan

Aliases are useful in longer queries, but beginners should first understand the full table names.

How to Answer SQL Questions in Exams

When given an SQL question, use this pattern:

  1. Identify the table or tables needed.
  2. Identify the fields required in the output.
  3. Identify the condition for filtering rows.
  4. Decide whether a join is needed.
  5. Decide whether grouping, counting, or sorting is needed.
  6. Write the query and check the clause order.

Example question:

Show the names of students in class 24S01.

Reasoning:

StepDecision
TableStudent
Output fieldname
Conditionclass_id = '24S01'
Join needed?no
Grouping needed?no

Query:

SELECT name
FROM Student
WHERE class_id = '24S01';

Example question:

Show the names of students and titles of books they borrowed.

Reasoning:

StepDecision
TablesStudent, Book, Loan
Output fieldsStudent.name, Book.title
Join needed?yes, through Loan
Conditionsmatch student IDs and book IDs

Query:

SELECT Student.name, Book.title
FROM Loan
INNER JOIN Student ON Loan.student_id = Student.student_id
INNER JOIN Book ON Loan.book_id = Book.book_id;

Common Mistakes

  • Forgetting the semicolon when a tool expects it.
  • Writing WHERE after ORDER BY; filtering should appear before sorting.
  • Using = with text but forgetting quotes around the text value.
  • Running UPDATE or DELETE without a WHERE clause.
  • Joining tables without a correct relationship condition.
  • Selecting a field name that appears in multiple tables without qualifying it.
  • Using GROUP BY without understanding what rows are being grouped.
  • Confusing INNER JOIN and LEFT JOIN.
  • Thinking that ORDER BY changes the stored data. It only sorts the output.
  • Writing a query from memory without checking which tables contain the needed fields.

Quick Reference

TaskSQL pattern
show all rows from a tableSELECT * FROM TableName;
show selected fieldsSELECT field1, field2 FROM TableName;
filter rowsWHERE condition
sort outputORDER BY field ASC or ORDER BY field DESC
count rows in each groupCOUNT(*) with GROUP BY
change rowsUPDATE ... SET ... WHERE ...
delete rowsDELETE FROM ... WHERE ...
combine related tablesJOIN ... ON key_field = matching_key_field

Check Your Understanding

  1. What does SELECT do?
  2. Why is WHERE important in UPDATE?
  3. What does GROUP BY class_id do?
  4. Why does a join need an ON condition?
  5. What is the difference between INNER JOIN and LEFT JOIN?
  6. Why might Student.name be clearer than just name in a join query?

Answers:

  1. It reads data and returns selected columns from matching rows.
  2. It limits which rows are changed.
  3. It groups rows with the same class_id.
  4. It tells the database how records in the two tables are related.
  5. An inner join returns only matching rows; a left join keeps all rows from the left table and adds matching right-side data where available.
  6. It shows exactly which table the field comes from, which is useful when a query uses multiple tables.