SQL是指“結構化查詢語言”,是一種操作資料庫的語言,包括建立資料庫、刪除資料庫、查詢記錄、修改記錄、新增欄位等。 SQL是關聯式資料庫的標準語言,所有的關聯式資料庫管理系統(RDBMS),例如 MySQL、Oracle、SQL Server、MS Access、Sybase、Informix、Postgres 等,都將 SQL 作為其標準處理語言。
本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。
SQL是什麼
SQL 是一種操作資料庫的語言,包括建立資料庫、刪除資料庫、查詢記錄、修改記錄、添加字段等。 SQL 雖然是一種被 ANSI 標準化的語言,但它有許多不同的實作版本。
SQL 是 Structured Query Language 的縮寫,中文譯為「結構化查詢語言」。 SQL 是一種電腦語言,用來儲存、檢索和修改關係型資料庫中儲存的資料。
SQL 是關聯式資料庫的標準語言,所有的關聯式資料庫管理系統(RDBMS),例如MySQL、Oracle、SQL Server、MS Access、Sybase、Informix、Postgres 等,都將SQL 作為其標準處理語言。
SQL 的用途
SQL 之所以廣受歡迎,是因為它具有以下用途:
允許用戶訪問關係型資料庫系統中的資料;
允許使用者描述資料;
允許使用者定義資料庫中的數據,並處理該資料;
允許將SQL 模組、函式庫或預處理器嵌入到其它程式語言中;
允許使用者建立和刪除資料庫、表格、資料項(記錄);
允許使用者在資料庫中建立視圖、預存程序、函數;
允許使用者設定對錶、儲存過程和檢視的權限。
Go語言操作資料庫(MySQL)
#在Go語言標準函式庫提供了進行資料庫操作的sql 函式庫,可以利用SQL語言來操作資料庫。
go get -u github.com/go-sql-driver/mysql
func Open(driverName, dataSourceName string) (*DB, error)
Open開啟一個dirverName指定的資料庫,dataSourceName指定資料來源,一般至少包括資料庫檔案名稱和其它連接必要的資訊。
import ( "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判断的下面 }
Open函數可能只是驗證其參數格式是否正確,實際上並不建立與資料庫的連線。如果要檢查資料來源的名稱是否真實有效,應該呼叫Ping方法
package 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
設定與數據函式庫建立連線的最大數目。如果n大於0且小於最大閒置連線數,會將最大閒置連線數減少到符合最大開啟連線數的限制。如果n<=0,不會限制最大開啟連線數,預設為0(無限制)
func (db *DB) SetMaxIdleConns(n int)連線池中的最大閒置連線數
。
的資料庫
CREATE DATABASE sql_test;###進入該資料庫:###
use sql_test;###執行下列指令建立一張用於測試的資料表:###
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;## ##########1.2.2 查詢############單行查詢#########單行查詢###db.QueryRow()###執行一次查詢,並期望傳回最多一行結果(即Row)。 QueryRow總是傳回非nil的值,直到傳回值的Scan方法被呼叫時,才會傳回被延遲的錯誤。 (如:找不到結果)###
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) } }############1.2.3 插入數據#########插入、更新和刪除操作都使用###Exec###方法。 ###
func (db *DB) Exec(query string, args ...interface{}) (Result, error)###Exec執行一次指令(包括查詢、刪除、更新、插入等),傳回的Result是對已執行的SQL指令的總結。參數args表示query中的佔位參數。 ######具體插入資料範例程式碼如下:###
// 插入数据 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) }#############1.2.4更新資料########具體更新資料範例程式碼如下: ###
// 更新数据 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> |
以上是go語言中sql是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!