TLDR set-based programming is specifying what rather than how. Describe the data you want. Let the engine decide how to get it.

Let's conceptualize it.

Let Go of Control

Remember when you first learned to program? I remember learning about control flow near the very beginning: if, else, for loops. I wrote procedural code because my brain was still grasping the basics.

This is how I imagine my early code looked.

// procedural
const filteredItems = [];
for (let i = 0; i < items.length; i++) {
  if (items[i].price < 5) {

Notice all the things that must be specified:

  • Start at the beginning of the list let i = 0
  • Go through all the items i < items.length
  • Do not skip any items i++
  • Break on the filtering condition if (items[i].price < 5) { break; }
  • Push the current item into an array variable filteredItems.push(items[i])

So much noise to filter on one condition.

Let's compare that to the functional style.

// functional
const filteredItems = items.filter(item => item.price < 5);

This style of programming is very popular and for good reason.

It's a one-liner and that's nice but the real benefit is the signal to noise ratio. You tell the program what you want. It is declarative. You don't specify the details of how it happens. You trust the array.filter method. Good SQL follows the same path, trusting the engine.

The analogy comparing procedural code to functional code and procedural SQL to declarative SQL is not perfect but I think it is useful.

SQL - What, Not How

In SQL we don't have to specify how the engine should do things. We just need to tell it what to do. Control flow concepts like if, else, break, for are antithetical to set-based SQL.

If you solve SQL problems with control flow the code becomes difficult to read and it will not scale. It will burn CPU cycles and hog memory. You are hammering in a screw.

The core of set-based programming is what, not how. Describe the data you want. Let the SQL engine decide how to get it.

Is Control Flow a Thing in SQL?

Yes, here are some of the keywords:

  • GOTO just no..
  • Cursors - OPEN, FETCH, CLOSE

Don't use these if you're writing new SQL. You wouldn't disable garbage collection on a new project built with a modern language, would you? Then don't write procedural SQL.

You may need to learn the procedural syntax in SQL to understand how to get rid of it in a codebase you've inherited. Cross that bridge when you get there.

Oddly enough, a project I'd worked on with tons of procedural SQL also had roll-your-own garbage collection in the app code.

The Beautiful Parts of SQL

By removing control flow we're left with a fun and useful subset of the language.

  • SUM, AVERAGE, Aggregate Functions..
  • JOINs
  • CTEs

If you learn these well, you will write good SQL queries. You won't be pigeonholed when you need to squeeze out more performance with the proper tools.