入坑!SQLAlchemy 和 GORM 的 first() 函數

一樣是 first 函數,在 SQLAlchemy 和 GORM 卻有不同的行為?使用上該怎麼區分?

由於之前主力語言是 python,orm 大多使用 SQLAlchemy。學習 golang 時轉用 GORM 作為 orm,容易把之前在 SQLAlchemy 的語法習慣帶入使用,導致入莫名的坑!今日我們來看看兩者的 first 函數差異。

差異

假設有一張表 test_score 定義如下

ID (int)date (date)score (int)
12021-02-28100
22021-03-0187
32021-03-0267

此時如果我心中想要達成以下 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

Written by J
雖然大學唸的是生物,但持著興趣與熱情自學,畢業後轉戰硬體工程師,與宅宅工程師們一起過著沒日沒夜的生活,做著台灣最薄的 intel 筆電,要與 macbook air 比拼。 離開後,憑著一股傻勁與朋友創業,再度轉戰軟體工程師,一手扛起前後端、雙平台 app 開發,過程中雖跌跌撞撞,卻也累計不少經驗。 可惜不是那 1% 的成功人士,於是加入其他成功人士的新創公司,專職開發後端。沒想到卻在採前人坑的過程中,拓寬了眼界,得到了深層的領悟。