中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

數據庫中的null值怎么利用Go語言解決

發布時間:2020-12-23 14:31:30 來源:億速云 閱讀:184 作者:Leah 欄目:開發技術

本篇文章為大家展示了數據庫中的null值怎么利用Go語言解決,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

從數據庫讀取可能為null值得值時,可以選擇使用sql.NULL***來讀取;或者使用IFNULL、COALESCE等命令讓數據庫查詢值返回不為”“或者NULL

若需要往數據庫中插入null值,則依然可以使用sql.NULL***存儲所需的值,然后進行插入NULL值

直接使用sql.NULL***類型容易出現valid遺漏設置等問題,普通int、string與其轉換時,請寫幾個簡單的get、set函數

本demo使用的數據庫表以及數據如下

mysql> desc person;
+------------+--------------+------+-----+---------+----------------+
| Field   | Type     | Null | Key | Default | Extra     |
+------------+--------------+------+-----+---------+----------------+
| id     | int(11)   | NO  | PRI | NULL  | auto_increment |
| first_name | varchar(100) | NO  |   | NULL  |        |
| last_name | varchar(40) | YES |   | NULL  |        |
| age    | int(11)   | YES |   | NULL  |        |
+------------+--------------+------+-----+---------+----------------+
mysql> select * from person;
+----+------------+-----------+------+
| id | first_name | last_name | age |
+----+------------+-----------+------+
| 1 | yousa   | NULL   | NULL |
+----+------------+-----------+------+
mysql> show create table person;
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                      |
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| person | CREATE TABLE `person` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `first_name` varchar(100) NOT NULL,
 `last_name` varchar(40) DEFAULT NULL,
 `age` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 |
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

從數據庫中讀取NULL值

如果不作處理直接從數據庫中讀取NULL值到string/int,會發生如下錯誤錯誤

Scan NULL值到string的報錯

sql: Scan error on column index 1: unsupported Scan, storing driver.Value type <nil> into type *string

Scan NULL值到int的報錯

sql: Scan error on column index 1: converting driver.Value type <nil> ("<nil>") to a int: invalid syntax

使用如下的struct來讀取數據庫內容

type Person struct {
  firstName        string
  lastName        string 
  age           int
}
  //由于只有一行,直接使用QueryRow
  row := db.QueryRow("SELECT first_name, last_name FROM person WHERE first_name='yousa'")
  err = row.Scan(&hello.firstName, &hello.lastName)
  if err != nil {
    fmt.Println(err)
  }
  fmt.Println(hello)
  row1 := db.QueryRow("SELECT first_name, age FROM person WHERE first_name='yousa'")
  err = row1.Scan(&hello.firstName, &hello.age)
  if err != nil {
    fmt.Println(err)
  }
  fmt.Println(hello)

運行代碼,可以通過日志看出來,錯誤來自Scan將NULL值賦值給int或者string時,報錯;解決這個問題可以使用sql原生結構體sql.Null***來解決

使用sqlNull***

sql.Null***在sql庫中聲明如下,在讀取時,(比如讀取的值存儲到NullInt64),假如發現存儲的值是NULL,則會將NullInt64的valid設置為false,然后不會將值存儲到Int64中,Int64值默認為0,如果是NullString則String值時nil;如果是正常值,則會將Valid賦值為true,將值存儲到Int64中。

type NullInt64 struct {
  Int64 int64
  Valid bool // Valid is true if Int64 is not NULL
}
func (n *NullInt64) Scan(value interface{}) error
func (n NullInt64) Value() (driver.Value, error)
type NullString struct {
  String string
  Valid bool // Valid is true if String is not NULL
}
func (ns *NullString) Scan(value interface{}) error
func (ns NullString) Value() (driver.Value, error)

代碼修改為如下:

type Person struct {
  firstName        string
  lastNullName      sql.NullString
  nullAge         sql.NullInt64
}
  rowNull := db.QueryRow("SELECT first_name, last_name FROM person WHERE first_name='yousa'")
  err = rowNull.Scan(&hello.firstName, &hello.lastNullName)
  if err != nil {
    fmt.Println(err)
  }
  fmt.Println(hello)
  rowNull1 := db.QueryRow("SELECT first_name, age FROM person WHERE first_name='yousa'")
  err = rowNull1.Scan(&hello.firstName, &hello.nullAge)
  if err != nil {
    fmt.Println(err)
  }
  fmt.Println(hello)

輸出結果

{yousa 0 { false} {0 false}}

{yousa 0 { false} {0 false}}

使用IFNULL或者COALESCE

coalesce()解釋:返回參數中的第一個非空表達式(從左向右依次類推)

IFNULL(expr1,expr2):如果expr1不是NULL,IFNULL()返回expr1,否則它返回expr2。IFNULL()返回一個數字或字符串值,取決于它被使用的上下文環境。

