Golang中如何使用pgx在返回多行的查询中返回NoRowsErr

Golang中如何使用pgx在返回多行的查询中返回NoRowsErr 在我的当前项目中,我使用 pgxpool 通过以下查询语句查询数据库:“query(ctx, SELECT * FROM invoices WHERE user_id = $1, id)”。 此函数返回一个 pgx.Rows 类型,它保存查询结果值和一个错误类型。 如果存在错误,则不会返回 pgx.Rows 的值。 然而,实际情况并非如此,因为 pgx.Query() 函数对于无结果行的情况不会返回错误。 在这种情况下,函数既不返回错误,也不返回 pgx.Rows 类型的值。 只有 pgx.QueryRow() 结合 pgxscan.ScanOne() 才能为你返回 NoRowsError。 问题是 pgx.ScanOne() 只返回一行,而我想返回多行。

根据我在网上搜集的信息,我应该使用 Rows.Next() 来检查下一行是否存在,否则当没有行时它会返回 false。然而,当查询确实返回行时,我不确定应该怎么做。

我当前的实现:

func ReadInvoicesByUserID(id int) ([]*Invoice, fields.GrammarError) {
	ctx, db := bikeshop.Connect()
	defer db.Close()

	var tempInv Invoice
	var invoices []*Invoice
	_, fieldErr := ReadInvoices()

	if fieldErr.ErrMsgs != nil &&
		strings.Contains(fieldErr.ErrMsgs[0], "failed to connect to `user=username") {
		fmt.Printf("ReadInvoicesByUserID funct: error username doesn't exist")
		return nil, fieldErr
	}

	rows, err := db.Query(ctx, `SELECT * FROM invoices WHERE user_id = $1`, id)

	if !rows.Next() {
		// if no rows were found thats an NoRowsError
		if rows.Err() == nil {
			fmt.Println("Found an Error Iterating in Getting All the Invoices for the Specified User")
			fieldErr.AddMsg(fields.ResourceNotFound, "no rows in result set")
			return nil, fieldErr
		}
		return nil, fieldErr
	}

	// err := pgxscan.ScanAll(&invs, rows)

	// var err error
	for rows.Next() {
		fmt.Println("Processing Query in ReadInvoicesbyUserID")
		err = pgxscan.ScanOne(&tempInv, rows)
		if errors.Is(err, pgx.ErrNoRows) {
			fmt.Println("Found an Error Iterating in Getting All the Invoices for the Specified User")
			break
		}
		invoices = append(invoices, &tempInv)
	}

	if rows.Err() != nil {
		fmt.Println("Found an Error Iterating in Getting All

更多关于Golang中如何使用pgx在返回多行的查询中返回NoRowsErr的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

这种行为并非此包独有——任何 SQL 数据库通常都是这样实现的。如果你要求“给我一行数据”,那么在没有行的情况下就会报错。 但 SELECT * ... 是在要求“给我找到的任何行”。在大多数情况下,你并不知道会得到多少行,零行也是一个有效的结果。通常,你可以正常继续处理,并在后续流程中处理这种情况。在 Web 应用程序中,你的前端可能只会收到一个空列表作为所有发票的查询结果,并可以显示“未找到发票”。

如果你确实想在此阶段抛出错误,最简单的方法可能是在 for 循环之后,直接添加一行 if len(invoices) == 0 { return nil, fmt.Errorf(...) }

if len(invoices) == 0 { return nil, fmt.Errorf(...) }

更多关于Golang中如何使用pgx在返回多行的查询中返回NoRowsErr的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在pgx中处理多行查询的ErrNoRows情况,正确的做法是检查rows.Next()的返回值和rows.Err()。以下是修正后的实现:

func ReadInvoicesByUserID(id int) ([]*Invoice, fields.GrammarError) {
    ctx, db := bikeshop.Connect()
    defer db.Close()

    var invoices []*Invoice
    rows, err := db.Query(ctx, `SELECT * FROM invoices WHERE user_id = $1`, id)
    
    if err != nil {
        // 处理查询执行错误
        fmt.Printf("Query error: %v\n", err)
        fieldErr := fields.GrammarError{}
        fieldErr.AddMsg(fields.DatabaseError, err.Error())
        return nil, fieldErr
    }
    defer rows.Close()

    // 检查是否有行返回
    if !rows.Next() {
        // 检查是否有扫描错误
        if rows.Err() != nil {
            fmt.Printf("Rows error: %v\n", rows.Err())
            fieldErr := fields.GrammarError{}
            fieldErr.AddMsg(fields.DatabaseError, rows.Err().Error())
            return nil, fieldErr
        }
        
        // 没有错误但没有数据行
        fmt.Println("No invoices found for user")
        fieldErr := fields.GrammarError{}
        fieldErr.AddMsg(fields.ResourceNotFound, "no invoices found")
        return []*Invoice{}, fieldErr
    }

    // 处理第一行
    var inv Invoice
    err = rows.Scan(&inv.ID, &inv.UserID, &inv.Amount /* 其他字段 */)
    if err != nil {
        fmt.Printf("Scan error: %v\n", err)
        fieldErr := fields.GrammarError{}
        fieldErr.AddMsg(fields.DatabaseError, err.Error())
        return nil, fieldErr
    }
    invoices = append(invoices, &inv)

    // 处理剩余行
    for rows.Next() {
        var inv Invoice
        err = rows.Scan(&inv.ID, &inv.UserID, &inv.Amount /* 其他字段 */)
        if err != nil {
            fmt.Printf("Scan error: %v\n", err)
            fieldErr := fields.GrammarError{}
            fieldErr.AddMsg(fields.DatabaseError, err.Error())
            return nil, fieldErr
        }
        invoices = append(invoices, &inv)
    }

    // 检查迭代过程中是否有错误
    if rows.Err() != nil {
        fmt.Printf("Rows iteration error: %v\n", rows.Err())
        fieldErr := fields.GrammarError{}
        fieldErr.AddMsg(fields.DatabaseError, rows.Err().Error())
        return nil, fieldErr
    }

    return invoices, fields.GrammarError{}
}

如果你使用pgxscan简化扫描过程,可以这样实现:

func ReadInvoicesByUserID(id int) ([]*Invoice, fields.GrammarError) {
    ctx, db := bikeshop.Connect()
    defer db.Close()

    var invoices []*Invoice
    rows, err := db.Query(ctx, `SELECT * FROM invoices WHERE user_id = $1`, id)
    
    if err != nil {
        fmt.Printf("Query error: %v\n", err)
        fieldErr := fields.GrammarError{}
        fieldErr.AddMsg(fields.DatabaseError, err.Error())
        return nil, fieldErr
    }
    defer rows.Close()

    // 使用pgxscan.ScanAll
    err = pgxscan.ScanAll(&invoices, rows)
    
    if err != nil {
        fmt.Printf("ScanAll error: %v\n", err)
        fieldErr := fields.GrammarError{}
        fieldErr.AddMsg(fields.DatabaseError, err.Error())
        return nil, fieldErr
    }

    // 检查是否没有数据
    if len(invoices) == 0 {
        fmt.Println("No invoices found for user")
        fieldErr := fields.GrammarError{}
        fieldErr.AddMsg(fields.ResourceNotFound, "no invoices found")
        return []*Invoice{}, fieldErr
    }

    return invoices, fields.GrammarError{}
}

关键点:

  1. db.Query()执行成功但无数据时不会返回错误
  2. 必须调用rows.Next()来检查是否有数据行
  3. 使用rows.Err()检查扫描过程中的错误
  4. 总是使用defer rows.Close()确保资源释放
  5. 对于空结果集,返回空切片和适当的错误信息
回到顶部