Stage 1 · Code
Working with Databases
SQL Basics with database/sql
Learn the standard library's database layer the calm, practical way: how drivers attach themselves, how queries turn back into Go values, and why safe parameters are non-negotiable.
Why database/sql Feels Different
Most Go code you've written so far works entirely inside one process. You call a function, pass a value, get a result, and stay inside your own program's memory. A database call is different. Now your program is sending a request to another system, waiting for that system to run SQL, and then translating the answer back into Go values you can use.
A helpful picture is a restaurant pass-through window. Your Go program writes the order ticket. The database kitchen does the actual work. database/sql is the counter in the middle: it knows how to hand orders across, receive finished plates back, and keep the conversation organized. It does not cook the meal itself, and it does not decide your menu. That is why it feels more mechanical than packages like fmt or strings.
The standard library gives you a common interface for talking to SQL databases. It does not contain a built-in PostgreSQL, MySQL, or SQLite engine. Instead, it defines the rules, and a driver package plugs a specific database into those rules.
| Tool | Use it when | What comes back |
|---|---|---|
QueryRow | You expect exactly one row, or zero rows | A single row handle that you later Scan |
Query | You expect many rows | A *sql.Rows cursor you loop through |
Exec | You are not reading rows back | A result describing affected rows or insert metadata |
Opening a DB and Loading a Driver
When you call sql.Open, you are not opening a single permanent wire and pinning your whole program to it. You are creating a *sql.DB value that knows which driver to use, how to build connections, and later how to manage a pool of them. The actual network connection may happen lazily when you run the first real query, which is why many programs call Ping right after setup to fail early if the configuration is wrong.
That mysterious blank import is part of the setup story. A driver package usually registers itself in its init function. You may never call that package directly, but you still need its side effect. Writing _ 'some/driver/package' means, 'compile and run this package's setup code, even though I will not refer to it by name later.'
Normally, Go rejects unused imports because they often signal dead code. A blank import is the deliberate exception: you are saying the value names are unused, but the package's startup side effects are required.
Querying, Exec, and Scan
Once the database is open, the everyday rhythm is simple: send SQL, pass any input values separately, then scan the returned columns into ordinary Go variables. The database speaks in rows and columns. Your Go code speaks in ints, strings, bools, and structs. Scan is the bridge between those two shapes.
Why Parameters Beat String Concatenation
String concatenation feels tempting because it looks quick. But the moment untrusted input gets pasted directly into SQL text, the user is no longer just providing a value. They are helping write the query itself. That is the door SQL injection walks through.
Different drivers use different placeholder styles. Many use ?. Some use numbered placeholders such as $1, $2, and so on. The safety idea is identical either way: the SQL statement stays fixed, and the values travel separately.
Quiz and Practice
Why do many Go programs call `Ping` right after `sql.Open`?
Hands-On Project
Implement `LoadShelfUnits`. It should call `QueryRow` with a parameterized query, scan the returned `units` value into an `int`, and return that value plus any scan error.
Summary and Key Takeaways
database/sqlis the shared interface layer between your Go code and a specific SQL driver.- A blank import is how many drivers register themselves for later use by
sql.Open. QueryRow,Query, andExecare three different shapes of database work, not interchangeable spelling variations.Scancopies database columns into ordinary Go variables in column order.- Parameterized queries are the baseline safe habit; string concatenation lets untrusted input rewrite your SQL.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.