起因
这是昨天中午楼下吃饭偶然吃到的瓜。
我与另一个同事在一桌用餐,隔壁桌新来了一个人(后续称为瓜),看着也不大,28左右。刚点完餐电话就来了。
由于当时吃饭人也多,就只能听清他的声音。
电话那头:“。。。。。。”
瓜:“没有啊,我这边今天什么也没动”
电话那头:“。。。。。。”
瓜:“我昨天发的0.1.3版本,今天没有动”
瓜:“今天他们发的1.2分支,跟我这个没关系啊”
电话那头:“.。。。。。。”
瓜:“我这边也没有动数据库”
电话那头:“。。。。。。”
瓜:“我就直接select * 然后查出来了”
瓜:“我今天也没有动数据库”
电话那头:“。。。。。。”
瓜:“那这个没有办法啊,可能他们改数据库影响到了”
后续还扯了几句,看瓜的状态双方应该都比较激动,后来他匆匆吃了几口就跑了。
分析
其实当瓜说到“select *”的时候我就大概猜到什么问题了。
因为我给公司制定的开发规范中的一条就是“代码中禁止使用select * ”。
原因有二
- select * 意味着该SQL大概率是要回表的,但是很多场景下,业务只需要部分字段,完全可以使用select column覆盖索引的方式避免回表,从而提升性能。
- 假设A表涉及多个业务,代码中使用结构体接收select * 的所有字段,另一个人需要对A表进行增减字段,这时使用select * 的代码就会异常。
我猜测该瓜也是遇到了第二种情况,自己用了select * ,但是别的业务也会用该表,别人根据需要对表字段进行了增减,他的select * 直接爆炸。
附上伪代码
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type Person struct {
ID int
Name string
Age int
}
func main() {
// 设置 MySQL 连接信息
dsn := "xxx:xxx@tcp(xxx.xxx.xxx.xxx:3306)/test_2"
// 连接数据库
db, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Println("Failed to connect to database:", err)
return
}
defer db.Close()
// 使用select * 查询 t2的表数据
rows, err := db.Query("SELECT * FROM t2")
if err != nil {
fmt.Println("Failed to query database:", err)
return
}
defer rows.Close()
// 遍历查询结果并映射到结构体
var people []Person
for rows.Next() {
var p Person
if err := rows.Scan(&p.ID, &p.Name, &p.Age); err != nil {
fmt.Println("Error scanning row:", err)
return
}
people = append(people, p)
}
// 打印查询结果
for _, p := range people {
fmt.Printf("ID: %d, Name: %s, Age: %d\n", p.ID, p.Name, p.Age)
}
if err := rows.Err(); err != nil {
fmt.Println("Error in rows:", err)
return
}
}
数据库表新增字段后
彩蛋
如果没有数据库相关的开发规范,像这种坑被踩烂了都会有人继续踩。