What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Start with four clauses: SELECT chooses the columns to show, FROM names the table, WHERE filters rows, and ORDER BY sorts the result. To practice, create a table, insert a row, and query it in SQLite’s sqlite3 command-line tool or browser fiddle.

How to start writing SQL

SQL is used to work with facts stored in relational databases, where information is organized into tables and relationships connect records. A query is made of clauses that specify what to read and how to filter or arrange it. SQL implementations differ, so examples below use broadly familiar syntax; check your database engine’s documentation when adapting commands.

Practice with SQLite

For a low-friction first exercise, SQLite’s official quick start shows how to open or create a database from a terminal with sqlite3 test.db. At the prompt, enter SQL statements. If you do not want to install anything, SQLite also links to a browser-based fiddle for experiments: SQLite quick start.

Another guided option is the PostgreSQL tutorial, which moves from creating a database and tables through queries, joins, aggregates, updates, and deletions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Create a table and add a row

CREATE TABLE defines a table and its columns. This SQLite-compatible example gives each customer an integer identifier, requires a name, and makes email values unique:

CREATE TABLE customers (
  customer_id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  email TEXT UNIQUE
);

Constraints such as NOT NULL and UNIQUE are checked when rows are inserted or updated in SQLite. The table structure and constraint behavior are documented in SQLite CREATE TABLE.

Use INSERT to add a row. Name the columns so it is clear which value goes where:

INSERT INTO customers (name, email)
VALUES ('Ada Lovelace', 'ada@example.com');

SQLite also supports inserting rows from a query with INSERT ... SELECT. If you omit a column from an insert, the database uses its default if one is defined; otherwise SQLite supplies NULL where allowed. See SQLite INSERT.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read and sort rows with SELECT

SELECT reads rows; it does not change the database. The clauses below choose the output, source, filter, and order:

SELECT customer_id, name
FROM customers
WHERE name LIKE 'A%'
ORDER BY name ASC;
  • SELECT customer_id, name returns those two columns.
  • FROM customers reads from the customers table.
  • WHERE name LIKE 'A%' keeps names matching the pattern.
  • ORDER BY name ASC sorts names in ascending order.

To remove duplicate values from the output and cap the number of rows, add DISTINCT and a limit:

SELECT DISTINCT email
FROM customers
ORDER BY email
LIMIT 20;

LIMIT is not universal SQL syntax: some database systems use forms such as TOP or FETCH FIRST instead. Confirm the form for your target engine.

Combine related tables with JOIN

A join connects rows from tables using a related value. This example matches each order to its customer:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
SELECT o.order_id, c.name
FROM orders AS o
JOIN customers AS c
  ON c.customer_id = o.customer_id;

JOIN here means an inner join: it returns rows with a match on both sides. A LEFT JOIN keeps every row from the left-hand table and adds matching values from the right; where no match exists, right-hand columns are NULL.

Include the join condition, usually in an ON clause. Leaving out a meaningful predicate can pair each row in one table with many or all rows in the other, multiplying the result unexpectedly. PostgreSQL introduces joins in its tutorial.

Group rows and filter groups

Aggregate functions such as COUNT summarize rows. GROUP BY defines which rows are summarized together, while HAVING filters the resulting groups:

SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
HAVING COUNT(*) >= 2;
  • WHERE filters individual rows before grouping.
  • GROUP BY forms groups, here one per customer.
  • HAVING keeps groups meeting a condition, here customers with at least two orders.

For example, to count only orders placed after a particular date, put that row-level condition in WHERE; use HAVING for a condition on the count or another group result. PostgreSQL’s tutorial covers aggregate functions and grouping.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Change or delete data safely

UPDATE changes existing rows and DELETE removes them. Before executing either, write a targeted WHERE condition and run the equivalent SELECT to inspect the rows it will affect.

Update a row

UPDATE customers
SET email = 'new@example.com'
WHERE customer_id = 1;

The condition targets the customer whose identifier is 1. Without a restrictive condition, an update can change every row.

Delete a row

DELETE FROM customers
WHERE customer_id = 1;

Without WHERE, this statement targets every row in the table. Where supported by your database workflow, use a transaction so you can verify the result before committing, and check the affected-row count. PostgreSQL’s tutorial covers these introductory write operations.

Know which SQL dialect you are using

SQL has a standard, but database products add or vary syntax and features. For example, SQLite documents some behavior as SQLite-specific, and Microsoft Access uses square brackets for identifiers that contain spaces. Do not assume every example or extension works unchanged across engines.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Check the documentation for the database you are targeting before using dialect-specific features.
  • Label examples by engine when they rely on a particular implementation.
  • Keep simple queries portable where practical, and verify their behavior in the actual database.

SQLite notes implementation-specific behavior in its SELECT documentation. Microsoft explains bracketed identifiers in Access SQL basics.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.