一樣是 first 函數,在 SQLAlchemy 和 GORM 卻有不同的行為?使用上該怎麼區分?
由於之前主力語言是 python,orm 大多使用 SQLAlchemy。學習 golang 時轉用 GORM 作為 orm,容易把之前在 SQLAlchemy 的語法習慣帶入使用,導致入莫名的坑!今日我們來看看兩者的 first 函數差異。
差異
假設有一張表 test_score 定義如下
ID (int) | date (date) | score (int) |
1 | 2021-02-28 | 100 |
2 | 2021-03-01 | 87 |
3 | 2021-03-02 | 67 |
此時如果我心中想要達成以下 SQL
SELECT * FROM test_score ORDER BY date DESC LIMIT 1;
在 SQLAlchemy 中會這麼寫
sess.query(TestScore).order_by(TestScore.date.desc()).first()
就能達成一樣的 SQL。
但如果在 GORM 這樣寫
db.Order("date desc").First(&testScore)
build 出來的 SQL 卻是這樣
SELECT * FROM test_score ORDER BY date desc, id LIMIT 1;
多了一個對 id sort 的描述。這在有些情況結果就會和我們預期的不同。
原理
詳細查看兩家的 doc,在 SQLAlchemy 對 first() 函數這麼說:
Return the first result of this Query or None if the result doesn’t contain any row.
first() applies a limit of one within the generated SQL, so that only one primary entity row is generated on the server side (note this may consist of multiple result rows if join-loaded collections are present).
意思是說,他的 first 函數是在 SQL 中增加 LIMIT 1 。
那回頭來看 GORM 呢?
Get the first record ordered by primary key
就這麼簡單一句話XD。如果使用 GORM 的 First,他預設會對 pk 做 order 再取 LIMIT 1。
結論
那如果要在 GORM 達到一開始一樣的 SQL 要怎麼做呢?
db.Order("date desc").Limit(1).Find(&testScore)
這樣就可以啦!
相關文章
用 ForwardRef 解決在 Angular 中遇到的 bug
Python 好慢!datetime 處理優化
發生 Name or service not known?手動設定 Linux DNS 吧!
參考資料
https://docs.sqlalchemy.org/en/14/orm/query.html#sqlalchemy.orm.Query.first
https://gorm.io/docs/query.html