选择单行和多行的MySQL教程

69 阅读5分钟

系列索引

连接到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;  

上述查询将返回价格在9001000 之间的所有产品的名称和价格。

大部分步骤与我们在上一节讨论的选择单行相似。我在下面提供了选择多行的完整函数。

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() 时有任何错误,它将返回falsefor循环将终止。rows 如果出现错误,Err() 方法将返回一个非nil 的值,我们需要调用这个方法来找出在调用Next() 时是否有任何错误。这将在第24行完成。

在第18行,我们创建了一个类型为。18行,我们创建了product 类型的prd 来存储结果。

如果你不记得了,product 结构有以下字段。

type product struct {  
    name  string
    price int
}

在第18行,我们创建了,以存储结果。prd 19,结果列被复制到nameprice 结构的字段中,在第 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)
    }

我们将9001000 作为最低和最高价格传递给第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…