13.6 Soft Deletes: deleted_at Columns vs Archival Tables

Right, let’s talk about the great “delete-but-not-really” debate. You’re not a monster; you don’t want to permanently nuke user data. Maybe it’s for legal compliance (GDPR, CCPA), maybe it’s for audit trails, or maybe you’ve just learned the hard way that users have a hilarious tendency to beg for their data back six months after angrily telling you to delete everything. So we implement soft deletes. But how? There are two main camps, and they’re at war in the corridors of every engineering department.

13.5 The RETURNING Clause in UPDATE and DELETE

Alright, let’s talk about RETURNING. This is the part where SQL stops being a faceless drone that just reports “UPDATE 14” and starts being a proper, helpful assistant. It’s the feature that answers the question you ask every single time you run an update or delete: “Okay, but what did I just change?” Think about it. You just executed UPDATE users SET status = 'banned' WHERE last_login < '2020-01-01';. The database smugly informs you that it updated 47 rows. Great. Which 47? The ones with user_id 5, 78, 203…? You have no idea. You’re left in the dark, forced to run a separate SELECT to see the aftermath. It’s clunky, it’s inefficient, and it introduces a potential race condition. RETURNING eliminates this entire song and dance.

13.4 TRUNCATE: Fast Bulk Deletion and Its Caveats

Alright, let’s talk about TRUNCATE. This is the command you use when you don’t just want to delete rows from a table, you want to obliterate them from existence with extreme prejudice. It’s the database equivalent of “I’m not just kicking you out of the pool, I’m draining the entire thing.” DELETE FROM table_name is a row-by-row operation. It’s meticulous. It writes to the transaction log for every. single. row. If you’re dealing with a billion-row table, that’s a billion log entries, and it’s going to take forever while absolutely thrashing your transaction log. TRUNCATE TABLE, on the other hand, is a bulk operation. It deallocates the data pages themselves—the fundamental units of data storage—and just marks them as ‘free space’. It’s not deleting rows; it’s making the entire table’s data disappear in a single, atomic operation. The result? It’s phenomenally faster. We’re talking seconds versus hours for large tables.

13.3 DELETE with a USING Clause

Right, so you’ve mastered deleting rows from a single table. That’s cute. Welcome to the big leagues, where DELETE statements get a power-up and the potential for catastrophic error multiplies accordingly. The USING clause is how you tell PostgreSQL, “I need to delete from this table over here, but to figure out which rows, I first need to poke around in these other tables over there.” It’s your join condition for a deletion.

13.2 Joins in UPDATE: Updating Rows Based on Another Table

Right, so you’ve mastered UPDATE for a single table. You feel powerful. Good. Now let’s graduate to the real world, where data is messy, relationships are complicated, and you need to update one table based on the contents of another. This is where UPDATE...FROM joins come in, and it’s also where you can accidentally nuke an entire table if you’re not careful. Don’t worry, I’ll show you how to avoid that.

13.1 UPDATE Syntax: SET, WHERE, and Expressions

Right, let’s talk about UPDATE. It’s the SQL command that lets you fix your data, or, if you’re not careful, spectacularly ruin it. The core concept is simple: find some rows and change some values. The execution is where you earn your paycheck. The basic syntax is a three-act play: UPDATE table_name SET column1 = value1, column2 = value2, ... WHERE some_condition; Forget the WHERE clause, and you’ve just performed a table-wide “oops.” It’s the database equivalent of using rm -rf / because you wanted to clean up a single file in your downloads folder. The SET clause is where the magic (or the tragedy) happens, allowing you to use expressions, not just literal values.

— joke —

...