The INSERT Statement

Every row that ever ends up in StreamHub - a new user, a new movie, a new review - gets there through an INSERT statement. It's the first of the four core statements that make up everyday SQL work (the other three, SELECT, UPDATE, and DELETE, are next up in this track), and it's worth getting comfortable with its variations, because real applications rarely insert exactly one plain row at a time.

Basic INSERT Syntax

Here's a brand-new StreamHub user signing up:

INSERT INTO users (username, email, signup_date)
VALUES ('devika_reviews', 'devika@streamhub.example', '2026-08-16');

You name the columns you're supplying values for, then list matching values in the same order. MySQL fills every other column in the row based on its definition - which brings us straight to the next few tricks.

Inserting Multiple Rows at Once

When you have a batch of rows ready - say, StreamHub's initial set of genres - a single INSERT can take several value lists separated by commas:

INSERT INTO genres (name)
VALUES
  ('Action'),
  ('Comedy'),
  ('Drama'),
  ('Sci-Fi');

This is meaningfully faster than running four separate INSERT statements, because MySQL only has to parse the statement once and can write the rows as one batch instead of four separate round trips. When you're loading any real volume of data - importing a CSV of movies, seeding lookup tables - always prefer one multi-row insert over a loop of single-row inserts.

Omitting Columns With a Default or NULL

As covered in Constraints, a column with a DEFAULT or one that simply allows NULL doesn't need to appear in the insert at all:

-- signup_date has DEFAULT (CURRENT_DATE), so we can leave it out entirely
INSERT INTO users (username, email)
VALUES ('arjun_movies', 'arjun@streamhub.example');

MySQL fills signup_date with today's date automatically, exactly as designed.

Letting AUTO_INCREMENT Assign the Primary Key

Every StreamHub table so far has used an id column with AUTO_INCREMENT as its primary key. You never supply that column yourself - just leave it out, and MySQL assigns the next number:

INSERT INTO movies (title, release_year, duration_minutes)
VALUES ('Signal Lost', 2024, 118);

If the last movie inserted had id 41, this new row automatically becomes id 42 - no guessing, and no risk of two inserts colliding on the same id.

Getting the Id MySQL Just Assigned

Right after that insert, your application usually needs to know the new movie's id immediately - for example, to insert its genre links into movie_genres in the very next statement. LAST_INSERT_ID() returns exactly that, for the connection that just did the insert:

INSERT INTO movies (title, release_year, duration_minutes)
VALUES ('Signal Lost', 2024, 118);

-- Returns the id MySQL just assigned to the row above, e.g. 42
SELECT LAST_INSERT_ID();

-- Now link that movie to a genre using the id we just got back
INSERT INTO movie_genres (movie_id, genre_id)
VALUES (42, 1);

This is the standard pattern for populating a many-to-many junction table like movie_genres right after creating the row on one side of the relationship.

INSERT ... ON DUPLICATE KEY UPDATE (Upserts)

Sometimes you don't know in advance whether a row already exists - you just want to insert it if it's new, or update it if it isn't. Instead of checking first and then choosing between INSERT and UPDATE, MySQL lets you say both in one statement. Imagine StreamHub tracks one watchlist entry per user/movie pair and wants re-adding a movie to simply refresh its added_date instead of erroring on a duplicate:

INSERT INTO watchlist (user_id, movie_id, added_date)
VALUES (7, 42, '2026-08-16')
ON DUPLICATE KEY UPDATE added_date = '2026-08-16';

If no row exists yet for that user and movie, this behaves like a normal insert. If a unique or primary key conflict happens - the pair is already on the watchlist - MySQL runs the UPDATE clause on the existing row instead of raising an error. This "upsert" pattern is genuinely useful anywhere you're syncing data and don't want to write the check-then-insert-or-update logic by hand.

When an INSERT Violates a Constraint

Everything covered in Constraints applies in full force to every insert. Try to sneak an invalid rating into reviews:

INSERT INTO reviews (user_id, movie_id, rating, comment)
VALUES (7, 42, 9, 'Incredible show!');
Error
ERROR 3819 (HY000): Check constraint 'reviews_chk_1' is violated

MySQL rejects the entire statement - the row is never written, rating 9 and all. This is exactly the safety net constraints are meant to provide: no matter which INSERT variation you use above, the rules defined on the table still get the final say.

Next up: Data is only useful once you can get it back out. SELECT Basics covers the statement you'll write more than any other in SQL.