Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Database 如何使中的扫描DB行变干?_Database_Go - Fatal编程技术网

Database 如何使中的扫描DB行变干?

Database 如何使中的扫描DB行变干?,database,go,Database,Go,我在数据库中有一个包含用户帐户信息的表。我有一个名为User defined的结构 type User struct { Id uint Username string Password string FirstName string LastName string Address1 string Address2 string .... a bunch more fields ... } 对于获取单个用户帐户,我定义了一个方法 func

我在数据库中有一个包含用户帐户信息的表。我有一个名为User defined的结构

type User struct {
  Id        uint
  Username  string
  Password  string
  FirstName string
  LastName  string
  Address1  string
  Address2  string
  .... a bunch more fields ...
}
对于获取单个用户帐户,我定义了一个方法

func (user *User) GetById(db *sql.DB, id uint) error {
  query := `SELECT 
            ...a whole bunch of SQL ...
            WHERE id = $1
            ... more SQL ...
            LIMIT 1`
  row := db.QueryRow(query, id)
  err := row.Scan(
    &user.Id,
    &user.UserName,
    &user.Password,
    &user.FirstName,
    &user.LastName,
    ... some 20 more lines of fields read into the struct ...
  )
  if err != nil {
    return err
  }
  return nil
}
系统中有几个地方需要获取用户信息,作为更大查询的一部分。也就是说,我正在获取一些其他类型的对象,但也获取与之相关的用户帐户

