Можно ли получить значение столбца по имени с помощью GoLang database/sql
все примеры, которые я видел за использование sql.Row
, доступ к возвращаемым значениям из запросов по позиция:sql.Rows.scan()
требуется правильно введенная переменная правильно расположенный на scan()
аргументы, соответствующие соответствующему столбцу, чтобы получить каждое возвращаемое значение столбца, например, в следующем примере:
пример на основе GoDocs (с небольшим модом):
rows, err := db.Query("SELECT name,age FROM users WHERE age>=50")
if err != nil {
log.Fatal(err)
}
for rows.Next() {
var name string
var age int
if err := rows.Scan(&name,&age); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %dn", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
&name
и &age
должно быть расположено правильно (столбцы 0 и 1) для строк.Scan (), чтобы получить правильные значения с правильными типами.
за годы разработки для производственных систем я старательно избегал этой практики, потому что она не является надежной: изменение базы данных в макете столбцов легко сломает ваш код, если он основан на позициях столбцов.
он гораздо более надежен в использовании имена столбцов для извлечения значений-это изолирует вы из изменений в базе данных, которые добавляют или удаляют столбцы, которые искажают код на основе позиции. Например, в Delphi и C#, все наборы данных, включая столбцы, возвращающие значения из запросов, поддержка FieldByName('age').asInteger
или fields['age'].value,
etc.
любой способ сделать это в Go? Если нет, это большой недостаток в поддержке базы данных Go и серьезное разочарование - совсем не безопасно, как упоминалось.
Edit:
также (возможно, это новый вопрос): примеры, которые я видел, похоже, требуют, чтобы вы извлекли все столбцы, возвращаемые запросом, или позиции столбцов будут искажены.
предположим, что в заблокированной базе данных есть запрос утилиты, который я не могу изменить или добавить, и он извлекает несколько столбцов, но мне нужен только один из них для моей текущей задачи. На основе текущего sql.Rows.Scan()
модель, я должен получить все значения из запроса в моем коде приложения, даже если они мне не нужны, тогда как если бы я мог запросить "columnByName"
это не было бы необходимо - я мог бы просто ввести в свой код приложения данные, которые мне нужны. Что-нибудь для этого придумали?
2 ответов
Да, это можно сделать без необходимости вручную сопоставлять позиции столбцов. Для этого можно использовать сторонние библиотеки, такие как sqlx или горп. Я бы рекомендовал придерживаться одного из них, а не сворачивать свой собственный.
именованное сопоставление имеет небольшой штраф. Именованное сопоставление ничем не отличается от сопоставления позиций столбцов самостоятельно. Он просто делает эту работу для вас во время выполнения - возможно, на каждом запросе исполнение. Это верно для любого другого языка.
почему во время выполнения? Запрос записывается в виде строки. Он должен быть проанализирован, чтобы определить положение.
Если вы должны были сделать свою собственную библиотеку, как вы делаете это самостоятельно?
- строк.Колонки чтобы получить имена столбцов и позиции.
передача ломтика указателей
[]interface{}
to строк.Scan получить ценности.отразить.Значение и значение.Addr чтобы получить указатель на целевое значение.
-
значение.FieldByName для получения
Value
поля структуры, если вы хотите сопоставить поля структуры.
Ok, так что давайте посмотрим, как это работает.
type Person struct {
Id int
Name string
}
rows, err := db.Query("SELECT id, name FROM person;")
if err != nil {
// handle err
log.Fatal(err)
}
columnNames, err := rows.Columns() // []string{"id", "name"}
if err != nil {
// handle err
log.Fatal(err)
}
people = make([]Person, 0, 2)
for rows.Next() {
person := Person{}
// person == Person{0, ""}
pointers := make([]interface{}, len(columnNames))
// pointers == `[]interface{}{nil, nil}`
structVal := reflect.ValueOf(person)
for i, colName := range columnNames {
fieldVal := structVal.FieldByName(strings.Title(colName))
if !fieldVal.IsValid() {
log.Fatal("field not valid")
}
pointers[i] = fieldVal.Addr().Interface()
}
// pointers == `[]interface{}{&int, &string}`
err := rows.Scan(pointers...)
if err != nil {
// handle err
log.Fatal(err)
}
// person == Person{1, "John Doe"}
people = append(people, person)
}
единственный разумный и чистый способ сделать это-использовать:https://github.com/jmoiron/sqlx
Допустим, у вас есть структура места:
type Place struct {
Country string
City sql.NullString
TelephoneCode int `db:"telcode"`
}
Вы проверить его легко:
rows, err := db.Queryx("SELECT * FROM place")
for rows.Next() {
var p Place
err = rows.StructScan(&p)
}
Подробнее: http://jmoiron.github.io/sqlx/