查詢語句使用一個默認值來替換NULL即可

SELECT first_name, COALESCE(age, 0) FROM person;//

SELECT first_name, IFNULL(age, 0) FROM person;//

往數據庫中插入NULL值

前面我們對SELECT語句使用了sql.Null***類型,同理,INSERT、UPDATE語句也可以通過使用這種類型來插入nil值

代碼如下:

  hello := Person {
    firstName: "",
    lastName: "",
    age: 0,
    lastNullName: sql.NullString{String:"", Valid:false},
    nullAge: sql.NullInt64{Int64:0, Valid:false}}
  _, err = db.Exec(
    "INSERT INTO person (first_name, last_name) VALUES (?, ?)", "yousa1", hello.lastName)
  if err != nil {
    fmt.Println(err)
  }
  _, err = db.Exec(
    "INSERT INTO person (first_name, last_name) VALUES (?, ?)", "yousa2", hello.lastNullName)
  if err != nil {
    fmt.Println(err)
  }
//數據庫插入結果
mysql> select * from person;
+----+------------+-----------+------+
| id | first_name | last_name | age |
+----+------------+-----------+------+
| 1 | yousa   | NULL   | NULL |
| 2 | yousa1   |      | NULL |
| 3 | yousa2   | NULL   | NULL |
+----+------------+-----------+------+

解釋下db.Exec操作hello.lastNullName的過程:

首先它會調用hello.lastNullName的Value方法,獲取到driver.Value,然后檢驗Valid值是true還是false,如果是false則會返回一個nil值(nil值傳給sql driver會被認為是NULL值),如果是true則會將hello.lastNullName.String的值傳過去。

PS: 為了保證你所插入的值能如你所期望是NULL值,一定記得要將sql.Null***中Valid值置為false

使用NULL還是有很多危害的,再回顧下數據庫中使用NULL值的危害

為什么不建議使用NULL

所有使用NULL值的情況,都可以通過一個有意義的值的表示,這樣有利于代碼的可讀性和可維護性,并能從約束上增強業務數據的規范性。

NULL值在timestamp類型下容易出問題,特別是沒有啟用參數explicit_defaults_for_timestamp

NOT IN、!= 等負向條件查詢在有 NULL 值的情況下返回永遠為空結果,查詢容易出錯

Null 列需要更多的存儲空間:需要一個額外字節作為判斷是否為 NULL 的標志位

NULL值到非NULL的更新無法做到原地更新,更容易發生索引分裂,從而影響性能。

PS:但把NULL列改為NOT NULL帶來的性能提示很小,除非確定它帶來了問題,否則不要把它當成優先的優化措施,最重要的是使用的列的類型的適當性。

當然有些情況是不得不使用NULL值進行存儲,或者在查詢時由于left/right join等導致NULL值,但總體來說,能少用就少用。

helper func(提升效率/減少錯誤)

如果使用sql.NULL***的話,由于其有兩個字段,如果直接手動賦值的話還是很容易遺漏,所以還是需要簡單的轉換函數,這里給了兩個簡單的helper fuc,分別是將int64轉換成NullInt64和將string轉換成NullString

//ToNullString invalidates a sql.NullString if empty, validates if not empty
func ToNullString(s string) sql.NullString {
  return sql.NullString{String : s, Valid : s != ""}
}
//ToNullInt64 validates a sql.NullInt64 if incoming string evaluates to an integer, invalidates if it does not
func ToNullInt64(s string) sql.NullInt64 {
  i, err := strconv.Atoi(s)
  return sql.NullInt64{Int64 : int64(i), Valid : err == nil}
}

補充:golang 處理mysql數據庫中的NULL, nil,time類型的值

在用golang獲取數據庫的數據的時候,難免會遇到可控field。這個時候拿到的數據如果直接用string, time.Time這樣的類型來解析的話會遇到panic。

下面的方法會解決這種問題:

表結構:

show create table checksum_mengyao;

