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)
登入後複製
連線池中的最大閒置連線數
如果n大於最大開啟連線數,則新的最大閒置連線數會減小到匹配最大開啟連線數的限制。 如果n<=0,不會保留閒置連線。
我們先在MySQL中建立一個名為sql_test
的資料庫
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;
登入後複製
單行查詢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)
}
}
登入後複製
插入、更新和刪除操作都使用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)
}
登入後複製
具體更新資料範例程式碼如下:
// 更新資料
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 | ? |
PostgreSQL | $1 , $2 等 |
SQLite | ? 和$1 |
Oracle | :name |
【相關推薦:Go視訊教學、】
以上就是go語言中sql是什麼的詳細內容,更多請關注TW511.COM其它相關文章!
<script type="text/javascript" src="https://sw.php.cn/hezuo/43cc2463da342d2af2696436bd2d05f4.html?bottom" ></script>