go語言中sql是什麼

2022-12-22 14:00:26

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語言來運算元據庫。

1.1連線

1.1.1下載依賴

go get -u github.com/go-sql-driver/mysql
登入後複製

1.1.2使用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判斷的下面
}
登入後複製

1.1.3 初始化連線

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
	}
}
登入後複製

1.1.4SetMaxOpenConns

SetMaxOpenConns設定與數據庫建立連線的最大數目。 如果n大於0且小於最大閒置連線數,會將最大閒置連線數減小到匹配最大開啟連線數的限制。 如果n<=0,不會限制最大開啟連線數,預設為0(無限制)

1.1.5SetMaxIdleConns

func (db *DB) SetMaxIdleConns(n int)
登入後複製

連線池中的最大閒置連線數

如果n大於最大開啟連線數,則新的最大閒置連線數會減小到匹配最大開啟連線數的限制。 如果n<=0,不會保留閒置連線

1.2CRUD

1.2.1 建庫建表

我們先在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;
登入後複製

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)
}
登入後複製

1.2.5刪除資料

具體刪除資料的範例程式碼如下:

// 刪除資料
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()
}
登入後複製

1.3MySQL預處理

1.3.1什麼是預處理?

普通SQL語句執行過程:

  • 使用者端對SQL語句進行預留位置替換得到完整的SQL語句。

  • 使用者端傳送完整SQL語句到MySQL伺服器端

  • MySQL伺服器端執行完整的SQL語句並將結果返回給使用者端

預處理執行過程:

  • 把SQL語句分成兩部分,命令部分與資料部分

  • 先把命令部分傳送給MySQL伺服器端MySQL伺服器端進行SQL預處理

  • 然後把資料部分傳送給MySQL伺服器端,MySQL伺服器端對SQL語句進行預留位置替換

  • MySQL伺服器端執行完整的SQL語句並將結果返回給使用者端

1.3.2為什麼要預處理?

  • 優化MySQL伺服器重複執行SQL的方法,可以提升伺服器效能,提前讓伺服器編譯,一次編譯多次執行,節省後續編譯的成本。

  • 避免SQL隱碼攻擊問題。

1.3.3 Go實現MySQL預處理

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.")
}
登入後複製

總結 其實就多了一個db.Prepare(sqlStr)

1.3.4 SQL隱碼攻擊問題

我們任何時候都不應該自己拼接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>