CREATE TABLE `checksum_mengyao` (
 `db` char(64) NOT NULL,
 `tbl` char(64) NOT NULL,
 `chunk` int(11) NOT NULL,
 `chunk_time` float DEFAULT NULL,
 `chunk_index` varchar(200) DEFAULT NULL,
 `lower_boundary` text,
 `upper_boundary` text,
 `this_crc` char(40) NOT NULL,
 `this_cnt` int(11) NOT NULL,
 `master_crc` char(40) DEFAULT NULL,
 `master_cnt` int(11) DEFAULT NULL,
 `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (`db`,`tbl`,`chunk`),
 KEY `ts_db_tbl` (`ts`,`db`,`tbl`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

表中的一條記錄:

+------------+-----------------+-------+------------+-------------+----------------+----------------+----------+----------+------------+------------+---------------------+
| db     | tbl       | chunk | chunk_time | chunk_index | lower_boundary | upper_boundary | this_crc | this_cnt | master_crc | master_cnt | ts         |
+------------+-----------------+-------+------------+-------------+----------------+----------------+----------+----------+------------+------------+---------------------+
| db_kb | admin_info |   1 |  0.007406 | NULL    | NULL      | NULL      | 33d5c5be |    1 | 33d5c5be  |     1 | 2019-12-11 10:39:03 |
+------------+-----------------+-------+------------+-------------+----------------+----------------+----------+----------+------------+------------+---------------------+

定義一個struct OriginalData 用于接收表中的數據

type OriginalData struct {
 db11      string
 tbl11      string
 chunk1     int
 chunk_time1   float64
 chunk_index1  sql.NullString
 lower_boundary1 sql.NullString
 upper_boundary1 sql.NullString
 this_crc1    sql.NullString
 this_cnt1    int
 master_crc1   sql.NullString
 master_cnt1   int
 ts1       mysql.NullTime   //"github.com/go-sql-driver/mysql"
}

拿到表中數據將其轉換格式后用另一個struct DatacheckInfo 去接收,這便于操作這些數據

type DatacheckInfo struct {
 Db1      string
 Tbl1      string
 Chunk     int
 Chunk_time   float64
 Chunk_index  string
 Lower_boundary string
 Upper_boundary string
 This_crc    string
 This_cnt    int
 Master_crc   string
 Master_cnt   int
 Ts       string
}

golang獲取表中原始數據

func SaveAlldata(rows *sql.Rows) []DatacheckInfo {
 var test OriginalData   //保存表中元數據
 var datalist []DatacheckInfo  //保存元數據轉換后的數據
 for rows.Next() {
 var dataInfo DatacheckInfo
 rows.Scan(&test.db11, &test.tbl11, &test.chunk1, &test.chunk_time1, &test.chunk_index1, &test.lower_boundary1,
  &test.upper_boundary1, &test.this_crc1, &test.this_cnt1, &test.master_crc1, &test.master_cnt1, &test.ts1)
 dataInfo.Db1 = test.db11
 dataInfo.Tbl1 = test.tbl11
 dataInfo.Chunk = test.chunk1
 dataInfo.Chunk_time = test.chunk_time1
 //fmt.Println(test.chunk_time1)
 
 if test.chunk_index1.Valid {     //true 非null值
  dataInfo.Chunk_index = test.chunk_index1.String
 }else{                //false null值
  dataInfo.Chunk_index = "NULL"
 }
 if test.lower_boundary1.Valid{
  dataInfo.Lower_boundary = test.lower_boundary1.String
 }else {
  dataInfo.Lower_boundary = "NULL"
 }
 if test.upper_boundary1.Valid{
  dataInfo.Upper_boundary = test.upper_boundary1.String
 }else {
  dataInfo.Upper_boundary = "NULL"
 }
 if test.this_crc1.Valid{
  dataInfo.This_crc = test.this_crc1.String
 }else {
  dataInfo.This_crc = "NULL"
 }
 dataInfo.This_cnt = test.this_cnt1
 if test.master_crc1.Valid{
  dataInfo.Master_crc = test.master_crc1.String
 }else {
  dataInfo.Master_crc = "NULL"
 }
 dataInfo.Master_cnt = test.master_cnt1
 
 //fmt.Println(test.ts1, reflect.TypeOf(test.ts1.Valid), reflect.TypeOf(test.ts1.Time))
 if test.ts1.Valid {
  dataInfo.Ts = test.ts1.Time.Format("2006-01-02 15:04:05")
 }else{
  dataInfo.Ts = "NULL"
 }
 datalist = append(datalist,dataInfo)
 fmt.Println(dataInfo)
 } 
 return datalist
}
 
func Selectalldata(sdb *sql.DB, ipval string){  //CheckdataDiffsendding()
  //*******省略連接數據庫的操作
 rows, err := sdb.Query("SELECT * FROM checksum_mengyao")
 defer rows.Close()
 dataInfo := SaveAlldata(rows) 
}

上述內容就是數據庫中的null值怎么利用Go語言解決,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

溆浦县| 辉南县| 永嘉县| 东莞市| 曲松县| 乌恰县| 临沂市| 湖南省| 周宁县| 祁门县| 阿坝| 成都市| 赣州市| 双江| 湖南省| 宜兰市| 周至县| 扎鲁特旗| 毕节市| 陈巴尔虎旗| 渑池县| 大埔区| 仪陇县| 汉中市| 柏乡县| 徐州市| 长寿区| 拉萨市| 广南县| 平陆县| 五常市| 宣城市| 临沂市| 会昌县| 杭锦后旗| 霍州市| 广宗县| 永登县| 固安县| 师宗县| 孟州市|