SQL merujuk kepada "Bahasa Pertanyaan Berstruktur", iaitu bahasa untuk mengendalikan pangkalan data, termasuk mencipta pangkalan data, memadam pangkalan data, menanya rekod, mengubah suai rekod, menambah medan, dsb. SQL ialah bahasa standard untuk pangkalan data hubungan Semua sistem pengurusan pangkalan data hubungan (RDBMS), seperti MySQL, Oracle, SQL Server, MS Access, Sybase, Informix, Postgres, dll., menggunakan SQL sebagai bahasa pemprosesan standard mereka.
Persekitaran pengendalian tutorial ini: sistem Windows 7, GO versi 1.18, komputer Dell G3.
Apakah itu SQL
SQL ialah bahasa untuk mengendalikan pangkalan data, termasuk mencipta pangkalan data, memadam pangkalan data, menyoal rekod, mengubah suai rekod dan menambah padang dll. Walaupun SQL ialah bahasa yang diseragamkan oleh ANSI, ia mempunyai banyak pelaksanaan yang berbeza.
SQL ialah singkatan dari Structured Query Language, yang diterjemahkan ke dalam bahasa Cina sebagai "Structured Query Language". SQL ialah bahasa komputer yang digunakan untuk menyimpan, mendapatkan semula dan mengubah suai data yang disimpan dalam pangkalan data hubungan.
SQL ialah bahasa standard untuk pangkalan data hubungan (RDBMS), seperti MySQL, Oracle, SQL Server, MS Access, Sybase, Informix, Postgres, dll., menggunakan SQL sebagai standard mereka. Bahasa pemprosesan.
Penggunaan SQL
SQL popular kerana penggunaannya:
Benarkan akses pengguna Data dalam sistem pangkalan data hubungan ;
membolehkan pengguna menerangkan data
membolehkan pengguna mentakrifkan data dalam pangkalan data dan memproses data
Membenarkan modul SQL, pustaka atau prapemproses dibenamkan ke dalam bahasa pengaturcaraan yang lain; 🎜>
Pustaka standard bahasa Go menyediakan perpustakaan sql untuk operasi pangkalan data , anda boleh menggunakan bahasa SQL untuk mengendalikan pangkalan data.
1.1 Sambungan
go get -u github.com/go-sql-driver/mysql
func Open(driverName, dataSourceName string) (*DB, error)
dataSourceName menentukan sumber data , yang secara amnya termasuk sekurang-kurangnya nama fail pangkalan data dan maklumat lain yang diperlukan untuk sambungan.
1.1.3 Memulakan sambunganimport ( "database/sql" _ "github.com/go-sql-driver/mysql" ) func main() { // DSN:Data Source Name dsn := "user:password@tcp(127.0.0.1:3306)/dbname" db, err := sql.Open("mysql", dsn) if err != nil { panic(err) } defer db.Close() // 注意这行代码要写在上面err判断的下面 }
panggil kaedah Ping
1.1.4SetMaxOpenConnspackage main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) //需要注意 这里需要引用自己的mysql文件 var db *sql.DB func initDB()(err error) { //账号 密码 端口号(tcp)127.0.0.1:3306 表名 字符集 校验时间 dsn := "root:123456@tcp(127.0.0.1:3306)/gomysql?charset=utf8mb4&parseTime=true" //加载驱动 //这里需要是=而不是:=因为我们是给全局变量(db)赋值 db,err = sql.Open("mysql",dsn) if err!=nil { return err } //尝试和数据库建立连接(校验dsn正确) //然后用了ping命令 err=db.Ping() if err!=nil { return err } return nil } func main() { err := initDB() if err!=nil { fmt.Printf("connect failed,err:%v\n",err) return } }
, SetMaxOpenConns
bilangan maksimum sambungan melahu akan dikurangkan untuk memadankan had bilangan maksimum sambungan terbuka. Jika n<=0, bilangan maksimum sambungan terbuka tidak akan dihadkan Lalainya ialah 0 (tiada had)1.1.5SetMaxIdleConns<. 🎜>
func (db *DB) SetMaxIdleConns(n int)
, bilangan maksimum sambungan melahu baharu akan dikurangkan Cukup kecil untuk sepadan dengan had bilangan maksimum sambungan terbuka. Jika
n<=0, sambungan melahutidak akan dikekalkan. 1.2CRUD
sql_test
Laksanakan arahan berikut untuk mencipta jadual data untuk ujian:
CREATE DATABASE sql_test;
use sql_test;
1.2.2 Pertanyaan
CREATE TABLE `user` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT '', `age` INT(11) DEFAULT '0', PRIMARY KEY(`id`) )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
db.QueryRow()
func (db *DB) QueryRow(query string, args ...interface{}) *Row
func queryRowDemo() { sqlStr := "select id, name, age from user where id=?" var u user // 非常重要:确保QueryRow之后调用Scan方法,否则持有的数据库链接不会被释放 err := db.QueryRow(sqlStr, 1).Scan(&u.id, &u.name, &u.age) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age) }
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
// 查询多条数据示例 func queryMultiRowDemo() { sqlStr := "select id, name, age from user where id > ?" rows, err := db.Query(sqlStr, 0) if err != nil { fmt.Printf("query failed, err:%v\n", err) return } // 非常重要:关闭rows释放持有的数据库链接 defer rows.Close() // 循环读取结果集中的数据 for rows.Next() { var u user err := rows.Scan(&u.id, &u.name, &u.age) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age) } }
Kod sampel khusus untuk memasukkan data adalah seperti berikut: Exec
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
1.2.4 Kemas kini data
// 插入数据 func insertRowDemo() { sqlStr := "insert into user(name, age) values (?,?)" ret, err := db.Exec(sqlStr, "王五", 38) if err != nil { fmt.Printf("insert failed, err:%v\n", err) return } theID, err := ret.LastInsertId() // 新插入数据的id if err != nil { fmt.Printf("get lastinsert ID failed, err:%v\n", err) return } fmt.Printf("insert success, the id is %d.\n", theID) }
// 更新数据 func updateRowDemo() { sqlStr := "update user set age=? where id = ?" ret, err := db.Exec(sqlStr, 39, 3) if err != nil { fmt.Printf("update failed, err:%v\n", err) return } n, err := ret.RowsAffected() // 操作影响的行数 if err != nil { fmt.Printf("get RowsAffected failed, err:%v\n", err) return } fmt.Printf("update success, affected rows:%d\n", n) }
具体删除数据的示例代码如下:
// 删除数据 func deleteRowDemo() { sqlStr := "delete from user where id = ?" ret, err := db.Exec(sqlStr, 3) if err != nil { fmt.Printf("delete failed, err:%v\n", err) return } n, err := ret.RowsAffected() // 操作影响的行数 if err != nil { fmt.Printf("get RowsAffected failed, err:%v\n", err) return } fmt.Printf("delete success, affected rows:%d\n", n) }
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) // 定义一个全局对象db var db *sql.DB // 定义一个初始化数据库的函数 func initDB() (err error) { // DSN:Data Source Name dsn := "root:123456@tcp(127.0.0.1:3306)/sql_test?charset=utf8&parseTime=True" // 不会校验账号密码是否正确 // 注意!!!这里不要使用:=,我们是给全局变量赋值,然后在main函数中使用全局变量db db, err = sql.Open("mysql", dsn) if err != nil { return err } // 尝试与数据库建立连接(校验dsn是否正确) err = db.Ping() if err != nil { return err } return nil } type user struct { id int age int name string } func queryRowDemo() { sqlStr := "select id, name, age from user where id=?" var u user // 非常重要:确保QueryRow之后调用Scan方法,否则持有的数据库链接不会被释放 err := db.QueryRow(sqlStr, 1).Scan(&u.id, &u.name, &u.age) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age) } // 查询多条数据示例 func queryMultiRowDemo() { sqlStr := "select id, name, age from user where id > ?" rows, err := db.Query(sqlStr, 0) if err != nil { fmt.Printf("query failed, err:%v\n", err) return } // 非常重要:关闭rows释放持有的数据库链接 defer rows.Close() // 循环读取结果集中的数据 for rows.Next() { var u user err := rows.Scan(&u.id, &u.name, &u.age) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age) } } func insertRowDemo() { sqlStr := "insert into user(name,age) value (?,?)" // ret,err := db.Exec(sqlStr,"王五",40) if err!=nil { fmt.Printf("inserf failed,err:%v\n",err) return } //插入成功之后需要返回这个id theID,err:=ret.LastInsertId() if err != nil{ fmt.Printf("get the last insertid failed,err:%v\n",theID) return } fmt.Printf("insert success,theID is:%v\n",theID) } func updateRowDemo() { sqlStr := "update user set name =? where id = ?" //执行含有sqlStr参数的语句 ret,err:=db.Exec(sqlStr,"赵四",4) if err!=nil { fmt.Printf("update failed,err:%v\n",err) return } AnoID,err:=ret.RowsAffected() if err!=nil { fmt.Printf("updateRowAffected failed,err:%v\n",err) return } fmt.Printf("update success AnoID:%v\n",AnoID) } // 删除数据 func deleteRowDemo() { sqlStr := "delete from user where id = ?" ret, err := db.Exec(sqlStr, 5) if err != nil { fmt.Printf("delete failed, err:%v\n", err) return } n, err := ret.RowsAffected() // 操作影响的行数 if err != nil { fmt.Printf("get RowsAffected failed, err:%v\n", err) return } fmt.Printf("delete success, affected rows:%d\n", n) } func main() { err := initDB() // 调用输出化数据库的函数 if err != nil { fmt.Printf("init db failed,err:%v\n", err) return } //queryRowDemo() //insertRowDemo() //updateRowDemo() deleteRowDemo() queryMultiRowDemo() }
普通SQL语句执行过程:
客户端对SQL语句进行占位符替换得到完整的SQL语句。
客户端发送完整SQL语句到MySQL服务端
MySQL服务端执行完整的SQL语句并将结果返回给客户端。
预处理执行过程:
把SQL语句分成两部分,命令部分与数据部分。
先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。
然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。
MySQL服务端执行完整的SQL语句并将结果返回给客户端
优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本。
避免SQL注入问题。
func (db *DB) Prepare(query string) (*Stmt, error)
查询操作的预处理示例代码如下:
// 预处理查询示例 func prepareQueryDemo() { sqlStr := "select id, name, age from user where id > ?" stmt, err := db.Prepare(sqlStr) if err != nil { fmt.Printf("prepare failed, err:%v\n", err) return } defer stmt.Close() rows, err := stmt.Query(0) if err != nil { fmt.Printf("query failed, err:%v\n", err) return } defer rows.Close() // 循环读取结果集中的数据 for rows.Next() { var u user err := rows.Scan(&u.id, &u.name, &u.age) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age) } }
插入、更新和删除操作的预处理十分类似,这里以插入操作的预处理为例:
// 预处理插入示例 func prepareInsertDemo() { sqlStr := "insert into user(name, age) values (?,?)" stmt, err := db.Prepare(sqlStr) if err != nil { fmt.Printf("prepare failed, err:%v\n", err) return } defer stmt.Close() _, err = stmt.Exec("小王子", 18) if err != nil { fmt.Printf("insert failed, err:%v\n", err) return } _, err = stmt.Exec("沙河娜扎", 18) if err != nil { fmt.Printf("insert failed, err:%v\n", err) return } fmt.Println("insert success.") }
我们任何时候都不应该自己拼接SQL语句!
// sql注入示例 func sqlInjectDemo(name string) { sqlStr := fmt.Sprintf("select id, name, age from user where name='%s'", name) fmt.Printf("SQL:%s\n", sqlStr) var u user err := db.QueryRow(sqlStr).Scan(&u.id, &u.name, &u.age) if err != nil { fmt.Printf("exec failed, err:%v\n", err) return } fmt.Printf("user:%#v\n", u) }
此时以下输入字符串都可以引发SQL注入问题:
sqlInjectDemo("xxx' or 1=1#") sqlInjectDemo("xxx' union select * from user #") sqlInjectDemo("xxx' and (select count(*) from user) <10 #")
数据库 | 占位符语法 |
---|---|
MySQL | <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">?</span> |
PostgreSQL | $1 , $2 等 |
SQLite | ? 和$1 |
Oracle | <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">:name</span> |
Atas ialah kandungan terperinci Apakah sql dalam bahasa go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!