trade_position_analysis.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. package data_manage
  2. import (
  3. sql2 "database/sql"
  4. "eta/eta_task/global"
  5. "eta/eta_task/utils"
  6. "fmt"
  7. "gorm.io/gorm"
  8. "time"
  9. "unsafe"
  10. )
  11. // 持仓榜单表
  12. type TradePositionTop struct {
  13. Id uint64 `orm:"column(id);pk" gorm:"primaryKey"`
  14. ClassifyName string //分类名称
  15. ClassifyType string //分类名称下的类型
  16. DataTime string //数据日期
  17. CreateTime time.Time //插入时间
  18. ModifyTime time.Time //修改时间
  19. DealShortName string //成交量公司简称
  20. DealValue int //成交量
  21. DealChange int //成交变化量
  22. DealType int //交易类型:1多单,2空单,3净多单,4净空单
  23. SourceType int //数据来源,0是原始数据的值,1是由T+1日推算出的值,2是由T日的榜单数据推算出的值
  24. Rank int //排名
  25. }
  26. func (m *TradePositionTop) AfterFind(db *gorm.DB) (err error) {
  27. m.DataTime = utils.GormDateStrToDateStr(m.DataTime)
  28. return
  29. }
  30. func (m *TradePositionTop) ConvertTimeStr() {
  31. m.DataTime = utils.GormDateStrToDateStr(m.DataTime)
  32. return
  33. }
  34. type TradePositionDalianTop struct {
  35. TradePositionTop
  36. }
  37. type TradePositionZhengzhouTop struct {
  38. TradePositionTop
  39. }
  40. type TradePositionCffexTop struct {
  41. TradePositionTop
  42. }
  43. type TradePositionShanghaiTop struct {
  44. TradePositionTop
  45. }
  46. type TradePositionIneTop struct {
  47. TradePositionTop
  48. }
  49. type TradePositionGuangzhouTop struct {
  50. TradePositionTop
  51. }
  52. func InsertMultiTradePositionTop(exchange string, items []*TradePositionTop) (err error) {
  53. o := global.DbMap[utils.DbNameIndex]
  54. if exchange == "dalian" {
  55. list := make([]*TradePositionDalianTop, 0)
  56. for _, v := range items {
  57. tmp := (*TradePositionDalianTop)(unsafe.Pointer(v))
  58. list = append(list, tmp)
  59. }
  60. err = o.CreateInBatches(list, utils.MultiAddNum).Error
  61. return
  62. } else if exchange == "zhengzhou" {
  63. list := make([]*TradePositionZhengzhouTop, 0)
  64. for _, v := range items {
  65. tmp := (*TradePositionZhengzhouTop)(unsafe.Pointer(v))
  66. list = append(list, tmp)
  67. }
  68. err = o.CreateInBatches(list, utils.MultiAddNum).Error
  69. return
  70. } else if exchange == "cffex" {
  71. list := make([]*TradePositionCffexTop, 0)
  72. for _, v := range items {
  73. tmp := (*TradePositionCffexTop)(unsafe.Pointer(v))
  74. list = append(list, tmp)
  75. }
  76. err = o.CreateInBatches(list, utils.MultiAddNum).Error
  77. return
  78. } else if exchange == "shanghai" {
  79. list := make([]*TradePositionShanghaiTop, 0)
  80. for _, v := range items {
  81. tmp := (*TradePositionShanghaiTop)(unsafe.Pointer(v))
  82. list = append(list, tmp)
  83. }
  84. err = o.CreateInBatches(list, utils.MultiAddNum).Error
  85. return
  86. } else if exchange == "ine" {
  87. list := make([]*TradePositionIneTop, 0)
  88. for _, v := range items {
  89. tmp := (*TradePositionIneTop)(unsafe.Pointer(v))
  90. list = append(list, tmp)
  91. }
  92. err = o.CreateInBatches(list, utils.MultiAddNum).Error
  93. return
  94. } else if exchange == "guangzhou" {
  95. list := make([]*TradePositionGuangzhouTop, 0)
  96. for _, v := range items {
  97. tmp := (*TradePositionGuangzhouTop)(unsafe.Pointer(v))
  98. list = append(list, tmp)
  99. }
  100. err = o.CreateInBatches(list, utils.MultiAddNum).Error
  101. return
  102. }
  103. return
  104. }
  105. func GetTradePositionTopByExchangeDataTime(exchange string, startDate, endDate string) (list []*TradePositionTop, err error) {
  106. o := global.DbMap[utils.DbNameIndex]
  107. sql := "SELECT * FROM trade_position_" + exchange + "_top where data_time >= ? and data_time <= ? and deal_type in (1,2) ORDER BY classify_name, classify_type, deal_type, data_time, deal_value desc"
  108. err = o.Raw(sql, startDate, endDate).Find(&list).Error
  109. return
  110. }
  111. func GetTradePositionTopByExchangeDataTimeByClassify(exchange string, startDate, endDate string, classifyNames, classifyTypes []string) (list []*TradePositionTop, err error) {
  112. o := global.DbMap[utils.DbNameIndex]
  113. sql := `SELECT * FROM trade_position_` + exchange + `_top where data_time >= ? and data_time <= ? and deal_type in (1,2) and classify_name in (` + utils.GetOrmInReplace(len(classifyNames)) + `) and classify_type in (` + utils.GetOrmInReplace(len(classifyTypes)) + `) ORDER BY classify_name, classify_type, deal_type, data_time, deal_value desc`
  114. err = o.Raw(sql, startDate, endDate, classifyNames, classifyTypes).Find(&list).Error
  115. return
  116. }
  117. func GetTradePositionTopCountByExchangeDataTime(exchange string, startDate, endDate string) (count int64, err error) {
  118. o := global.DbMap[utils.DbNameIndex]
  119. sql := "SELECT count(*) FROM trade_position_" + exchange + "_top where data_time >= ? and data_time <= ? and deal_type in (1,2) ORDER BY classify_name, classify_type, deal_type, data_time, deal_value desc"
  120. err = o.Raw(sql, startDate, endDate).Scan(&count).Error
  121. return
  122. }
  123. func GetTradePositionTopByExchangeSourceType(exchange string, dataTime string, sourceType int) (list []*TradePositionTop, err error) {
  124. o := global.DbMap[utils.DbNameIndex]
  125. sql := "SELECT * FROM trade_position_" + exchange + "_top where data_time= ? and source_type = ? ORDER BY classify_name, classify_type, deal_type, deal_value desc"
  126. err = o.Raw(sql, dataTime, sourceType).Find(&list).Error
  127. return
  128. }
  129. func GetTradePositionTopByExchangeSourceTypeClassify(exchange string, dataTime string, sourceType int, classifyNames, classifyTypes []string) (list []*TradePositionTop, err error) {
  130. o := global.DbMap[utils.DbNameIndex]
  131. sql := `SELECT * FROM trade_position_` + exchange + `_top where data_time= ? and source_type = ? and classify_name in (` + utils.GetOrmInReplace(len(classifyNames)) + `) and classify_type in (` + utils.GetOrmInReplace(len(classifyTypes)) + `) ORDER BY classify_name, classify_type, deal_type, deal_value desc`
  132. err = o.Raw(sql, dataTime, sourceType, classifyNames, classifyTypes).Find(&list).Error
  133. return
  134. }
  135. type TradeTopClassify struct {
  136. ClassifyName string //分类名称
  137. ClassifyType string //分类名称下的类型
  138. }
  139. type TradePositionSub struct {
  140. ClassifyName string //分类名称
  141. ClassifyType string //分类名称下的类型
  142. DataTime string //数据日期
  143. DealShortName string //成交量公司简称
  144. SubValue int //差值
  145. DealType int
  146. }
  147. type TradePositionSubList []*TradePositionSub
  148. func (v TradePositionSubList) Len() int {
  149. return len(v)
  150. }
  151. func (v TradePositionSubList) Swap(i, j int) {
  152. v[i], v[j] = v[j], v[i]
  153. }
  154. func (v TradePositionSubList) Less(i, j int) bool {
  155. return v[i].SubValue > v[j].SubValue
  156. }
  157. type UpdateDealValueChange struct {
  158. Id uint64
  159. DealValue int //成交量
  160. DealChange int
  161. SourceType int
  162. ModifyTime time.Time //修改时间
  163. }
  164. type UpdateChangeVal struct {
  165. Id uint64
  166. DealChange int
  167. ModifyTime time.Time //修改时间
  168. }
  169. func MultiUpdatePositionTop(exchange string, updates []UpdateDealValueChange) (err error) {
  170. o := global.DbMap[utils.DbNameIndex]
  171. sql := "UPDATE trade_position_" + exchange + "_top SET deal_value=?, deal_change=?, source_type=?, modify_time=? WHERE id = ?"
  172. for _, v := range updates {
  173. err = o.Exec(sql, v.DealValue, v.DealChange, v.SourceType, v.ModifyTime, v.Id).Error
  174. if err != nil {
  175. return
  176. }
  177. }
  178. return
  179. }
  180. func DeletePositionTopByDataTime(exchange string, dataTime string, dealType int) (err error) {
  181. o := global.DbMap[utils.DbNameIndex]
  182. sql := "delete from trade_position_" + exchange + "_top WHERE data_time=? and deal_type=?"
  183. err = o.Exec(sql, dataTime, dealType).Error
  184. return
  185. }
  186. func DeletePositionTopByDataTimeClassify(exchange string, dataTime string, dealType int, classifyNames, classifyTypes []string) (err error) {
  187. o := global.DbMap[utils.DbNameIndex]
  188. sql := `delete from trade_position_` + exchange + `_top WHERE data_time=? and deal_type=? and classify_name in (` + utils.GetOrmInReplace(len(classifyNames)) + `) and classify_type in (` + utils.GetOrmInReplace(len(classifyTypes)) + `)`
  189. err = o.Exec(sql, dataTime, dealType, classifyNames, classifyTypes).Error
  190. return
  191. }
  192. func GetTradePositionTopByExchangeDataTimeType(exchange string, dataTime string, dealType int) (list []TradePositionTop, err error) {
  193. o := global.DbMap[utils.DbNameIndex]
  194. sql := "select * from trade_position_" + exchange + "_top WHERE data_time=? and deal_type=?"
  195. err = o.Raw(sql, dataTime, dealType).Find(&list).Error
  196. return
  197. }
  198. func GetTradePositionTopByExchangeDataTimeTypeClassify(exchange string, dataTime string, dealType int, classifyNames, classifyTypes []string) (list []TradePositionTop, err error) {
  199. o := global.DbMap[utils.DbNameIndex]
  200. sql := `select * from trade_position_` + exchange + `_top WHERE data_time=? and deal_type=? and classify_name in (` + utils.GetOrmInReplace(len(classifyNames)) + `) and classify_type in (` + utils.GetOrmInReplace(len(classifyTypes)) + `)`
  201. err = o.Raw(sql, dataTime, dealType, classifyNames, classifyTypes).Find(&list).Error
  202. return
  203. }
  204. func MultiInsertTradeBaseDataToTop(exchange string, startDate, endDate string) (err error) {
  205. o := global.DbMap[utils.DbNameIndex]
  206. now := time.Now().Format(utils.FormatDateTime)
  207. sql1 := "INSERT INTO trade_position_" + exchange + "_top(classify_name,classify_type,deal_short_name,deal_value,deal_change,data_time,deal_type,source_type,rank,create_time,modify_time) " +
  208. "SELECT classify_name,classify_type,buy_short_name,buy_value,buy_change,data_time,1,0,rank,?,? FROM base_from_trade_" + exchange + "_index where rank <50 and buy_short_name !='' and buy_short_name !=' ' and data_time between ? and ?"
  209. err = o.Exec(sql1, now, now, startDate, endDate).Error
  210. if err != nil {
  211. return
  212. }
  213. sql2 := "INSERT INTO trade_position_" + exchange + "_top(classify_name,classify_type,deal_short_name,deal_value,deal_change,data_time,deal_type,source_type,rank,create_time,modify_time) " +
  214. "SELECT classify_name,classify_type,sold_short_name,sold_value,sold_change,data_time,2,0,rank,?,? FROM base_from_trade_" + exchange + "_index where rank <50 and sold_short_name !='' and sold_short_name !=' ' and data_time between ? and ?"
  215. err = o.Exec(sql2, now, now, startDate, endDate).Error
  216. return
  217. }
  218. func MultiInsertTradeBaseDataToTopByClassify(exchange string, startDate, endDate string, classifyNames, classifyTypes []string) (err error) {
  219. o := global.DbMap[utils.DbNameIndex]
  220. now := time.Now().Format(utils.FormatDateTime)
  221. sql1 := "INSERT INTO trade_position_" + exchange + "_top(classify_name,classify_type,deal_short_name,deal_value,deal_change,data_time,deal_type,source_type,rank,create_time,modify_time)" +
  222. "SELECT classify_name,classify_type,buy_short_name,buy_value,buy_change,data_time,1,0,rank,?,? FROM base_from_trade_" + exchange + "_index where rank <50 and buy_short_name !='' and buy_short_name !=' ' and data_time between ? and ? and classify_name in (" + utils.GetOrmInReplace(len(classifyNames)) + ") and classify_type in (" + utils.GetOrmInReplace(len(classifyTypes)) + ")"
  223. err = o.Exec(sql1, now, now, startDate, endDate, classifyNames, classifyTypes).Error
  224. if err != nil {
  225. return
  226. }
  227. sql2 := "INSERT INTO trade_position_" + exchange + "_top(classify_name,classify_type,deal_short_name,deal_value,deal_change,data_time,deal_type,source_type,rank,create_time,modify_time)" +
  228. "SELECT classify_name,classify_type,sold_short_name,sold_value,sold_change,data_time,2,0,rank,?,? FROM base_from_trade_" + exchange + "_index where rank <50 and sold_short_name !='' and sold_short_name !=' ' and data_time between ? and ? and classify_name in (" + utils.GetOrmInReplace(len(classifyNames)) + ") and classify_type in (" + utils.GetOrmInReplace(len(classifyTypes)) + ")"
  229. err = o.Exec(sql2, now, now, startDate, endDate, classifyNames, classifyTypes).Error
  230. return
  231. }
  232. // GetTradePositionTopOriginDataTimes 获取榜单原始数据日期-正序
  233. func GetTradePositionTopOriginDataTimes(exchange string) (dates []string, err error) {
  234. o := global.DbMap[utils.DbNameIndex]
  235. sql := `SELECT DISTINCT data_time FROM base_from_trade_%s_index ORDER BY data_time ASC`
  236. sql = fmt.Sprintf(sql, exchange)
  237. err = o.Raw(sql).Scan(&dates).Error
  238. return
  239. }
  240. // BaseFromTradeClassify 交易所分类表
  241. type BaseFromTradeClassify struct {
  242. Id uint64 `orm:"column(id);pk" gorm:"primaryKey"`
  243. ClassifyName string //分类名称
  244. ClassifyType string //分类名称下的类型
  245. Exchange string //交易所
  246. LatestDate time.Time //数据最近的日期
  247. CreateTime time.Time //插入时间
  248. ModifyTime time.Time //修改时间
  249. }
  250. // GetAllBaseFromTradeClassify 获取所有的交易所分类列表
  251. func GetAllBaseFromTradeClassify() (list []*BaseFromTradeClassify, err error) {
  252. sql := `SELECT * FROM base_from_trade_classify `
  253. o := global.DbMap[utils.DbNameIndex]
  254. err = o.Raw(sql).Find(&list).Error
  255. return
  256. }
  257. // Update 更新
  258. func (m *BaseFromTradeClassify) Update(cols []string) (err error) {
  259. o := global.DbMap[utils.DbNameIndex]
  260. err = o.Select(cols).Updates(m).Error
  261. return
  262. }
  263. // MultiAddBaseFromTradeClassify 批量插入交易所分类
  264. func MultiAddBaseFromTradeClassify(items []*BaseFromTradeClassify) (err error) {
  265. if len(items) == 0 {
  266. return
  267. }
  268. o := global.DbMap[utils.DbNameIndex]
  269. err = o.CreateInBatches(items, utils.MultiAddNum).Error
  270. return
  271. }
  272. type TradeClassifyName struct {
  273. ClassifyName string //分类名称
  274. ClassifyType string //分类名称下的类型
  275. DataTime time.Time //数据最近的日期
  276. ModifyTime time.Time //数据最近的日期
  277. }
  278. // GetExchangeClassify 获取交易所分类列表
  279. func GetExchangeClassify(exchange string) (list []TradeClassifyName, err error) {
  280. tableName := "trade_position_" + exchange + "_top"
  281. orderStr := "classify_name DESC, classify_type asc"
  282. if exchange == "zhengzhou" {
  283. orderStr = "classify_name asc"
  284. }
  285. sql := `SELECT classify_name, classify_type,MAX(data_time) as data_time,MAX(modify_time) as modify_time FROM ` + tableName + ` GROUP BY classify_name, classify_type `
  286. sql += ` ORDER BY ` + orderStr
  287. o := global.DbMap[utils.DbNameIndex]
  288. err = o.Raw(sql).Find(&list).Error
  289. return
  290. }
  291. // GetTradePositionTopCleanByExchangeDataTime 根据时间查询净多单和净空单的值
  292. func GetTradePositionTopCleanByExchangeDataTime(exchange string, startDate, endDate string) (list []*TradePositionTop, err error) {
  293. o := global.DbMap[utils.DbNameIndex]
  294. sql := "SELECT * FROM trade_position_" + exchange + "_top where data_time >= ? and data_time <= ? and deal_type in (3,4) ORDER BY classify_name, classify_type, deal_type, data_time, deal_value desc"
  295. err = o.Raw(sql, startDate, endDate).Find(&list).Error
  296. return
  297. }
  298. // GetTradePositionTopCleanByExchangeDataTimeClassify 根据时间查询净多单和净空单的值
  299. func GetTradePositionTopCleanByExchangeDataTimeClassify(exchange string, startDate, endDate string, classifyNames, classifyTypes []string) (list []*TradePositionTop, err error) {
  300. o := global.DbMap[utils.DbNameIndex]
  301. sql := `SELECT * FROM trade_position_` + exchange + `_top where data_time >= ? and data_time <= ? and deal_type in (3,4) and classify_name in (` + utils.GetOrmInReplace(len(classifyNames)) + `) and classify_type in (` + utils.GetOrmInReplace(len(classifyTypes)) + `) ORDER BY classify_name, classify_type, deal_type, data_time, deal_value desc`
  302. err = o.Raw(sql, startDate, endDate, classifyNames, classifyTypes).Find(&list).Error
  303. return
  304. }
  305. // MultiUpdatePositionTopChangeVal 批量更新榜单里变化量的值
  306. func MultiUpdatePositionTopChangeVal(exchange string, updates []UpdateChangeVal) (err error) {
  307. o := global.DbMap[utils.DbNameIndex]
  308. sql := "UPDATE trade_position_" + exchange + "_top SET deal_change=?, modify_time=? WHERE id = ?"
  309. for _, v := range updates {
  310. err = o.Exec(sql, v.DealChange, v.ModifyTime, v.Id).Error
  311. if err != nil {
  312. return
  313. }
  314. }
  315. return
  316. }
  317. func GetTradePositionOriginClassifyCountByExchangeDataTime(exchange string, startDate, endDate string) (count int64, err error) {
  318. o := global.DbMap[utils.DbNameIndex]
  319. var pars []interface{}
  320. var sql string
  321. if utils.DbDriverName == utils.DbDriverByDm {
  322. // DM不支持COUNT(DISTINCT column1, column2)这种写法
  323. sql = `SELECT COUNT(*)
  324. FROM (
  325. SELECT DISTINCT classify_name, classify_type
  326. FROM base_from_trade_ine_index
  327. WHERE rank < 50
  328. AND (buy_short_name != '' OR sold_short_name != '')
  329. AND (buy_short_name != ' ' OR sold_short_name != ' ')
  330. AND data_time >= ?
  331. AND data_time <= ?
  332. )`
  333. pars = append(pars, startDate, endDate)
  334. } else {
  335. sql = "SELECT COUNT(DISTINCT classify_name, classify_type) FROM base_from_trade_" + exchange + "_index where rank <50 and (buy_short_name !='' or sold_short_name !='' ) and (buy_short_name !=' ' or sold_short_name !=' ' ) and data_time >= ? and data_time <= ?"
  336. pars = append(pars, startDate, endDate)
  337. }
  338. err = o.Raw(sql, pars...).Scan(&count).Error
  339. return
  340. }
  341. func GetTradePositionTopClassifyCountByExchangeDataTime(exchange string, startDate, endDate string) (count int64, err error) {
  342. o := global.DbMap[utils.DbNameIndex]
  343. var pars []interface{}
  344. var sql string
  345. if utils.DbDriverName == utils.DbDriverByDm {
  346. sql = `SELECT COUNT(*)
  347. FROM (
  348. SELECT DISTINCT classify_name, classify_type
  349. FROM trade_position_ine_top
  350. WHERE data_time >= ?
  351. AND data_time <= ?
  352. AND deal_type IN (1, 2)
  353. )`
  354. pars = append(pars, startDate, endDate)
  355. } else {
  356. sql = "SELECT COUNT(DISTINCT classify_name, classify_type) FROM trade_position_" + exchange + "_top where data_time >= ? and data_time <= ? and deal_type in (1,2) "
  357. pars = append(pars, startDate, endDate)
  358. }
  359. err = o.Raw(sql, pars...).Scan(&count).Error
  360. return
  361. }
  362. type TradePositionClassifyInfo struct {
  363. ClassifyName string //分类名称
  364. ClassifyType string //分类名称下的类型
  365. }
  366. func GetTradePositionOriginClassifyByExchangeDataTime(exchange string, startDate, endDate string) (list []TradePositionClassifyInfo, err error) {
  367. o := global.DbMap[utils.DbNameIndex]
  368. sql := "SELECT DISTINCT classify_name, classify_type FROM base_from_trade_" + exchange + "_index where rank <50 and (buy_short_name !='' or sold_short_name !='' ) and (buy_short_name !=' ' or sold_short_name !=' ' ) and data_time >= ? and data_time <= ?"
  369. err = o.Raw(sql, startDate, endDate).Find(&list).Error
  370. return
  371. }
  372. func GetTradePositionTopClassifyByExchangeDataTime(exchange string, startDate, endDate string) (list []TradePositionClassifyInfo, err error) {
  373. o := global.DbMap[utils.DbNameIndex]
  374. sql := "SELECT DISTINCT classify_name, classify_type FROM trade_position_" + exchange + "_top where data_time >= ? and data_time <= ? and deal_type in (1,2) "
  375. err = o.Raw(sql, startDate, endDate).Find(&list).Error
  376. return
  377. }
  378. // DeleteTradePositionTopAllByExchangeDataTime 删除计算数据
  379. func DeleteTradePositionTopAllByExchangeDataTime(exchange string, startDate, endDate string) (err error) {
  380. o := global.DbMap[utils.DbNameIndex]
  381. sql := "DELETE FROM trade_position_" + exchange + "_top where data_time >= ? and data_time <= ? "
  382. err = o.Exec(sql, startDate, endDate).Error
  383. return
  384. }
  385. type GetFirstBaseFromTradeIndeDate struct {
  386. DataTime string
  387. }
  388. func GetFirstBaseFromTradeIndexByDate(exchange string) (item *GetFirstBaseFromTradeIndeDate, err error) {
  389. o := global.DbMap[utils.DbNameIndex]
  390. sql := "SELECT * FROM base_from_trade_" + exchange + "_index where rank < 50 order by data_time asc"
  391. err = o.Raw(sql).First(&item).Error
  392. return
  393. }
  394. // 获取持仓分析的最新数据
  395. func GetTradePositionTopLastedDataTime(exchange string) (dataTime time.Time, err error) {
  396. o := global.DbMap[utils.DbNameIndex]
  397. sql := "SELECT max(data_time) FROM trade_position_" + exchange + "_top"
  398. var timeNull sql2.NullTime
  399. err = o.Raw(sql).Scan(&timeNull).Error
  400. if err != nil {
  401. return
  402. }
  403. if timeNull.Valid {
  404. dataTime = timeNull.Time
  405. }
  406. return
  407. }