子查詢
假設您有兩個模型,Post
和 Reaction
,並設定了一對多關聯,使得一個貼文有多個反應。
const Post = sequelize.define(
'post',
{
content: DataTypes.STRING,
},
{ timestamps: false },
);
const Reaction = sequelize.define(
'reaction',
{
type: DataTypes.STRING,
},
{ timestamps: false },
);
Post.hasMany(Reaction);
Reaction.belongsTo(Post);
注意:我們停用了時間戳記,只是為了讓接下來的範例查詢更簡短。
讓我們在表格中填入一些資料
async function makePostWithReactions(content, reactionTypes) {
const post = await Post.create({ content });
await Reaction.bulkCreate(reactionTypes.map(type => ({ type, postId: post.id })));
return post;
}
await makePostWithReactions('Hello World', [
'Like',
'Angry',
'Laugh',
'Like',
'Like',
'Angry',
'Sad',
'Like',
]);
await makePostWithReactions('My Second Post', ['Laugh', 'Laugh', 'Like', 'Laugh']);
現在,我們準備好開始了解子查詢的強大之處。
假設我們想透過 SQL 計算每個貼文的 laughReactionsCount
。我們可以使用子查詢來達成,如下所示
SELECT
*,
(
SELECT COUNT(*)
FROM reactions AS reaction
WHERE
reaction.postId = post.id
AND
reaction.type = "Laugh"
) AS laughReactionsCount
FROM posts AS post
如果我們透過 Sequelize 執行上述原始 SQL 查詢,我們會得到
[
{
"id": 1,
"content": "Hello World",
"laughReactionsCount": 1
},
{
"id": 2,
"content": "My Second Post",
"laughReactionsCount": 3
}
]
那麼,我們如何透過 Sequelize 的更多協助來達成這個目標,而無需手動編寫整個原始查詢呢?
答案是:將尋找器方法(例如 findAll
)的 attributes
選項與 sequelize.literal
實用函數結合使用,這個函數允許您直接將任意內容插入查詢中,而無需任何自動跳脫。
這表示 Sequelize 將協助您處理主要的、較大的查詢,但您仍然必須自己編寫該子查詢
Post.findAll({
attributes: {
include: [
[
// Note the wrapping parentheses in the call below!
sequelize.literal(`(
SELECT COUNT(*)
FROM reactions AS reaction
WHERE
reaction.postId = post.id
AND
reaction.type = "Laugh"
)`),
'laughReactionsCount',
],
],
},
});
重要注意事項:由於 sequelize.literal
將任意內容插入查詢中而不進行跳脫,因此值得特別注意,因為它可能是(重大)安全漏洞的來源。不應在使用者產生的內容上使用。 然而,在這裡,我們使用 sequelize.literal
與一個固定的字串,由我們(程式設計師)仔細編寫。這是沒問題的,因為我們知道自己在做什麼。
上述程式碼會產生以下輸出
[
{
"id": 1,
"content": "Hello World",
"laughReactionsCount": 1
},
{
"id": 2,
"content": "My Second Post",
"laughReactionsCount": 3
}
]
成功!
使用子查詢進行複雜排序
這個想法可用於啟用複雜排序,例如按照貼文獲得的「笑」反應次數進行排序
Post.findAll({
attributes: {
include: [
[
sequelize.literal(`(
SELECT COUNT(*)
FROM reactions AS reaction
WHERE
reaction.postId = post.id
AND
reaction.type = "Laugh"
)`),
'laughReactionsCount',
],
],
},
order: [[sequelize.literal('laughReactionsCount'), 'DESC']],
});
結果
[
{
"id": 2,
"content": "My Second Post",
"laughReactionsCount": 3
},
{
"id": 1,
"content": "Hello World",
"laughReactionsCount": 1
}
]