系列索引
连接到MySQL并创建数据库
创建表并插入行
选择单行和多行
预备语句 - WIP
更新行 - WIP
删除行 -
选择单行
我们在前面的教程中创建的product 表有三条记录。在该表上运行select * from product; ,返回以下行。

在本节教程中,我们将编写代码,从该表中选择一条记录。我们将编写一个函数,当输入产品名称时,将返回产品价格。下面提供了这样的查询。
select product_price from product where product_name = "iphone";
上述查询将返回一条记录,并将返回该行的product_price 列。
现在我们已经准备好了查询,让我们继续对它进行编码。
第一步是创建一个预备语句。预备语句是用来对SQL查询进行参数化处理的,这样就可以用不同的参数有效地运行同一个查询。它还可以防止SQL注入。
下面提供了准备好的语句的模板。
select product_price from product where product_name = ?
我们用? 替换了iphone 来创建准备好的语句模板。
下一步是创建准备好的语句。这是用PrepareContext方法完成的。
func selectPrice(db *sql.DB, productName string) (int, error) {
query := `select product_price from product where product_name = ?`
ctx, cancelfunc := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelfunc()
stmt, err := db.PrepareContext(ctx, query)
if err != nil {
log.Printf("Error %s when preparing SQL statement", err)
return 0, err
}
defer stmt.Close()
}
我们将一个带有5 秒超时的上下文传递给第5行的PrepareContext 方法。5.该方法返回一个准备好的语句。我们在第5行推迟关闭该语句。10.
下一步是执行这个准备好的语句。这是用QueryRowContext方法完成的。
var price int
row := stmt.QueryRowContext(ctx, productName)
if err := row.Scan(&price); err != nil {
return 0, err
}
QueryRowContext 方法在准备好的语句中被调用stmt 。这个方法需要一个上下文和一个参数的变量列表作为参数。在我们的例子中,我们在查询中只有一个参数,即产品名称,我们把它作为参数传递给这个方法。
QueryRowContext ,返回一个单行。然后我们需要在row 上调用Scan,将查询返回的列复制到传递给Scan 方法的指针上。这里我们在第3行传递一个整数指针。在这里,我们在上述代码段的第3行传递了一个整数指针,价格将被存储到该指针中。如果查询返回多于一行,Scan 方法将只返回第一行,并且不会有错误。如果没有返回任何行,Scan 将返回一个错误。我们将在下一节中讨论错误处理。
下面提供了完整的函数。
func selectPrice(db *sql.DB, productName string) (int, error) {
log.Printf("Getting product price")
query := `select product_price from product where product_name = ?`
ctx, cancelfunc := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelfunc()
stmt, err := db.PrepareContext(ctx, query)
if err != nil {
log.Printf("Error %s when preparing SQL statement", err)
return 0, err
}
defer stmt.Close()
var price int
row := stmt.QueryRowContext(ctx, productName)
if err := row.Scan(&price); err != nil {
return 0, err
}
return price, nil
}
错误处理
在上述函数的第14行中使用的扫描方法14行使用的扫描方法在查询没有返回任何行时将返回错误ErrNoRows 。让我们从main 中调用我们的selectPrice 函数,该函数将处理错误。
func main() {
...
productName := "iphone"
price, err := selectPrice(db, productName)
switch {
case err == sql.ErrNoRows:
log.Printf("Product %s not found in DB", productName)
case err != nil:
log.Printf("Encountered err %s when fetching price from DB", err)
default:
log.Printf("Price of %s is %d", productName, price)
}
}
我们将产品名称作为iphone 传递给selectPrice ,然后在错误上切换案例。如果错误的类型是ErrNoRows ,我们就打印一条信息说没有找到该产品。如果是其他错误,我们就打印错误信息。而在default 的情况下,我们打印结果。
这个程序将打印
Price of iphone is 950
选择多行
在本节中,我们将学习如何从一个表中选择多行。我们将编写一个函数,将最低价格和最高价格作为参数,并返回符合查询条件的产品的名称和价格。
下面提供了这样的查询。
select product_name, product_price from product where product_price >= 900 && product_price <= 1000;
上述查询将返回价格在900 和1000 之间的所有产品的名称和价格。
大部分步骤与我们在上一节讨论的选择单行相似。我在下面提供了选择多行的完整函数。
func selectProductsByPrice(db *sql.DB, minPrice int, maxPrice int) ([]product, error) {
log.Printf("Getting products by price")
query := `select product_name, product_price from product where product_price >= ? && product_price <= ?;`
ctx, cancelfunc := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelfunc()
stmt, err := db.PrepareContext(ctx, query)
if err != nil {
log.Printf("Error %s when preparing SQL statement", err)
return []product{}, err
}
defer stmt.Close()
rows, err := stmt.QueryContext(ctx, minPrice, maxPrice)
if err != nil {
return []product{}, err
}
var products = []product{}
for rows.Next() {
var prd product
if err := rows.Scan(&prd.name, &prd.price); err != nil {
return []product{}, err
}
products = append(products, prd)
}
if err := rows.Err(); err != nil {
return []product{}, err
}
return products, nil
}
与上一节类似,我们首先在上述函数的第6行创建一个准备好的语句。在上述函数的第6行,我们首先创建了一个准备好的语句。之后,我们在第6行调用QueryContext方法,stmt 。这个方法将返回由查询选择的行的列表。
得到行列表后,我们在第17行调用rows 上的Next方法。17.这将为使用Scan 方法读取结果做准备。如果在调用Next() 时有任何错误,它将返回false ,for循环将终止。rows 如果出现错误,Err() 方法将返回一个非nil 的值,我们需要调用这个方法来找出在调用Next() 时是否有任何错误。这将在第24行完成。
在第18行,我们创建了一个类型为。18行,我们创建了product 类型的prd 来存储结果。
如果你不记得了,product 结构有以下字段。
type product struct {
name string
price int
}
在第18行,我们创建了,以存储结果。prd 19,结果列被复制到name 和price 结构的字段中,在第 22 行,prd 被附加到products 片断中。
products 在第27行中,返回了 "A"。
下一步是在main中调用这个函数。
func main() {
...
minPrice := 900
maxPrice := 1000
products, err := selectProductsByPrice(db, minPrice, maxPrice)
if err != nil {
log.Printf("Error %s when selecting product by price", err)
return
}
for _, product := range products {
log.Printf("Name: %s Price: %d", product.name, product.price)
}
我们将900 和1000 作为最低和最高价格传递给第5行的selectProductsByPrice 函数。
如果有任何错误,我们在打印错误后返回。
如果没有错误,则在第5行打印符合查询条件的产品的名称和价格。
这个程序将被打印出来。
2021/10/16 21:10:52 Getting products by price
2021/10/16 21:10:52 Name: iphone Price: 950
2021/10/16 21:10:52 Name: Galaxy Price: 990
至此,本教程就结束了。请在评论中分享您的宝贵意见。
整个代码可在github.com/golangbot/m…