Prisma 中文文档 下载 App

原始 SQL 查询:$queryRaw 和 $executeRaw 回退方案

什么时候需要原始 SQL

Prisma Client 的查询 API 覆盖了绝大多数 CRUD 和关系查询场景。但以下情况你可能需要回退到原生 SQL:

Prisma Client 提供 $queryRaw 和 $executeRaw 两个兜底。

$queryRaw — 执行查询并返回结果

返回行数据,适合 SELECT:

// 简单查询
const result = await prisma.$queryRaw`SELECT * FROM users WHERE active = true`

// 带参数(用模板字符串占位符,Prisma 自动防 SQL 注入)
const email = 'alice@prisma.io'
const user = await prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`

模板字符串 vs 字符串拼接

正确:用 ES6 模板字符串(Prisma 内部处理参数绑定)

const result = await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`

危险:不要用 .unsafe() 或手动拼接字符串

// ❌ 绝对不要这样写!这是 SQL 注入
const result = await prisma.$queryRawUnsafe(
  `SELECT * FROM users WHERE email = '${email}'`
)

$queryRawUnsafe 存在但只有在你能 100% 确定输入安全时才用。

带命名参数(PostgreSQL)

const result = await prisma.$queryRaw`
  SELECT * FROM users
  WHERE email = ${Prisma.sql`${email}`}
  AND created_at > ${Prisma.sql`${sinceDate}`}
`

使用 Prisma.sql 构造片段

const tableName = Prisma.raw('users')  // 注意:raw 用于标识符(表名、列名),仍然需要确保安全
const result = await prisma.$queryRaw`SELECT * FROM ${tableName}`

$executeRaw — 执行语句不返回结果

适合 INSERT / UPDATE / DELETE / DDL:

await prisma.$executeRaw`UPDATE users SET active = false WHERE inactive_days > 30`

// 批量清理历史数据
await prisma.$executeRaw`DELETE FROM logs WHERE created_at < ${cutoffDate}`

实战案例

案例一:窗口函数(Prisma Client 不支持)

// 计算每个用户在每个月的排名
const rankings = await prisma.$queryRaw`
  SELECT
    user_id,
    DATE_TRUNC('month', created_at) as month,
    ROW_NUMBER() OVER (PARTITION BY DATE_TRUNC('month', created_at) ORDER BY total DESC) as rank
  FROM user_stats
`

案例二:PostgreSQL JSONB 查询

// 查询 payload JSON 里包含某个标签的记录
const tag = 'production'
const logs = await prisma.$queryRaw`
  SELECT * FROM logs
  WHERE payload @> ${`{"tags": ["${tag}"]}`}::jsonb
`

案例三:全文搜索

// PostgreSQL 全文搜索
const keyword = 'prisma'
const results = await prisma.$queryRaw`
  SELECT * FROM posts
  WHERE to_tsvector('english', title || ' ' || content) @@ plainto_tsquery('english', ${keyword})
  ORDER BY ts_rank(to_tsvector('english', title || ' ' || content), plainto_tsquery('english', ${keyword})) DESC
`

与事务配合

$queryRaw 和 $executeRaw 可以在 Prisma 事务里使用:

await prisma.$transaction(async (tx) => {
  await tx.$executeRaw`UPDATE accounts SET balance = balance - 100 WHERE id = 1`
  await tx.$executeRaw`UPDATE accounts SET balance = balance + 100 WHERE id = 2`
})

性能考虑