---
# System prepended metadata

title: Day27  Golang MySQL 我的資料庫

---

# Day27  Golang MySQL 我的資料庫  

在成功連上SQL資料庫後，  
我們可以在一開始先寫好`init()` 初始化資料庫的連線。  

```go
var db *sql.DB

// 與DB連線。 init() 初始化，時間點比 main() 更早。
func init() {
	dbConnect, err := sql.Open(
		"mysql",
		"root:root@tcp(127.0.0.1:3306)/",
	)

	if err != nil {
		log.Fatalln(err)
	}

	err = dbConnect.Ping()
	if err != nil {
		log.Fatalln(err)
	}

	db = dbConnect // 用全域變數接

	db.SetMaxOpenConns(10) // 可設置最大DB連線數，設<=0則無上限（連線分成 in-Use正在執行任務 及 idle執行完成後的閒置 兩種）  
	db.SetMaxIdleConns(10) // 設置最大idle閒置連線數。
	// 更多用法可以 進 sql.DBStats{}、sql.DB{} 裡面看
}
```


---

Golang中的`*sql.DB`這一物件抽了一層介面出來，  
日後等子孫們不想使用`MySQL`時，   
只需更換 [**driver**](https://github.com/golang/go/wiki/SQLDrivers) 即可。  



若在使用上時出現 `Error 1046 no database selected`的錯誤，  
表示沒有指定資料庫，可改成以下指定該資料庫來連線。  
```go
db, err := sql.Open(
	"mysql",
	"root:root@tcp(127.0.0.1:3306)/dbName",
)
```

或者用SQL語法的`USE`來指定資料庫。  

```go
db.Exec("USE `school`")
```




## 建立、刪除資料庫

下`SQL語法關鍵字`時，通常會使用`全大寫`（雖然小寫也可以，但大寫較容易區分）  

#### 【建立資料庫】Create Database

```go
db.Exec("CREATE DATABASE `test1`")
```

加了`IF NOT EXISTS `之後，會先檢查要建立的物件存不存在，如果不存在再執行建立。  
否則 如果無法建立物件時（未成功執行），`err` 會回傳 `Error 1007: Can't create database '..'; database exists`。   

```go
_, err := db.Exec("CREATE DATABASE IF NOT EXISTS `test1`")
```

#### 【刪除資料庫】Detele Database

同樣能先判斷`IF EXISTS `看欲刪除的物件存不存在。  

```go
db.Exec("DROP DATABASE IF EXISTS `test1`")
```


此外，SQL語法可以用變數代入、拉出來寫成一個`func`  

```go
func main() {
	createDb("`school`") // 比較好的習慣會使用 `` 反引號（重音號）來區隔名字
	// deleteDb("`school`")
}

func createDb(dbName string) {
	_, err := db.Exec("CREATE DATABASE IF NOT EXISTS " + dbName + ";")
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Print(err)
}

func deleteDb(dbName string) {
	_, err := db.Exec("DROP DATABASE IF EXISTS " + dbName + ";")
	if err != nil {
		log.Fatalln(err)
	}
}
```


## 用Goland IDE與資料庫連線  

新增MySQL  
![新增MySQL](https://i.imgur.com/uIXAhna.png)

輸入帳號與密碼  
![輸入帳號密碼](https://i.imgur.com/jYVCEh2.png)

切換到Scheme頁面  
![Scheme](https://i.imgur.com/pxEbqy5.png)

## 建立、刪除表格

資料表中每個欄位都必須要有類型、並設置Byte長度，才能存放該型別的資料。  
常見的變數類型有：  
* `INT`(每長度 4 bytes): 數字，存放整數資料
* `CHAR`(每長度 1 byte): 固定長度字串
* `VARCHAR`: 可變長度字串，用多少長度就多少
* `TEXT`: 文字敘述、說明（長度較長）
* `DATE`: 時間格式
* `TIMESTAMP`: 時間戳



#### 【建立資料表】Create Table

建立student的表格  
```go
func createTable1() {
	// SQL: CREATE TABLE IF NOT EXISTS `school.student`
	// 初始化Table 沒給任何欄位時，會出現 `A table must have at least 1 column` 的錯誤

	_, err := db.Exec("CREATE TABLE IF NOT EXISTS `school`.`student`(`name` VARCHAR (10))")
	if err != nil {
		log.Fatalln(err)
	}

	// 或者用 USE 來指定該資料庫
	// db.Exec("USE `school`")
	// _, err := db.Exec("CREATE TABLE IF NOT EXISTS `student`(`name` VARCHAR (10))")
}
```

再建立一個teacher的表格，這次存放`name(VARCHAR)`、`age(INT)`兩個欄位，  
並且設置name為`PRIMARY KEY`（唯一不重複的識別ID）。  
表單目前內是沒任何內容的。  
![teacher表格](https://i.imgur.com/TKaHMIV.png)  
```go
_, err := db.Exec("CREATE TABLE IF NOT EXISTS `school`.`teacher`(`name` VARCHAR (10) PRIMARY KEY, `age` INT (1))")
```


#### 【刪除表格】Detele Table

```go
_, err := db.Exec("DROP TABLE IF EXISTS `school`.`student`")
```

#### 【更改欄位】Alter Table

替 student的Table加上 `id` 這個會自動遞增(AUTO_INCREMENT)的欄位，並設定成主鍵（數字遞增的特性：唯一、不重複，且Not Null）。  

```go
_, err := db.Exec("ALTER TABLE `school`.`student` ADD `id` INT AUTO_INCREMENT PRIMARY KEY;")
```

![Alter AUTO_INCREMENT](https://i.imgur.com/9gXCTh1.png)

## 反引號 vs 雙引號
`資料表、欄位` 會用反引號\` \` 包起來，以防與SQL語法關鍵字搞混。  
而雙引號" "常用在字串的組合、組成SQL語法傳遞給資料庫。  