表格數(shù)據(jù)
| app_id | 參數(shù)名稱 | 參數(shù)值 | 參數(shù)值名稱 |
|---|---|---|---|
| 100074 | qwe12345 | mao11 | 超超1 |
| 100074 | ssss | mao3 | 超超1 |
| 100074 | qwe12 | ma01 | 超超2 |
| 100074 | www | mao3 | 超超3 |
| 100074 | www | mao4 | 超超4 |
| 100036 | qwe12345 | 1233 | 123 |
| 100036 | ssss | mao3 | 123 |
| 100036 | qwe12 | 100036 | 100036 |
| 100036 | www | www1 | dd |
對應(yīng)結(jié)構(gòu)體
type appEvents struct {
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"` // 主鍵
AppID string `gorm:"column:app_id;not null" json:"app_id"` // 應(yīng)用id
Params string `gorm:"column:params;not null" json:"params"` // 參數(shù)名稱
ParamsValue string `gorm:"column:params_value;not null" json:"params_value"` // 參數(shù)值
ParamsLabel string `gorm:"column:params_label;not null" json:"params_label"` // 參數(shù)值名稱
Creator int32 `gorm:"column:creator;not null" json:"creator"` // 創(chuàng)建人id
CreatedAt int64 `gorm:"column:created_at;autoCreateTime" json:"created_at"` // 創(chuàng)建時(shí)間
UpdatedAt int64 `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // 更新時(shí)間
}
錯(cuò)誤示例
appEventsIDStr := make([]string, 0, len(appEvents))
appEventsParams := make([]string, 0, len(appEvents))
for _, event := range appEvents {
appEventsIDStr = append(appEventsIDStr, event.AppID)
appEventsParams = append(appEventsParams, event.Params)
}
_, err = dao.WithContext(ctx).Where(dao.AppID.In(appEventsIDStr...), dao.Params.In(appEventsParams...)).Delete()
if err != nil {
return err
}
這段代碼的意圖是:直接將表格中的AppID和Params裝入分別appEventsIDStr和appEventsParams中
然后批量刪除那些其 AppID 值存在于 appEventsIDStr 且其 Params 值存在于 appEventsParams 的記錄。
這會造成的問題如下:
正常刪除了如下數(shù)據(jù):
| app_id | 參數(shù)名稱 |
|---|---|
| 100074 | qwe12345 |
但同時(shí)也會誤刪其他app_id下名稱為 qwe12345 的數(shù)據(jù):
| app_id | 參數(shù)名稱 |
|---|---|
| 100036 | qwe12345 |
只要是 appEventsIDStr 包含的應(yīng)用id 和 appEventsParams 中包含的參數(shù)名稱都會被全部刪除!
這顯然是不符合業(yè)務(wù)需求的:刪除指定app_id和參數(shù)名稱下的數(shù)據(jù)
正確邏輯
- 創(chuàng)建映射:為 appEvents 創(chuàng)建一個(gè)映射,其中鍵由 AppID 和 Params 組合而成。
- 從數(shù)據(jù)庫獲取數(shù)據(jù):查詢數(shù)據(jù)庫,獲取那些其 AppID 和 Params 在指定的列表中的數(shù)據(jù)。
- 收集要刪除的 IDs:根據(jù)先前創(chuàng)建的映射,篩選出數(shù)據(jù)庫中需要刪除的條目的 IDs。
- 刪除數(shù)據(jù)庫條目:基于篩選出來的 IDs,執(zhí)行數(shù)據(jù)庫中的刪除操作。
代碼實(shí)現(xiàn)
// 將 appEventsIDStr 和 appEventsParams 組合成 map的key ,value為空結(jié)構(gòu)體的map
appEventsMap := make(map[string]struct{}, len(appEventsIDStr))
for _, event := range appEvents {
appEventsMap[event.AppID+"_"+event.Params] = struct{}{}
}
// 批量刪除舊數(shù)據(jù)
dao := tx.CfgEventParamsValue
find, err := dao.WithContext(ctx).Where(dao.AppID.In(appEventsIDStr...), dao.Params.In(appEventsParams...)).Find()
if err != nil {
return err
}
var ids []int32
for _, value := range find {
// 將數(shù)據(jù)庫中的數(shù)據(jù)組合成map的key
// value.AppID + "_" + value.Params 為map的key
// value.ParamsValue 為map的value
_, exists := appEventsMap[value.AppID+"_"+value.Params]
if exists {
ids = append(ids, value.ID)
}
}
_, err = dao.WithContext(ctx).Where(dao.ID.In(ids...)).Delete()
if err != nil {
return err
}
代碼詳解
- 構(gòu)建appEventsMap映射:
appEventsMap := make(map[string]struct{}, len(appEventsIDStr))
for _, event := range appEvents {
appEventsMap[event.AppID+"_"+event.Params] = struct{}{}
}
在這段代碼中,首先初始化了一個(gè)名為 appEventsMap 的映射,其鍵是字符串,值是空結(jié)構(gòu)體(struct{} 不占用任何額外的內(nèi)存空間)。接下來,遍歷每個(gè) appEvents 條目并構(gòu)造映射鍵,該鍵是 AppID 和 Params 的組合,并為每個(gè)鍵分配一個(gè)空結(jié)構(gòu)體值。目的: 將 appEvents 列表轉(zhuǎn)換為映射,以便后續(xù)可以快速檢查某個(gè)組合(AppID 和 Params)是否存在于 appEvents 中。
- 查詢出要刪除的舊數(shù)據(jù):
appEventsIDStr := make([]string, 0, len(appEvents))
appEventsParams := make([]string, 0, len(appEvents))
for _, event := range appEvents {
appEventsIDStr = append(appEventsIDStr, event.AppID)
appEventsParams = append(appEventsParams, event.Params)
}
dao := tx.CfgEventParamsValue
find, err := dao.WithContext(ctx).Where(dao.AppID.In(appEventsIDStr...), dao.Params.In(appEventsParams...)).Find()
if err != nil {
return err
}
使用 dao 查詢數(shù)據(jù)庫中的數(shù)據(jù)。此查詢的條件是 AppID 必須在 appEventsIDStr 中,Params 必須在 appEventsParams 中。
- 篩選要刪除的數(shù)據(jù)ID:
var ids []int32
for _, value := range find {
_, exists := appEventsMap[value.AppID+"_"+value.Params]
if exists {
ids = append(ids, value.ID)
}
}
在這個(gè)循環(huán)中,您檢查從數(shù)據(jù)庫查詢返回的每個(gè)條目是否存在于之前創(chuàng)建的 appEventsMap 中。如果存在,那么該條目的 ID 被添加到要刪除的ID列表中。
- 執(zhí)行刪除操作:
_, err = dao.WithContext(ctx).Where(dao.ID.In(ids...)).Delete()
if err != nil {
return err
}
最后,執(zhí)行實(shí)際的刪除操作。您通過在ids列表中指定的ID來刪除所有條目。
綜合分析: 該代碼的主要目的是從數(shù)據(jù)庫中刪除特定的舊數(shù)據(jù)。這些數(shù)據(jù)由其AppID和Params確定,并在appEvents列表中給出。首先,代碼將這個(gè)列表轉(zhuǎn)換為一個(gè)映射,以便可以快速檢查一個(gè)給定的AppID和Params組合是否存在。然后,代碼查詢與這些AppID和Params組合匹配的所有數(shù)據(jù)庫條目,并刪除這些條目。