layout | title |
---|---|
article |
Получение наборов результатов |
Существуют несколько типичных операций для получения данных из хранилища.
Имена функций пакета database/sql
Go имеют важное значение. Если имя функции включает Query
, это значит, что она предназначена для отправки запроса базе данных и вернёт множество строк, даже если они пусты. Заявки, которые не возвращают строки, должны использовать функции Exec()
, а не Query
.
Рассмотрим пример того, как выполнять запрос к базе данных и работать с результатами. Мы запросим таблицу users
за пользователя, чей id
равен 1, и выведем его id
и name
. Будем присваивать результаты переменным, строка за строкой, используя rows.Scan()
.
var (
id int
name string
)
rows, err := db.Query("select id, name from users where id = ?", 1)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&id, &name)
if err != nil {
log.Fatal(err)
}
log.Println(id, name)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
Вот что происходит в вышеуказанном коде:
db.Query()
для отправки запроса в базу данных. Проверяем ошибки, как обычно.rows.Close()
. Это очень важно.rows.Next()
.rows.Scan()
.Это практически единственный способ сделать это в Go. Например, вы не можете получить строку как карту значений. Это потому, что всё строго типизировано. Вам нужно создать переменные правильного типа и передать указатели на них, как показано.
Несколько частей этого легко запутаться и могут иметь неблагоприятные последствия.
for rows.Next()
. Если возникает ошибка во время цикла, вам нужно знать об этом. Не просто предполагайте, что цикл продолжается до тех пор, пока все строки не будут обработаны.rows
), нижележащее соединение занято и не может использоваться для других запросов. Это означает, что оно недоступно в пуле соединений. Если вы пройдёте через все строки с помощью rows.Next()
, рано или поздно вы прочитаете последнюю строку, и rows.Next()
встретит внутреннюю ошибку конца файла и закроет rows.Close()
для вас. Но если по какой-либо причине вы покидаете этот цикл — ранний выход или что-то ещё — то rows
не будет закрыт, и соединение останется открытым. (Оно автоматически закрывается, если rows.Next()
возвращает ложь из-за ошибки). Это простой способ исчерпания ресурсов.rows.Close()
является безопасной операцией, которая ничего не делает, если rows
уже закрыта, поэтому вы можете вызывать её несколько раз. Обратите внимание, однако, что мы сначала проверяем ошибки и только затем вызываем rows.Close()
, чтобы избежать паники во время выполнения.defer rows.Close()
, даже если вы также явно вызываете rows.Close()
в конце цикла, что не так уж плохо.defer
внутри цикла. Отложенный вызов выполняется только при выходе из функции, поэтому длинноиграющая функция не должна использовать его. Если вы сделаете это, вы будете медленно накапливать память. Если вы повторно запрашиваете и потребляете наборы результатов внутри цикла, вы должны явно вызвать rows.Close()
каждый раз, когда закончите с каждым результатом, и не использовать defer
.Scan()
Когда вы проходите по строкам и сканируете их в целевые переменные, Go выполняет преобразования типов данных за вас, в фоновом режиме. Это основано на типе целевой переменной. Быть осведомлённым об этом может очистить ваш код и помочь избежать повторяющейся работы.
Например, предположим, что вы выбираете некоторые строки из таблицы, определённой с колонками типа строк, таких как VARCHAR(45)
или подобные. Однако вы знаете, что таблица всегда содержит числа. Если вы передадите указатель на строку, Go скопирует байты в строку. Теперь вы можете использовать strconv.ParseInt()
или подобное для преобразования значения в число. Вам придётся проверять ошибки в операциях SQL, а также ошибки парсинга целого числа. Это мешает и затрудняет работу.
Или же вы можете просто передать Scan()
указатель на целое число. Go обнаружит это и вызовет strconv.ParseInt()
за вас. Если возникнет ошибка преобразования, вызов Scan()
вернёт её. Ваш код теперь более аккуратный и компактный. Это рекомендованный способ использования database/sql
.
Общими правилами следует всегда готовить запросы для повторного использования. Результат подготовки запроса представляет собой подготовленное заявление, которое может содержать плейсхолдеры (также известные как привязанные значения) для параметров, которые вы предоставите при выполнении заявления. Это намного лучше, чем конкатенация строк, по всем обычным причинам (например, защита от атак SQL-инъекций).
В MySQL плейсхолдер представлен символом ?
, а в PostgreSQL он имеет вид $N
, где N — это номер. SQLite принимает любой из этих вариантов. В Oracle плейсхолдеры начинаются с двоеточия и имеют имена, такие как :param1
. Мы будем использовать ?
, поскольку используем MySQL в качестве нашего примера.
stmt, err := db.Prepare("select id, name from users where id = ?")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
rows, err := stmt.Query(1)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
// ...
}
if err = rows.Err(); err != nil {
log.Fatal(err)
}
```Под капотом `db.Query()` фактически готовит, выполняет и закрывает подготовленное заявление. Это три поездки к базе данных. Если вы не будете внимательны, вы можете увеличить количество взаимодействий вашего приложения с базой данных втрое! Некоторые драйверы могут избежать этого в конкретных случаях, но не все драйверы это делают. Смотрите [подготовленные заявления](prepared.html) для получения больше информации.
Запросы одной строки
=====================
Если запрос возвращает максимум одну строку, вы можете использовать сокращённый вариант для некоторых длинноватых шаблонов кода:
```go
var name string
err = db.QueryRow("select name from users where id = ?", 1).Scan(&name)
if err != nil {
log.Fatal(err)
}
fmt.Println(name)
``
Ошибки от запроса откладываются до вызова `Scan()`, и затем возвращаются оттуда. Также можно вызвать `QueryRow()` на подготовленном заявлении:
```go
stmt, err := db.Prepare("select name from users where id = ?")
if err != nil {
log.Fatal(err)
}
var name string
err = stmt.QueryRow(1).Scan(&name)
if err != nil {
log.Fatal(err)
}
fmt.Println(name)
Предыдущий: Доступ к базе данных Следующий: Модификация данных и использование транзакций
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )