This MySQL tutorial builds a small inventory table and answers two practical questions: which items need replenishment, and what is the known value of the stock? You will create typed fields, insert records, filter and sort them, then connect those steps to transactions and application safety. The SQL targets MySQL 8.4-style syntax and uses InnoDB explicitly.
A MySQL client is different from the MySQL server. A graphical editor or command-line client sends statements to a server connection; the server stores tables and evaluates queries. Verify which server and database your connection targets before running a setup script. Use a disposable practice database for the table below.
Connection and database context
A connection has a host, a database account and an active database context. The account’s privileges determine which commands it may run. You can select an authorised existing practice database with USE database_name; creating a database requires additional permission. Installation and account setup are operational tasks, while SELECT is a query sent after the connection is established.
Do not place account passwords inside saved SQL examples. An application should read its connection configuration through an appropriate secret or environment mechanism and use a limited account. A reporting job may need SELECT without receiving DROP or account-management privileges. Connection success is not evidence that the correct database has been selected.
Types, keys and InnoDB
The inventory example declares an integer generated primary key, a unique item code, a decimal price and a nonnegative stock quantity. The id provides technical row identity, while code provides a separate business uniqueness rule. DECIMAL(10,2) expresses exact decimal intent for the price in MySQL; quantity is a count, so it uses INTEGER.
InnoDB provides transaction and foreign-key support for common application tables. AUTO_INCREMENT generates omitted ids but does not promise consecutive numbers without gaps. A UNIQUE label still matters even with a generated key, because two technical ids should not allow accidental duplicate business codes. CHECK enforcement depends on a supported MySQL version; the modern target here enforces the quantity rule.
Reading inventory deliberately
SELECT chooses the columns a reader needs, WHERE restricts the records, and ORDER BY makes the displayed order intentional. quantity<5 is the replenishment rule in this lesson; it is not a universal stock policy. price*quantity calculates each row’s current known stock value without changing saved fields. Use an alias that identifies the calculation.
A LIMIT applied after ordering can produce a small queue of items, but a stable key should resolve tied priorities. Backticks are MySQL’s usual identifier quotes, whereas single quotes delimit text values. The example avoids reserved names so quoted identifiers are unnecessary. Do not copy PostgreSQL’s default double-quoted naming style into MySQL without considering SQL mode.
Changing rows with a transaction
INSERT adds inventory records, UPDATE can adjust a saved quantity, and DELETE removes records. Preview the intended UPDATE or DELETE population with SELECT using the same key condition. A stock adjustment often belongs with another write, such as recording its movement, in one InnoDB transaction so the related changes succeed or fail together.
START TRANSACTION begins the unit, COMMIT confirms it and ROLLBACK abandons uncommitted work. Autocommit means ordinary statements can commit individually unless grouped explicitly. Many MySQL DDL commands cause implicit commits, so table creation should not be treated as just another reversible row update. Check the operation’s documented transaction behaviour rather than applying one rollback assumption to every SQL statement.
Application queries and dialect boundaries
Bind item codes and thresholds as values through the driver’s prepared-statement interface. A bound parameter does not quote a dynamic table name, and it does not make LIKE wildcards literal. For generated ids, use the insert result or a same-connection mechanism such as LAST_INSERT_ID rather than reading the largest id from the table.
MySQL supports CTEs and window functions in modern versions, but it has no native FULL OUTER JOIN. Date arithmetic commonly uses DATE_ADD and INTERVAL syntax. ONLY_FULL_GROUP_BY helps reject ambiguous summaries; do not disable it merely because a query selects an ungrouped label. These distinctions matter when translating a general SQL example into a MySQL application.
Inspecting a query as data grows
An index on a selective lookup such as code can support efficient item retrieval, while an index on quantity may or may not benefit a low-stock report depending on the population and plan. EXPLAIN shows the chosen access strategy. An index is not automatically useful just because its column appears somewhere in the query.
Measure with realistic row counts and distributions, and include the write cost of additional indexes. Keep source records and report totals reconcilable. If price later becomes nullable or multiple currencies are introduced, the inventory-value expression needs a new completeness or currency policy; a query that runs successfully can still answer an outdated business question.
Run the complete practice setup
Run these statements in an existing disposable MySQL database. The three records include a stocked item, an out-of-stock item and a low-stock item. Explicit ids keep the expected output fixed while the table definition demonstrates how future omitted ids would be generated.
CREATE TABLE mysql_inventory (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(20) NOT NULL UNIQUE,
price DECIMAL(10,2) NOT NULL,
quantity INTEGER NOT NULL CHECK(quantity>=0)
) ENGINE=InnoDB;
INSERT INTO mysql_inventory (id,code,price,quantity) VALUES
(1,'PEN',1.50,20),(2,'BOOK',4.00,0),(3,'BAG',12.00,3);Practical exercise and worked solution
Calculate the known inventory value across all three items, and show the number of different records included. Keep the output as one summary row.
SELECT COUNT(*) AS item_count,SUM(price*quantity) AS stock_total
FROM mysql_inventory;item_count | stock_total
3 | 66.00PEN contributes twenty units at 1.50, or thirty. BOOK contributes zero, and BAG contributes thirty-six. The total is sixty-six across three rows. SUM measures value rather than item identity, while COUNT measures records rather than total units. If a new item duplicates a price, its value should still contribute; SUM(DISTINCT price) would not be an appropriate inventory valuation.
Official documentation
See the MySQL tutorial, InnoDB transaction model and MySQL join grammar for their individual server rules.
MySQL Tutorial for Beginners with a Small Inventory Database Examples
Each example shows the code, the output it produces and what each part does.
Find items that need replenishment
The query keeps the two records below the lesson’s stock threshold. BOOK has no units and therefore zero stock value. BAG has three units at twelve each, giving thirty-six. The explicit quantity-and-id order places the most depleted item first and resolves any future ties predictably.
SELECT code,quantity,price*quantity AS stock_value
FROM mysql_inventory WHERE quantity<5
ORDER BY quantity,id;code | quantity | stock_value
BOOK | 0 | 0.00
BAG | 3 | 36.00Common MySQL Tutorial for Beginners with a Small Inventory Database Mistakes
Mixing ambiguous grouping with a label
SELECT code,SUM(price*quantity) FROM mysql_inventory asks for one total but also an unrelated item code. With ONLY_FULL_GROUP_BY this is rejected unless a valid grouping or dependency applies. If you need a single stock-value total, omit code. If you need totals per item code, group at that grain. Disabling the mode hides the unclear meaning instead of defining the requested result.
MySQL Tutorial for Beginners with a Small Inventory Database FAQ
Is MySQL the same as SQL?
No. SQL is the language family; MySQL is one database product implementing a dialect with its own functions, types and extensions, such as AUTO_INCREMENT and LIMIT. Most standard queries work in MySQL with little change.
Does AUTO_INCREMENT prevent gaps?
No. Generated identities are not gap-free business numbering: failed inserts, rollbacks and deleted rows all leave gaps. Use a separate, controlled sequence if you need consecutive invoice numbers.
Can I use these commands in SQLite unchanged?
Not all of them. The AUTO_INCREMENT and ENGINE clauses are MySQL-specific, so translate the schema deliberately, for example to INTEGER PRIMARY KEY in SQLite. Plain SELECT, INSERT, UPDATE and joins usually work as written.
What is the difference between MySQL and MariaDB?
MariaDB began as a fork of MySQL and is highly compatible for everyday SQL, using the same client tools and many of the same commands. The two have diverged over time in features such as JSON handling and some optimiser behaviour, so check the documentation for the one you run.
Which storage engine should I use in MySQL?
InnoDB, the default. It supports transactions, foreign keys, row-level locking and crash recovery. Older tutorials mention MyISAM, which lacks transactions and foreign keys and should only be used for special legacy cases.
MySQL Tutorial for Beginners with a Small Inventory Database Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
What is the stock value of BAG in the supplied data?
MySQL Tutorial for Beginners with a Small Inventory Database Quiz
2 questions to check what you learned.
Which feature generates omitted ids in this MySQL table?
- AUTO_INCREMENT
- A column alias
- ORDER BY quantity
Does MySQL provide native FULL OUTER JOIN?
- Yes, with the same syntax as every other database
- No; the required full-match result needs a correct alternative
- Only when all prices are decimal