PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Por que usar *DB.exec() ou instruções preparadas em Golang?

"Por que usar db.Exec()":


É verdade que você pode usar db.Exec e db.Query alternadamente para executar as mesmas instruções sql, no entanto, os dois métodos retornam diferentes tipos de resultados. Se implementado pelo driver, o resultado retornado de db.Exec pode dizer quantas linhas foram afetadas pela consulta, enquanto db.Query retornará o objeto de linhas em vez disso.

Por exemplo, digamos que você queira executar um DELETE declaração e você quer saber quantas linhas foram excluídas por ela. Você pode fazer isso da maneira correta:
res, err := db.Exec(`DELETE FROM my_table WHERE expires_at = $1`, time.Now())
if err != nil {
    panic(err)
}

numDeleted, err := res.RowsAffected()
if err != nil {
    panic(err)
}
print(numDeleted)

ou a maneira mais detalhada e objetivamente mais cara:
rows, err := db.Query(`DELETE FROM my_table WHERE expires_at = $1 RETURNING *`, time.Now())
if err != nil {
    panic(err)
}
defer rows.Close()

var numDelete int
for rows.Next() {
    numDeleted += 1
}
if err := rows.Err(); err != nil {
    panic(err)
}
print(numDeleted)

Há uma terceira maneira de fazer isso com uma combinação de CTEs postgres, SELECT COUNT , db.QueryRow e row.Scan mas não acho que um exemplo seja necessário para mostrar o quão irracional seria uma abordagem quando comparada a db.Exec .

Outra razão para usar db.Exec sobre db.Query é quando você não se importa com o resultado retornado, quando tudo que você precisa é executar a consulta e verificar se houve erro ou não. Nesse caso, você pode fazer isso:
if _, err := db.Exec(`<my_sql_query>`); err != nil {
    panic(err)
}

Por outro lado, você não pode (você pode, mas não deve) fazer isso:
if _, err := db.Query(`<my_sql_query>`); err != nil {
    panic(err)
}

Fazendo isso, depois de um tempo, seu programa entrará em pânico com um erro que diz algo semelhante a too many connections open . Isso ocorre porque você está descartando o db.Rows retornado valor sem primeiro fazer o Close obrigatório ligue para ele, e assim você acaba com o número de conexões abertas subindo e eventualmente atingindo o limite do servidor.

"ou declarações preparadas em Golang?":


Acho que o livro que você citou não está correto. Pelo menos para mim parece ou não um db.Query call cria uma nova instrução preparada toda vez que depende do driver que você está usando.

Veja por exemplo estas duas seções de queryDC (um método não exportado chamado por db.Query ):sem declaração preparada e com declaração preparada.

Independentemente de o livro estar correto ou não, um db.Stmt criado por db.Query seria, a menos que haja algum cache interno acontecendo, jogado fora depois que você fechar as Rows retornadas objeto. Se você chamar manualmente db.Prepare e, em seguida, armazenar em cache e reutilizar o db.Stmt retornado você pode potencialmente melhorar o desempenho das consultas que precisam ser executadas com frequência.

Para entender como uma declaração preparada pode ser usada para otimizar o desempenho, você pode dar uma olhada na documentação oficial:https://www.postgresql.org/docs/current/static/sql-prepare.html