oracle在此select语句中缺少into⼦句,Godatabasesql⽂档No.1 ⽂档概要
在Golang中使⽤SQL或类似SQL的数据库的惯⽤⽅法是通过 database/sql 包操作。它为⾯向⾏的数据库提供了轻量级的接⼝。这篇⽂章是关于如何使⽤它,最常见的参考。
为什么需要这个?包⽂档告诉你每件事情都做了什么,但它并没有告诉你如何使⽤这个包。我们很多⼈都希望⾃⼰能快速参考和⼊门的⽅法,⽽不是讲故事。欢迎捐款;请在这⾥发送请求。
在Golang中你⽤sql.DB访问数据库。你可以使⽤此类型创建语句和事务,执⾏查询,并获取结果。下⾯的代码列出了sql.DB是⼀个结构体,点击 database/ 查看官⽅源码。
⾸先你应该知道⼀个sql.DB不是⼀个数据库的连接。它也没有映射到任何特点数据库软件的“数据库”或“模式”的概念。它是数据库的接⼝和数据库的抽象,它可能与本地⽂件不同,可以通过⽹络连接访问,也可以在内存和进程中访问。
sql.DB为你在幕后执⾏⼀些重要的任务:
· 通过驱动程序打开和关闭实际的底层数据库的连接。
· 它根据需要管理⼀个连接池,这可能是如上所述的各种各样的事情。
sql.DB抽象旨在让你不必担⼼如何管理对基础数据存储的并发访问。⼀个连接在使⽤它执⾏任务时被标记为可⽤,然后当它不在使⽤时返回到可⽤的池中。这样的后果之⼀是,如果你⽆法将连接释放到池中,则可能导致db.SQL打开⼤量连接,可能会耗尽资源(连接太多,打开的⽂件句柄太多,缺少可⽤⽹络端⼝等)。稍后我们将进⼀步讨论这个问题。
在创建sql.DB之后,你可以⽤它来查询它所代表的数据库,以及创建语句和事务。
No.2 导⼊数据库驱动
要使⽤ database/sql,你需要 database/sql ⾃⾝,以及需要使⽤的特定的数据库驱动。
ascii码的编码规则你通常不应该直接使⽤驱动包,尽管有些驱动⿎励你这样做。(在我们看来,这通常是个坏主意。) 相反的,如果可能,你的代码应该仅引⽤database/sql 中定义的类型。这有助于避免使你的代码依赖于驱动,从⽽可以通过最少的代码来更改底层驱动(因此访问的数据库)。它还强制你使⽤Golang习惯⽤法,⽽不是特定驱动作者可能提供的特定的习惯⽤法。
在本⽂档中,我们将使⽤@julienschmidt 和 @arnehormann中优秀的MySql驱动。
将以下内容添加到Go源⽂件的顶部(也就是package name下⾯):
import (
"database/sql"
_ "github/go-sql-driver/mysql"
轮博伤号吗)
注意我们正在加载的驱动是匿名的,将其限定符别名为_,因此我们的代码中没有⼀个导出的名称可见。在引擎下,驱动将⾃⾝注册为可⽤于 database/sql 包,但⼀般来说没有其他情况发⽣。
现在你已经准备好访问数据库了。
No.3 访问数据库
现在你已经加载了驱动包,就可以创建⼀个数据库对象sql.DB。创建⼀个sql.DB你可以使⽤sql.Open()。Open返回⼀个*sql.DB。
func main() {
db, err := sql.Open("mysql",
"user:password@tcp(127.0.0.1:3306)/hello")
if err != nil {
defer db.Close()
}
在⽰例中,我们演⽰了⼏件事:
sql.Open的第⼀个参数是驱动名称。这是驱动⽤来注册database/sql的字符串,并且通常与包名相同以避免混淆。例如,它是
github/go-sql-driver/mysql的MySql驱动(作者:jmhodges)。某些驱动不遵守公约的名称,例如github/mattn/go-sqlite3的sqlite3(作者:matte)和github/lib/pq的postgres(作者:mjibson)。
第⼆个参数是⼀个驱动特定的语法,它告诉驱动如何访问底层数据存储。在本例中,我们将连接本地的MySql服务器实例中的“hello”数据库。
你应该(⼏乎)总是检查并处理从所有database/sql操作返回的错误。有⼀些特殊情况,我们稍后将讨论这样做事没有意义的。
如果sql.DB不应该超出该函数的作⽤范围,则延迟函数defer db.Close()是惯⽤的。
也许是反直觉的,sql.Open()不建⽴与数据库的任何连接,也不会验证驱动连接参数。相反,它只是准备数据库抽象以供以后使⽤。⾸次真正的连接底层数据存储区将在第⼀次需要时懒惰地建⽴。如果你想⽴即检查数据库是否可⽤(例如,检查是否可以建⽴⽹络连接并登陆),请使⽤db.Ping()来执⾏此操作,记得检查错误:
sql select sumerr = db.Ping()
if err != nil {
// do something here
}
怎么添加小程序链接
虽然在完成数据库之后Close()数据库是惯⽤的,但是sql.DB对象被设计为长连接。不要经常Open()和Close()数据库。相反,为你需要访问的每个不同的数据存储创建⼀个sql.DB对象,并保留它,直到程序访问数据存储完毕。在需要时传递它,或在全局范围内使其可⽤,但要保持开放。并且不要从短暂的函数中Open()和Close()。相反,通过sql.DB作为参数传递给该短暂的函数。
如果你不把sql.DB视为长期存在的对象,则可能会遇到诸如重复使⽤和连接共享不⾜,耗尽可⽤的⽹络资源以及由于TIME_WAIT中剩余⼤量TCP连接⽽导致的零星故障的状态。这些问题表明你没有像设
计的那样使⽤database/sql的迹象。
现在是时候使⽤你的sql.DB对象了。
No.4 检索结果集
有⼏个惯⽤的操作来从数据存储中检索结果。
执⾏返回⾏的查询。
准备重复使⽤的语句,多次执⾏并销毁它。
以⼀次关闭的⽅式执⾏语句,不准备重复使⽤。
执⾏⼀个返回单⾏的查询。这种特殊情况有⼀个捷径。
Golang的database/sql函数名⾮常重要。如果⼀个函数名包含查询Query(),它被设计为询问数据库的问题,并返回⼀组⾏,即使它是空的。不返回⾏的语句不应该使⽤Query()函数;他们应该使⽤Exec()。
从数据库获取数据
让我们来看⼀下如何查询数据库,使⽤Query的例⼦。我们将向⽤户表查询id为1的⽤户,并打印出⽤户的id和name。我们将使⽤
rows.Scan()将结果分配给变量,⼀次⼀⾏。
var (
id int
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()将查询发送到数据库。我们像往常⼀样检查错误。
我们⽤defer内置函数推迟了rows.Close()的执⾏。这个⾮常重要。
我们⽤rows.Next()遍历了数据⾏。
我们⽤rows.Scan()读取每⾏中的列变量。
我们完成遍历⾏之后检查错误。
这⼏乎是Golang中唯⼀的办法。例如,你不能将⼀⾏作为映射来获取。这是因为所有东西都是强类型的。你需要创建正确类型的变量并将指针传递给它们,如图所⽰。
其中的⼏个部分很容易出错,可能会产⽣不良后果。
· 你应该总是检查rows.Next()循环结尾处的错误。如果循环中出现错误,则需要了解它。不要仅仅假设循环遍历,直到你已经处理了所有的⾏。
· 第⼆,只要有⼀个打开的结果集(由⾏代表),底层连接就很忙,不能⽤于任何其他查询。这意味着它在连接池中不可⽤。如果你使⽤rows.Next()遍历所有⾏,最终将读取最后⼀⾏,rows.Next()将遇到内部EOF错误,并为你调⽤rows.Close()。但是,如果由于某种原因退出该循环-提前返回,那么⾏不会关闭,并且连接保持打开状态。(如果rows.Next()由于错误⽽返回false,则会⾃动关闭)。这是⼀种简单耗尽资源的⽅法。
· rows.Close()是⼀种⽆害的操作,如果它已经关闭,所以你可以多次调⽤它。但是请注意,我们⾸先检查错误,如果没有错误,则调⽤rows.Close(),以避免运⾏时的panic。
· 你应该总是⽤延迟语句defer推迟rows.Close(),即使你也在循环结束时调⽤rows.Close(),这不是⼀个坏主意。
· 不要在循环中⽤defer推迟。延迟语句在函数退出之前不会执⾏,所以长时间运⾏的函数不应该使⽤它。如果你这样做,你会慢慢积累记忆。如果你在循环中反复查询和使⽤结果集,则在完成每个结果后应显⽰的调⽤rows.Close(),⽽不⽤延迟语句defer。
Scan()如何⼯作
当你遍历⾏并将其扫描到⽬标变量中时,Golang会在幕后为你执⾏数据类型转换。它基于⽬标变量的类型。意识到这⼀点可以⼲净你的代码,并帮助避免重复⼯作。
例如,假设你从表中选择了⼀些⾏,这是⽤字符串列定义的。如varchar(45)或类似的列。然⽽,你碰巧知道表格总是包含数字。如果传递指向字符串的指针,Golang会将字节复制到字符串中。现在可以使⽤strconv.ParseInt()或类似的⽅式将值转换为数字。你必须检查SQL 操作中的错误以及解析整数的错误。这⼜乱⼜糟糕。
或者,你可以通过Scan()指向⼀个整数即可。Golang会检测到并为你调⽤strconv.ParseInt()。如果有转换错误,则调⽤Scan()将返回它。你的代码现在更⼩更整洁。这是推荐使⽤database/sql的⽅法。
准备查询
⼀般来说,你应该总是准备多次使⽤查询。准备查询的结果是⼀个准备语句,可以为执⾏语句时提供的参数,提供占位符(a.k.a bind值)。这⽐连接字符串更好,出于所有通常的理由(例如避免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 statements查看更多声明。
单⾏查询
如果⼀个查询返回最多⼀⾏,可以使⽤⼀些快速的样板代码:
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():
mysql语句转oracle
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)
No.5 修改数据和使⽤事务
现在我们已经准备好了如何修改数据和处理事务。如果你习惯于使⽤“statement”对象来获取⾏并更新数据,那么这种区别可能视乎是认为的,但是在Golang中有⼀个重要的原因。
修改数据的statements
使⽤Exec(),最好⽤⼀个准备好的statement来完成INSERT,UPDATE,DELETE或者其他不返回⾏的语句。下⾯的⽰例演⽰如何插⼊⾏并检查有关操作的元数据:
linux操作系统在日常生活中的应用论文
stmt, err := db.Prepare("INSERT INTO users(name) VALUES(?)")
if err != nil {
log.Fatal(err)
}
res, err := stmt.Exec("Dolly")
if err != nil {
log.Fatal(err)
}
lastId, err := res.LastInsertId()
if err != nil {
log.Fatal(err)
}
rowCnt, err := res.RowsAffected()
if err != nil {

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。