SQL は「構造化照会言語」を指し、データベースの作成、データベースの削除、レコードのクエリ、レコードの変更、フィールドの追加など、データベースを操作するための言語です。 SQL はリレーショナル データベースの標準言語です。MySQL、Oracle、SQL Server、MS Access、Sybase、Informix、Postgres などのすべてのリレーショナル データベース管理システム (RDBMS) は、標準処理言語として SQL を使用します。
このチュートリアルの動作環境: Windows 7 システム、GO バージョン 1.18、Dell G3 コンピューター。
SQL とは
SQL は、データベースの作成、データベースの削除、レコードのクエリ、レコードの変更、追加など、データベースを操作するための言語です。フィールドなどSQL は ANSI によって標準化された言語ですが、さまざまな実装があります。
SQL は Structured Query Language の略称で、中国語に訳すと「構造化照会言語」となります。 SQL は、リレーショナル データベースに保存されたデータの保存、取得、変更に使用されるコンピュータ言語です。
SQL はリレーショナル データベースの標準言語です。MySQL、Oracle、SQL Server、MS Access、Sybase、Informix、Postgres などのすべてのリレーショナル データベース管理システム (RDBMS) は、標準として 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)
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判断的下面 }
データ ソース名が実際で有効な かどうかを確認する場合は、 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 } }
数 未満の場合、アイドル接続の
最大数は、開いている接続の最大数と一致する制限 まで減らされます。 n<=0 の場合、開いている接続の最大数は制限されません。デフォルトは 0 (制限なし)です##1.1.5SetMaxIdleConns
func (db *DB) SetMaxIdleConns(n int)
より大きい場合、新しい最大値アイドル状態の接続の数は、開いている接続の最大数の 制限 に一致するまで減ります。
n<=0 の場合、アイドル状態の接続は保持されません。 1.2CRUD
CREATE DATABASE sql_test;
use sql_test;
次のコマンドを実行して、テスト用のデータ テーブルを作成します。<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">CREATE TABLE `user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT &#39;&#39;,
`age` INT(11) DEFAULT &#39;0&#39;,
PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;</pre><div class="contentsignin">ログイン後にコピー</div></div>
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) } }
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
データを挿入するための具体的なサンプル コードは次のとおりです。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">// 插入数据
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)
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
##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 中国語 Web サイトの他の関連記事を参照してください。