Querying for data

When executing an SQL statement that returns data, use one of the Query methods provided in the database/sql package. Each of these returns a Row or Rows whose data you can copy to variables using the Scan method. You’d use these methods to, for example, execute SELECT statements.

When executing a statement that doesn’t return data, you can use an Exec or ExecContext method instead. For more, see Executing statements that don’t return data.

The database/sql package provides two ways to execute a query for results.

If your code will be executing the same SQL statement repeatedly, consider using a prepared statement. For more, see Using prepared statements.

Caution: Don’t use string formatting functions such as fmt.Sprintf to assemble an SQL statement! You could introduce an SQL injection risk. For more, see Avoiding SQL injection risk.

Querying for a single row

QueryRow retrieves at most a single database row, such as when you want to look up data by a unique ID. If multiple rows are returned by the query, the Scan method discards all but the first.

QueryRowContext works like QueryRow but with a context.Context argument. For more, see Canceling in-progress operations.

The following example uses a query to find out if there’s enough inventory to support a purchase. The SQL statement returns true if there’s enough, false if not. Row.Scan copies the boolean return value into the enough variable through a pointer.

func canPurchase(id int, quantity int) (bool, error) {
    var enough bool
    // Query for a value based on a single row.
    if err := db.QueryRow("SELECT (quantity >= ?) from album where id = ?",
        quantity, id).Scan(&enough); err != nil {
        if err == sql.ErrNoRows {
            return false, fmt.Errorf("canPurchase %d: unknown album", id)
        }
        return false, fmt.Errorf("canPurchase %d: %v", id, err)
    }
    return enough, nil
}

Note: Parameter placeholders in prepared statements vary depending on the DBMS and driver you’re using. For example, the pq driver for Postgres requires a placeholder like $1 instead of ?.

Handling errors

QueryRow itself returns no error. Instead, Scan reports any error from the combined lookup and scan. It returns sql.ErrNoRows when the query finds no rows.

Functions for returning a single row

Function Description
DB.QueryRow
DB.QueryRowContext
Run a single-row query in isolation.
Tx.QueryRow
Tx.QueryRowContext
Run a single-row query inside a larger transaction. For more, see Executing transactions.
Stmt.QueryRow
Stmt.QueryRowContext
Run a single-row query using an already-prepared statement. For more, see Using prepared statements.
Conn.QueryRowContext For use with reserved connections. For more, see Managing connections.

Querying for multiple rows

You can query for multiple rows using Query or QueryContext, which return a Rows representing the query results. Your code iterates over the returned rows using Rows.Next. Each iteration calls Scan to copy column values into variables.

QueryContext works like Query but with a context.Context argument. For more, see Canceling in-progress operations.

The following example executes a query to return the albums by a specified artist. The albums are returned in an sql.Rows. The code uses Rows.Scan to copy column values into variables represented by pointers.

func albumsByArtist(artist string) ([]Album, error) {
    rows, err := db.Query("SELECT * FROM album WHERE artist = ?", artist)
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    // An album slice to hold data from returned rows.
    var albums []Album

    // Loop through rows, using Scan to assign column data to struct fields.
    for rows.Next() {
        var alb Album
        if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist,
            &alb.Price, &alb.Quantity); err != nil {
            return albums, err
        }
        albums = append(albums, alb)
    }
    if err = rows.Err(); err != nil {
        return albums, err
    }
    return albums, nil
}

Note the deferred call to rows.Close. This releases any resources held by the rows no matter how the function returns. Looping all the way through the rows also closes it implicitly, but it is better to use defer to make sure rows is closed no matter what.

Note: Parameter placeholders in prepared statements vary depending on the DBMS and driver you’re using. For example, the pq driver for Postgres requires a placeholder like $1 instead of ?.

Handling errors

Be sure to check for an error from sql.Rows after looping over query results. If the query failed, this is how your code finds out.

Functions for returning multiple rows

Function Description
DB.Query
DB.QueryContext
Run a query in isolation.
Tx.Query
Tx.QueryContext
Run a query inside a larger transaction. For more, see Executing transactions.
Stmt.Query
Stmt.QueryContext
Run a query using an already-prepared statement. For more, see Using prepared statements.
Conn.QueryContext For use with reserved connections. For more, see Managing connections.

Handling nullable column values

The database/sql package provides several special types you can use as arguments for the Scan function when a column’s value might be null. Each includes a Valid field that reports whether the value is non-null, and a field holding the value if so.

Code in the following example queries for a customer name. If the name value is null, the code substitutes another value for use in the application.

var s sql.NullString
err := db.QueryRow("SELECT name FROM customer WHERE id = ?", id).Scan(&s)
if err != nil {
    log.Fatal(err)
}

// Find customer name, using placeholder if not present.
name := "Valued Customer"
if s.Valid {
    name = s.String
}

See more about each type in the sql package reference:

Getting data from columns

When looping over the rows returned by a query, you use Scan to copy a row’s column values into Go values, as described in the Rows.Scan reference.

There is a base set of data conversions supported by all drivers, such as converting SQL INT to Go int. Some drivers extend this set of conversions; see each individual driver’s documentation for details.

As you might expect, Scan will convert from column types to Go types that are similar. For example, Scan will convert from SQL CHAR, VARCHAR, and TEXT to Go string. However, Scan will also perform a conversion to another Go type that is a good fit for the column value. For example, if the column is a VARCHAR that will always contain a number, you can specify a numeric Go type, such as int, to receive the value, and Scan will convert it using strconv.Atoi for you.

For more detail about conversions made by the Scan function, see the Rows.Scan reference.

Handling multiple result sets

When your database operation might return multiple result sets, you can retrieve those by using Rows.NextResultSet. This can be useful, for example, when you’re sending SQL that separately queries multiple tables, returning a result set for each.

Rows.NextResultSet prepares the next result set so that a call to Rows.Next retrieves the first row from that next set. It returns a boolean indicating whether there is a next result set at all.

Code in the following example uses DB.Query to execute two SQL statements. The first result set is from the first query in the procedure, retrieving all of the rows in the album table. The next result set is from the second query, retrieving rows from the song table.

rows, err := db.Query("SELECT * from album; SELECT * from song;")
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

// Loop through the first result set.
for rows.Next() {
    // Handle result set.
}

// Advance to next result set.
rows.NextResultSet()

// Loop through the second result set.
for rows.Next() {
    // Handle second set.
}

// Check for any error in either result set.
if err := rows.Err(); err != nil {
    log.Fatal(err)
}