这意味着,我必须一遍又一遍地重复整个
行。扫描(&user.Username,&user…
这件事,它占用了整个页面,而且很容易出错,如果我更改用户表结构,我将不得不在很多地方更改代码。我怎样才能使这个更干

编辑:我不确定为什么会将其标记为重复,但由于需要进行此编辑,我将尝试再次解释。我不是问如何将一行扫描到结构中。正如上面的代码清楚地显示的那样,我已经知道如何做到这一点。我在问如何构造结构扫描代码,这样我就不必每次扫描同一类型的结构时都重复同一页的扫描代码

编辑:还有,是的,我知道sqlstruct和sqlx以及类似的库。我故意避免这些问题,因为它们依赖于具有良好记录的性能问题的reflect包。我打算使用这些技术扫描数百万行(不是数百万用户,但这个问题扩展到了其他记录类型)

编辑:所以,是的,我知道我应该写一个函数。我不确定这个函数应该取什么作为参数,它应该返回什么结果。假设我要容纳的另一个查询如下所示

SELECT
    s.id,
    s.name,
    ... more site fields ...
    u.id,
    u.username,
    ... more user fields ...
FROM site AS s
JOIN user AS u ON (u.id = s.user_id)
JOIN some_other_table AS st1 ON (site.id = st1.site_id)
... more SQL ...

我有一个站点结构方法,它嵌入了一个用户结构。我不想在这里重复用户扫描代码。我想调用一个函数,将raw的用户部分扫描到用户结构中,方法与上面的用户方法相同。

为了避免重复扫描
*sql.Rows
结构所需的步骤,可以引入两个接口。描述
*sql.Rows
*sql.Row
已经实现的行为的代码

// This interface is already implemented by *sql.Rows and *sql.Row.
type Row interface {
    Scan(...interface{}) error
}
另一个是抽象出行的实际扫描步骤

RowScanner接口的示例实现如下所示:

type User struct {
    Id       uint
    Username string
    // ...
}

// Implements RowScanner
func (u *User) ScanRow(r Row) error {
    return r.Scan(
        &u.Id,
        &u.Username,
        // ...
    )
}

type UserList struct {
    Items []*User
}

// Implements RowScanner
func (list *UserList) ScanRow(r Row) error {
    u := new(User)
    if err := u.ScanRow(r); err != nil {
        return err
    }
    list.Items = append(list.Items, u)
    return nil
}
有了这些接口,您现在可以通过使用这两个函数为实现RowScanner接口的所有类型烘干行扫描代码

func queryRows(query string, rs RowScanner, params ...interface{}) error {
    rows, err := db.Query(query, params...)
    if err != nil {
        return err
    }
    defer rows.Close()

    for rows.Next() {
        if err := rs.ScanRow(rows); err != nil {
            return err
        }
    }
    return rows.Err()
}

func queryRow(query string, rs RowScanner, params ...interface{}) error {
    return rs.ScanRow(db.QueryRow(query, params...))
}

// example
ulist := new(UserList)
if err := queryRows(queryString, ulist, arg1, arg2); err != nil {
    panic(err)
}

// or
u := new(User)
if err := queryRow(queryString, u, arg1, arg2); err != nil {
    panic(err)
}

如果您有要扫描的复合类型,但希望避免重复枚举其元素的字段,则可以引入一个返回类型字段的方法,并在需要时重用该方法。例如:

func (u *User) ScannableFields() []interface{} {
    return []interface{}{
        &u.Id,
        &u.Username,
        // ...
    }
}

func (u *User) ScanRow(r Row) error {
    return r.Scan(u.ScannableFields()...)
}

// your other entity type
type Site struct {
    Id   uint
    Name string
    // ...
}

func (s *Site) ScannableFields() []interface{} {
    return []interface{}{
        &p.Id,
        &p.Name,
        // ...
    }
}

// Implements RowScanner
func (s *Site) ScanRow(r Row) error {
    return r.Scan(s.ScannableFields()...)
}

// your composite
type UserWithSite struct {
    User *User
    Site *Site
}

// Implements RowScanner
func (u *UserWithSite) ScanRow(r Row) error {
    u.User = new(User)
    u.Site = new(Site)
    fields := append(u.User.ScannableFields(), u.Site.ScannableFields()...)
    return r.Scan(fields...)
}

// retrieve from db
u := new(UserWithSite)
if err := queryRow(queryString, u, arg1, arg2); err != nil {
    panic(err)
}

很好的结果似乎是我需要对Row和Rows使用两种不同的方法,因为它们是不同的类型。否则,这是一种提取行扫描的好方法。谢谢。@MadWombat我不知道你说的两种不同的方法是什么意思。“行和行”是指*sql.Row和*sql.Rows?是的,所以在接口中我想我需要实现ScanRow(Row sql.Row)和ScanRows(Rows sql.Rows)以适应查询和查询的结果。如果您定义了位于我答案顶部的接口(
键入Row interface{
),您不需要这样做,它由
*sql.Row
*sql.Rows
实现,您可以使用它作为ScanRow的参数,即
ScanRow(r行)错误
。尝试一下,当您将*sql.Row和*sql.Rows传递给ScanRow时,您的程序将很好。请记住,in-Go接口是隐式满足的,这意味着您可以为已经存在的类型定义接口,而无需修改它们。啊!我明白了。谢谢。
func (u *User) ScannableFields() []interface{} {
    return []interface{}{
        &u.Id,
        &u.Username,
        // ...
    }
}

func (u *User) ScanRow(r Row) error {
    return r.Scan(u.ScannableFields()...)
}

// your other entity type
type Site struct {
    Id   uint
    Name string
    // ...
}

func (s *Site) ScannableFields() []interface{} {
    return []interface{}{
        &p.Id,
        &p.Name,
        // ...
    }
}

// Implements RowScanner
func (s *Site) ScanRow(r Row) error {
    return r.Scan(s.ScannableFields()...)
}

// your composite
type UserWithSite struct {
    User *User
    Site *Site
}

// Implements RowScanner
func (u *UserWithSite) ScanRow(r Row) error {
    u.User = new(User)
    u.Site = new(Site)
    fields := append(u.User.ScannableFields(), u.Site.ScannableFields()...)
    return r.Scan(fields...)
}

// retrieve from db
u := new(UserWithSite)
if err := queryRow(queryString, u, arg1, arg2); err != nil {
    panic(err)
}