在同一个函数中对Postgres进行多个查询。

huangapple go评论62阅读模式
英文:

Multiple queries to Postgres within the same function

问题

我是Go的新手,所以提前对愚蠢的问题表示抱歉!

我正在使用Gin框架,并且想在同一个处理程序中对数据库进行多个查询(使用database/sql + lib/pq)。

userIds := []int{}
bookIds := []int{}
var id int

/* 在此处理第一个查询 */
rows, err := pgClient.Query(getUserIdsQuery)
defer rows.Close()
if err != nil {
	return
}
for rows.Next() {
	err := rows.Scan(&id)
	if err != nil {
		return
	}
	userIds = append(userIds, id)
}

/* 在此处理第二个查询 */
rows, err = pgClient.Query(getBookIdsQuery)
defer rows.Close()
if err != nil {
	return
}
for rows.Next() {
	err := rows.Scan(&id)
	if err != nil {
		return
	}
	bookIds = append(bookIds, id)
}

对于这段代码,我有几个问题(欢迎任何改进和最佳实践):

1)在这种情况下,Go是否正确处理defer rows.Close()?我的意思是,我在代码后面重新分配了rows变量,所以编译器会跟踪两者并在函数末尾正确关闭吗?

2)在rows.Next()循环中,重复使用id共享变量是否可以?还是应该在迭代时重新声明它?

3)在一个处理程序中有更多查询的更好方法是什么?我应该使用某种接受查询和切片并填充检索到的id的Writer吗?

谢谢。

英文:

I'm new to Go, so sorry for the silly question in advance!

I'm using Gin framework and want to make multiple queries to the database within the same handler (database/sql + lib/pq)

userIds := []int{}
bookIds := []int{}
var id int

/* Handling first query here */
rows, err := pgClient.Query(getUserIdsQuery)
defer rows.Close()
if err != nil {
	return
}
for rows.Next() {
	err := rows.Scan(&id)
	if err != nil {
		return
	}
	userIds = append(userIds, id)
}

/* Handling second query here */
rows, err = pgClient.Query(getBookIdsQuery)
defer rows.Close()
if err != nil {
	return
}
for rows.Next() {
	err := rows.Scan(&id)
	if err != nil {
		return
	}
	bookIds = append(bookIds, id)
}

I have a couple of questions regarding this code (any improvements and best practices would be appreciated)

  1. Does Go properly handle defer rows.Close() in such a case? I mean I have reassignment of rows variable later down the code, so will compiler track both and properly close at the end of a function?

  2. Is it ok to reuse id shared var or should I redeclare it while iterating within rows.Next() loop?

  3. What's the better approach of having even more queries within one handler? Should I have some kind of Writer that accepts query and slice and populate it with ids retrieved?

Thanks.

答案1

得分: 1

我从未使用过go-pg库,我的回答主要集中在其他方面,这些内容是通用的,与golang或go-pg无关。

    1. 不管rows在这里是否具有相同的引用,在两个查询之间共享(因此只需要一个rows.Close()调用,除非库有一些特殊的实现),定义两个变量更清晰,比如userRowsbookRows
    1. 尽管我已经说过我没有使用过go-pg,但我相信你不需要手动迭代行并手动扫描所有行的id,我相信该库已经提供了一些类似的API(根据快速查看文档):
    userIds := []int{}
    err := pgClient.Query(&userIds, "select id from users where ...", args...)
    
    1. 关于你的第二个问题,这取决于你对“ok”的理解。由于你正在进行一些同步迭代,我认为不会导致错误,但从编码风格的角度来看,我个人不会这样做。
    1. 我认为在你的情况下,最好的做法是这样的:
    // repo层
    func getUserIds(args whatever) ([]int, error) {...}
    // 根据你的打包逻辑,这些可以暴露出去
    func getBookIds(args whatever) ([]int, error) {...}
    
    // service层,或者你想要聚合两个查询的任何地方
    func getUserAndBookIds() ([]int, []int, error) {
        userIds, err := getUserIds(...)
        // 潜在的错误处理
        bookIds, err := getBookIds(...)
        // 潜在的错误处理
        return userIds, bookIds, nil // 之前已经处理了错误
    }
    

    我认为这段代码更易于阅读和维护。你不会遇到变量重新赋值和其他问题。

你可以查看go-pg文档以获取有关如何改进查询的更多详细信息。

英文:

I've never worked with go-pg library, and my answer is mostly focused on the other stuff, which are generic, and are not specific to golang or go-pg.

    1. Regardless of the fact that the rows here has the same reference while being shared between 2 queries (so one rows.Close() call would suffice, unless the library has some special implementation), defining two variables is cleaner, like userRows and bookRows.
    1. Although I already said that I have not worked with go-pg, I believe that you wont need to iterate through rows and scan the id for all the rows manually, I believe that the lib has provided some API like this (based on the quick look on the documentations):
    userIds := []int{}
    err := pgClient.Query(&userIds, "select id from users where ...", args...)
    
    1. Regarding your second question, it depends on what you mean by "ok". Since your doing some synchronous iteration, I don't think it would result into bugs, but when it comes to coding style, personally, I wouldn't do this.
    1. I think that the best thing to do in your case is this:
    // repo layer
    func getUserIds(args whatever) ([]int, err) {...}
    // these can be exposed, based on your packaging logic
    func getBookIds(args whatever) ([]int, err) {...}
    
    // service layer, or wherever you want to aggregate both queries
    func getUserAndBookIds() ([]int, []int, err) {
        userIds, err := getUserIds(...)
        // potential error handling
        bookIds, err := getBookIds(...)
        // potential error handling
        return userIds, bookIds, nil // you have done err handling earlier
    }
    

    I think this code is easier to read/maintain. You won't face the variable reassignment and other issues.

You can take a look at the go-pg documentations for more details on how to improve your query.

huangapple
  • 本文由 发表于 2022年7月14日 20:49:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/72980796.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定