Search code examples
sqlgoprepared-statement

Is it okay to prepare SQL statement once for multiple request?


I have some SQL queries that do not change on every request (only it's parameter). So, instead of doing this for each request:

func HandleRequest() {
    rows, err := db.Query(sqlQuery, params...)
    // do something with data
}

Is it okay if for each reqest I do this instead:

// together with server initialization
stmt, err := db.Prepare(sqlQuery)

func HandleRequest() {
    rows, err := stmt.Query(params...)
    // do something with data
}

Solution

  • As the documentation of DB.Prepare() states:

    Multiple queries or executions may be run concurrently from the returned statement.

    It is safe for concurrent use, although the intended use for prepared statements is not to share them between multiple requests. The main reason is that a prepared statement (may) allocate resources in the DB server itself, and it's not freed until you call the Close() method of the returned statement. So I'd advise against it.

    The typical use case is if you have to run the same statement multiple times with different parameters, such as the example in the documentation:

    projects := []struct {
        mascot  string
        release int
    }{
        {"tux", 1991},
        {"duke", 1996},
        {"gopher", 2009},
        {"moby dock", 2013},
    }
    
    stmt, err := db.Prepare("INSERT INTO projects(id, mascot, release, category) VALUES( ?, ?, ?, ? )")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close() // Prepared statements take up server resources and should be closed after use.
    
    for id, project := range projects {
        if _, err := stmt.Exec(id+1, project.mascot, project.release, "open source"); err != nil {
            log.Fatal(err)
        }
    }