原生查询
通过 Prisma ORM 5.19.0
,我们发布了 TypedSQL。TypedSQL 是一种编写类型安全且更易于集成到您工作流程中的 SQL 查询的新方法。
我们强烈建议您尽可能使用 TypedSQL 查询,而不是下面描述的传统原生查询。
Prisma Client 支持向您的数据库发送原生查询的选项。您可能希望在以下情况下使用原生查询:
- 您想运行高度优化的查询
- 您需要 Prisma Client 尚未支持的功能(请考虑提出一个问题)
Prisma ORM 支持的所有关系型数据库都支持原生查询。此外,从版本 3.9.0
开始,MongoDB 也支持原生查询。更多详情请参阅相关章节:
关系型数据库的原生查询
对于关系型数据库,Prisma Client 提供了四种方法,允许您发送原生查询。您可以使用:
$queryRaw
用于返回实际记录(例如,使用SELECT
)。$executeRaw
用于返回受影响的行数(例如,在UPDATE
或DELETE
之后)。$queryRawUnsafe
用于使用原生字符串返回实际记录(例如,使用SELECT
)。$executeRawUnsafe
用于使用原生字符串返回受影响的行数(例如,在UPDATE
或DELETE
之后)。
名称中包含“Unsafe”的方法更加灵活,但极有可能使您的代码容易受到 SQL 注入攻击。
另外两种方法使用简单的模板标签、无需字符串构建和拼接,使用起来很安全。但是,对于更复杂的用例需要谨慎,因为如果以某些方式使用这些方法,仍然可能导致 SQL 注入。更多详情请参阅下面的SQL 注入防范章节。
注意:上述列表中的所有方法一次只能运行一条查询。您不能附加第二条查询——例如,使用
select 1; select 2;
调用其中任何一个都将不起作用。
$queryRaw
$queryRaw
返回实际的数据库记录。例如,以下 SELECT
查询返回 User
表中每个记录的所有字段
const result = await prisma.$queryRaw`SELECT * FROM User`;
该方法被实现为带标签的模板(tagged template),这允许您传递一个模板字符串,以便轻松插入您的变量。这样,Prisma Client 会创建预处理语句,这些语句可以防止 SQL 注入。
const email = "emelie@prisma.io";
const result = await prisma.$queryRaw`SELECT * FROM User WHERE email = ${email}`;
您还可以使用 Prisma.sql
辅助函数,实际上,$queryRaw
方法仅接受模板字符串或 Prisma.sql
辅助函数。
const email = "emelie@prisma.io";
const result = await prisma.$queryRaw(Prisma.sql`SELECT * FROM User WHERE email = ${email}`);
如果您使用字符串构建将不可信的输入合并到传递给此方法的查询中,则会打开 SQL 注入攻击的可能性。SQL 注入攻击可能导致您的数据被修改或删除。首选的机制是在运行此方法时包含查询文本。有关此风险以及如何防范的示例,请参阅下面的SQL 注入防范章节。
注意事项
请注意:
-
模板变量不能在 SQL 字符串字面量中使用。例如,以下查询将不起作用
const name = "Bob";
await prisma.$queryRaw`SELECT 'My name is ${name}';`;相反,您可以将整个字符串作为变量传递,或者使用字符串拼接。
const name = "My name is Bob";
await prisma.$queryRaw`SELECT ${name};`;const name = "Bob";
await prisma.$queryRaw`SELECT 'My name is ' || ${name};`; -
模板变量只能用于数据值(如上例中的
email
)。变量不能用于标识符,如列名、表名或数据库名,也不能用于 SQL 关键字。例如,以下两个查询将不起作用const myTable = "user";
await prisma.$queryRaw`SELECT * FROM ${myTable};`;const ordering = "desc";
await prisma.$queryRaw`SELECT * FROM Table ORDER BY ${ordering};`; -
Prisma 会将
$queryRaw
和$queryRawUnsafe
返回的任何数据库值映射到其对应的 JavaScript 类型。了解更多。 -
$queryRaw
不支持 PostgreSQL 数据库中的动态表名。了解更多
返回值类型
$queryRaw
返回一个数组。每个对象对应一个数据库记录。
[
{ id: 1, email: "emelie@prisma.io", name: "Emelie" },
{ id: 2, email: "yin@prisma.io", name: "Yin" },
]
您还可以为 $queryRaw
的结果指定类型。
签名
$queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): PrismaPromise<T>;
为 $queryRaw
结果指定类型
PrismaPromise<T>
使用一个泛型类型参数 T
。您可以在调用 $queryRaw
方法时确定 T
的类型。在以下示例中,$queryRaw
返回 User[]
// import the generated `User` type from the `@prisma/client` module
import { User } from "@prisma/client";
const result = await prisma.$queryRaw<User[]>`SELECT * FROM User`;
// result is of type: `User[]`
注意:如果您不提供类型,
$queryRaw
默认为unknown
。
如果您正在选择模型的特定字段或想包含关联关系,如果您想确保结果类型正确,请参阅关于利用 Prisma Client 生成的类型的文档。
使用原生 SQL 时的类型注意事项
当您为 $queryRaw
的结果指定类型时,原生数据可能不总是与建议的 TypeScript 类型匹配。例如,以下 Prisma 模型包含一个名为 published
的 Boolean
字段
model Post {
id Int @id @default(autoincrement())
published Boolean @default(false)
title String
content String?
}
以下查询返回所有帖子。然后它打印出每个 Post
的 published
字段的值。
const result = await prisma.$queryRaw<Post[]>`SELECT * FROM Post`;
result.forEach((x) => {
console.log(x.published);
});
对于常规 CRUD 查询,Prisma Client 查询引擎标准化了所有数据库的返回值类型。使用原生查询则不会。如果数据库提供者是 MySQL,返回的值是 1
或 0
。但是,如果数据库提供者是 PostgreSQL,值是 true
或 false
。
注意:Prisma 将 JavaScript 整数作为
INT8
发送到 PostgreSQL。这可能与您只接受INT4
作为输入的用户定义函数冲突。如果您将$queryRaw
与 PostgreSQL 数据库结合使用,请将输入类型更新为INT8
,或将查询参数强制转换为INT4
。
PostgreSQL 中的动态表名
无法内插表名。这意味着您不能在 $queryRaw
中使用动态表名。相反,您必须使用 $queryRawUnsafe
,如下所示:
let userTable = "User";
let result = await prisma.$queryRawUnsafe(`SELECT * FROM ${userTable}`);
请注意,如果您将 $queryRawUnsafe
与用户输入结合使用,您将面临 SQL 注入攻击的风险。了解更多。
$queryRawUnsafe()
$queryRawUnsafe()
方法允许您向数据库传递原生字符串(或模板字符串)。
如果您将此方法与用户输入一起使用(换句话说,SELECT * FROM table WHERE columnx = ${userInput}
),则会打开 SQL 注入攻击的可能性。SQL 注入攻击可能导致您的数据被修改或删除。
尽可能使用 $queryRaw
方法代替。正确使用 $queryRaw
方法会更安全,但请注意,在某些情况下,$queryRaw
方法也可能变得脆弱。更多信息请参阅下面的SQL 注入防范章节。
以下查询返回 User
表中每个记录的所有字段。
// import the generated `User` type from the `@prisma/client` module
import { User } from "@prisma/client";
const result = await prisma.$queryRawUnsafe("SELECT * FROM User");
您还可以运行参数化查询。以下示例返回电子邮件包含字符串 emelie@prisma.io
的所有用户:
prisma.$queryRawUnsafe("SELECT * FROM users WHERE email = $1", "emelie@prisma.io");
注意:Prisma 将 JavaScript 整数作为
INT8
发送到 PostgreSQL。这可能与您只接受INT4
作为输入的用户定义函数冲突。如果您将参数化的$queryRawUnsafe
查询与 PostgreSQL 数据库结合使用,请将输入类型更新为INT8
,或将查询参数强制转换为INT4
。
有关使用参数化查询的更多详情,请参阅下面的参数化查询章节。
签名
$queryRawUnsafe<T = unknown>(query: string, ...values: any[]): PrismaPromise<T>;
$executeRaw
$executeRaw
返回数据库操作影响的行数,例如 UPDATE
或 DELETE
。此函数不返回数据库记录。以下查询更新数据库中的记录,并返回更新的记录数:
const result: number =
await prisma.$executeRaw`UPDATE User SET active = true WHERE emailValidated = true`;
该方法被实现为带标签的模板(tagged template),这允许您传递一个模板字符串,以便轻松插入您的变量。这样,Prisma Client 会创建预处理语句,这些语句可以防止 SQL 注入。
const emailValidated = true;
const active = true;
const result: number =
await prisma.$executeRaw`UPDATE User SET active = ${active} WHERE emailValidated = ${emailValidated};`;
如果您使用字符串构建将不可信的输入合并到传递给此方法的查询中,则会打开 SQL 注入攻击的可能性。SQL 注入攻击可能导致您的数据被修改或删除。首选的机制是在运行此方法时包含查询文本。有关此风险以及如何防范的示例,请参阅下面的SQL 注入防范章节。
注意事项
请注意:
-
$executeRaw
不支持单个字符串中的多个查询(例如,同时执行ALTER TABLE
和CREATE TABLE
)。 -
Prisma Client 提交预处理语句,而预处理语句只允许 SQL 语句的一个子集。例如,不允许使用
START TRANSACTION
。您可以在此处了解 MySQL 在预处理语句中允许的语法。 -
PREPARE
不支持ALTER
- 请参阅解决方法。 -
模板变量不能在 SQL 字符串字面量中使用。例如,以下查询将不起作用
const name = "Bob";
await prisma.$executeRaw`UPDATE user SET greeting = 'My name is ${name}';`;相反,您可以将整个字符串作为变量传递,或者使用字符串拼接。
const name = "My name is Bob";
await prisma.$executeRaw`UPDATE user SET greeting = ${name};`;const name = "Bob";
await prisma.$executeRaw`UPDATE user SET greeting = 'My name is ' || ${name};`; -
模板变量只能用于数据值(如上例中的
email
)。变量不能用于标识符,如列名、表名或数据库名,也不能用于 SQL 关键字。例如,以下两个查询将不起作用const myTable = "user";
await prisma.$executeRaw`UPDATE ${myTable} SET active = true;`;const ordering = "desc";
await prisma.$executeRaw`UPDATE User SET active = true ORDER BY ${desc};`;
返回值类型
$executeRaw
返回一个 number
。
签名
$executeRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): PrismaPromise<number>;
$executeRawUnsafe()
$executeRawUnsafe()
方法允许您向数据库传递原生字符串(或模板字符串)。与 $executeRaw
一样,它不返回数据库记录,而是返回受影响的行数。
如果您将此方法与用户输入一起使用(换句话说,SELECT * FROM table WHERE columnx = ${userInput}
),则会打开 SQL 注入攻击的可能性。SQL 注入攻击可能导致您的数据被修改或删除。
尽可能使用 $executeRaw
方法代替。正确使用 $executeRaw
方法会更安全,但请注意,在某些情况下,$executeRaw
方法也可能变得脆弱。更多信息请参阅下面的SQL 注入防范章节。
以下示例使用模板字符串更新数据库中的记录。然后它返回更新的记录数:
const emailValidated = true;
const active = true;
const result = await prisma.$executeRawUnsafe(
`UPDATE User SET active = ${active} WHERE emailValidated = ${emailValidated}`
);
同样可以写成参数化查询:
const result = prisma.$executeRawUnsafe(
"UPDATE User SET active = $1 WHERE emailValidated = $2",
"yin@prisma.io",
true
);
有关使用参数化查询的更多详情,请参阅下面的参数化查询章节。
签名
$executeRawUnsafe<T = unknown>(query: string, ...values: any[]): PrismaPromise<number>;
原生查询类型映射
Prisma 将 $queryRaw
和 $queryRawUnsafe
返回的任何数据库值映射到其对应的JavaScript 类型。此行为与常规的 Prisma 查询方法(如 findMany()
)相同。
功能可用性
- 在 v3.14.x 和 v3.15.x 中,原生查询类型映射通过预览功能
improvedQueryRaw
可用。我们在 4.0.0 版本中使原生查询类型映射正式可用(GA),因此在 4.0.0 或更高版本中您无需使用improvedQueryRaw
。 - 在 4.0.0 版本之前,原生查询类型映射对 SQLite 不可用。
例如,一个从表中选择包含 BigInt
、Bytes
、Decimal
和 Date
类型的列的原生查询如下:
const result = await prisma.$queryRaw`SELECT bigint, bytes, decimal, date FROM "Table";`;
console.log(result);
{ bigint: BigInt("123"), bytes: <Buffer 01 02>), decimal: Decimal("12.34"), date: Date("<some_date>") }
在 result
对象中,数据库值已被映射到对应的 JavaScript 类型。
下表显示了数据库中使用的类型与原生查询返回的 JavaScript 类型之间的转换:
数据库类型 | JavaScript 类型 |
---|---|
Text | String |
32 位整数 | Number |
32 位无符号整数 | BigInt |
浮点数 | Number |
双精度数 | Number |
64 位整数 | BigInt |
Decimal / numeric | Decimal |
Bytes | Uint8Array (v6 之前:Buffer ) |
Json | Object |
DateTime | Date |
Date | Date |
Time | Date |
Uuid | String |
Xml | String |
请注意,每种数据库类型的具体名称在不同数据库之间会有所不同——例如,布尔类型在 PostgreSQL 中称为 boolean
,在 CockroachDB 中称为 STRING
。有关每种数据库的类型名称的完整详细信息,请参阅标量类型参考。
原生查询类型转换行为
使用 Prisma Client 进行原生查询可能需要参数符合 SQL 函数或查询的预期类型。Prisma Client 不会进行细微的隐式转换。
例如,以下使用 PostgreSQL 的 LENGTH
函数的查询,该函数只接受 text
类型作为输入:
await prisma.$queryRaw`SELECT LENGTH(${42});`;
此查询将返回错误:
// ERROR: function length(integer) does not exist
// HINT: No function matches the given name and argument types. You might need to add explicit type casts.
在这种情况下,解决方法是将 42
显式转换为 text
类型:
await prisma.$queryRaw`SELECT LENGTH(${42}::text);`;
功能可用性:此功能自 4.0.0 版本起已正式可用(GA)。在 v3.14.x 和 v3.15.x 中,它通过预览功能 improvedQueryRaw
可用。
对于 4.0.0 版本之前的上述示例,Prisma ORM 会静默地将 42
强制转换为 text
,并且不需要显式转换。
另一方面,以下原生查询现在可以正常工作并返回一个整数结果,而之前则会失败:
await prisma.$queryRaw`SELECT ${1.5}::int as int`;
// Now: [{ int: 2 }]
// Before: db error: ERROR: incorrect binary data format in bind parameter 1
事务
在 2.10.0 及更高版本中,您可以在事务中使用 .$executeRaw()
和 .$queryRaw()
。
使用变量
$executeRaw
和 $queryRaw
被实现为带标签的模板(tagged templates)。带标签的模板是 Prisma Client 中使用原生 SQL 变量的推荐方式。
以下示例包含一个名为 ${userId}
的占位符:
const userId = 42;
const result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${userId};`;
✔ 使用 $queryRaw
和 $executeRaw
的带标签模板版本的好处包括:
- Prisma Client 会转义所有变量。
- 带标签的模板与数据库无关——您无需记住变量是应该写成
$1
(PostgreSQL) 还是?
(MySQL)。 - SQL Template Tag 提供了有用的辅助函数。
- 嵌入式的命名变量更易于阅读。
注意:您不能将表名或列名传递到带标签模板的占位符中。例如,您不能根据某些条件执行
SELECT ?
并传入*
或id, name
。
带标签模板辅助函数
Prisma Client 特别使用了 SQL Template Tag,它公开了许多辅助函数。例如,以下查询使用 join()
传递一个 ID 列表:
import { Prisma } from "@prisma/client";
const ids = [1, 3, 5, 10, 20];
const result = await prisma.$queryRaw`SELECT * FROM User WHERE id IN (${Prisma.join(ids)})`;
以下示例使用 empty
和 sql
辅助函数,根据 userName
是否为空来更改查询:
import { Prisma } from "@prisma/client";
const userName = "";
const result = await prisma.$queryRaw`SELECT * FROM User ${
userName ? Prisma.sql`` : Prisma.empty // Cannot use "" or NULL here!
}`;
ALTER
限制 (PostgreSQL)
PostgreSQL 不支持在预处理语句中使用 ALTER
,这意味着以下查询将不起作用:
await prisma.$executeRaw`ALTER USER prisma WITH PASSWORD "${password}"`;
await prisma.$executeRaw(Prisma.sql`ALTER USER prisma WITH PASSWORD "${password}"`);
您可以使用以下查询,但请注意,这可能不安全,因为 ${password}
未被转义:
await prisma.$executeRawUnsafe('ALTER USER prisma WITH PASSWORD "$1"', password})
不支持的类型
Unsupported
类型在使用 $queryRaw
或 $queryRawUnsafe
之前需要强制转换为 Prisma Client 支持的类型。例如,以下模型有一个类型为 Unsupported
的 location
字段:
model Country {
location Unsupported("point")?
}
对不支持的字段执行以下查询将不起作用:
await prisma.$queryRaw`SELECT location FROM Country;`;
相反,将 Unsupported
字段强制转换为任何受支持的 Prisma Client 类型,如果您的 Unsupported
列支持此强制转换。
您可能希望将 Unsupported
列强制转换成的最常见类型是 String
。例如,在 PostgreSQL 中,这将映射到 text
类型:
await prisma.$queryRaw`SELECT location::text FROM Country;`;
数据库因此将提供您的数据的 String
表示,Prisma Client 支持此表示。
有关支持的 Prisma 类型的详细信息,请参阅相关数据库的Prisma 连接器概述。
SQL 注入防范
在 Prisma Client 中避免 SQL 注入的理想方法是尽可能使用 ORM 模型执行查询。
如果无法做到这一点并且需要原生查询,Prisma Client 提供了各种原生方法,但安全地使用这些方法非常重要。
本节将提供安全和不安全使用这些方法的各种示例。您可以在Prisma Playground 中测试这些示例。
在 $queryRaw
和 $executeRaw
中
$queryRaw
和 $executeRaw
的简单安全用法
当您使用带标签的模板时,这些方法可以通过转义所有变量来减轻 SQL 注入的风险,并将所有查询作为预处理语句发送。
$queryRaw`...`; // Tagged template
$executeRaw`...`; // Tagged template
以下示例安全 ✅,可防范 SQL 注入
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
const result = await prisma.$queryRaw`SELECT id, name FROM "User" WHERE name = ${inputString}`;
console.log(result);
$queryRaw
和 $executeRaw
的不安全用法
但是,也可以以不安全的方式使用这些方法。
一种方法是人为地生成一个不安全地拼接用户输入的带标签模板。
以下示例易受 ❌ SQL 注入攻击
// Unsafely generate query text
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`; // SQL Injection
const query = `SELECT id, name FROM "User" WHERE name = ${inputString}`;
// Version for Typescript
const stringsArray: any = [...[query]];
// Version for Javascript
const stringsArray = [...[query]];
// Use the `raw` property to impersonate a tagged template
stringsArray.raw = [query];
// Use queryRaw
const result = await prisma.$queryRaw(stringsArray);
console.log(result);
另一种使这些方法易受攻击的方式是滥用 Prisma.raw
函数。
以下示例都易受 ❌ SQL 注入攻击
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
const result = await prisma.$queryRaw`SELECT id, name FROM "User" WHERE name = ${Prisma.raw(
inputString
)}`;
console.log(result);
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
const result = await prisma.$queryRaw(
Prisma.raw(`SELECT id, name FROM "User" WHERE name = ${inputString}`)
);
console.log(result);
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
const query = Prisma.raw(`SELECT id, name FROM "User" WHERE name = ${inputString}`);
const result = await prisma.$queryRaw(query);
console.log(result);
在更复杂的场景中安全使用 $queryRaw
和 $executeRaw
将原生查询构建与查询执行分离
如果您想在其他地方构建原生查询或将其与参数分离,您将需要使用以下方法之一。
在此示例中,使用 sql
辅助方法通过安全地包含变量来构建查询文本。它安全 ✅,可防范 SQL 注入
// inputString can be untrusted input
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
// Safe if the text query below is completely trusted content
const query = Prisma.sql`SELECT id, name FROM "User" WHERE name = ${inputString}`;
const result = await prisma.$queryRaw(query);
console.log(result);
在此示例中,它安全 ✅,可防范 SQL 注入,使用 sql
辅助方法构建查询文本,包括输入值的参数标记。每个变量都由一个标记符号表示(MySQL 用 ?
,PostgreSQL 用 $1
、$2
等)。请注意,示例仅显示 PostgreSQL 查询。
// Version for Typescript
const query: any;
// Version for Javascript
const query;
// Safe if the text query below is completely trusted content
query = Prisma.sql`SELECT id, name FROM "User" WHERE name = $1`;
// inputString can be untrusted input
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
query.values = [inputString];
const result = await prisma.$queryRaw(query);
console.log(result);
注意:PostgreSQL 变量由
$1
等表示
在其他地方或分阶段构建原生查询
如果您想在查询执行以外的其他地方构建原生查询,理想的方法是从查询的各个部分创建一个 Sql
对象,并将参数值传递给它。
在以下示例中,我们有两个变量需要参数化。只要传递给 Prisma.sql
的查询字符串只包含可信内容,此示例就是安全 ✅ 的,可防范 SQL 注入
// Example is safe if the text query below is completely trusted content
const query1 = `SELECT id, name FROM "User" WHERE name = `; // The first parameter would be inserted after this string
const query2 = ` OR name = `; // The second parameter would be inserted after this string
const inputString1 = "Fred";
const inputString2 = `'Sarah' UNION SELECT id, title FROM "Post"`;
const query = Prisma.sql([query1, query2, ""], inputString1, inputString2);
const result = await prisma.$queryRaw(query);
console.log(result);
注意:请注意,作为
Prisma.sql
的第一个参数传递的字符串数组需要在末尾有一个空字符串,因为sql
函数期望查询段的数量比参数数量多一个。
如果您想将原生查询构建成一个大字符串,这仍然是可能的,但这需要一些注意,因为它使用了潜在危险的 Prisma.raw
方法。您还需要使用适合您数据库的正确参数标记来构建查询,因为 Prisma 将无法像通常那样为相关数据库提供标记。
以下示例安全 ✅,可防范 SQL 注入,只要传递给 Prisma.raw
的查询字符串只包含可信内容
// Version for Typescript
const query: any;
// Version for Javascript
const query;
// Example is safe if the text query below is completely trusted content
const query1 = `SELECT id, name FROM "User" `;
const query2 = `WHERE name = $1 `;
query = Prisma.raw(`${query1}${query2}`);
// inputString can be untrusted input
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
query.values = [inputString];
const result = await prisma.$queryRaw(query);
console.log(result);
在 $queryRawUnsafe
和 $executeRawUnsafe
中
不安全地使用 $queryRawUnsafe
和 $executeRawUnsafe
如果您不能使用带标签的模板,可以改为使用 $queryRawUnsafe
或 $executeRawUnsafe
。但是,请注意这些函数会显著增加您的代码中存在 SQL 注入漏洞的风险。
以下示例拼接了 query
和 inputString
。Prisma Client ❌ 无法在此示例中转义 inputString
,这使得它易受 SQL 注入攻击
const inputString = '"Sarah" UNION SELECT id, title, content FROM Post'; // SQL Injection
const query = "SELECT id, name, email FROM User WHERE name = " + inputString;
const result = await prisma.$queryRawUnsafe(query);
console.log(result);
参数化查询
作为带标签模板的替代方案,$queryRawUnsafe
支持标准参数化查询,其中每个变量都由一个符号表示(MySQL 用 ?
,PostgreSQL 用 $1
、$2
等)。请注意,示例仅显示 PostgreSQL 查询。
以下示例安全 ✅,可防范 SQL 注入
const userName = "Sarah";
const email = "sarah@prisma.io";
const result = await prisma.$queryRawUnsafe(
"SELECT * FROM User WHERE (name = $1 OR email = $2)",
userName,
email
);
注意:PostgreSQL 变量由
$1
和$2
表示
与带标签的模板一样,Prisma Client 在以此方式提供变量时会转义所有变量。
注意:您不能将表名或列名作为变量传递到参数化查询中。例如,您不能根据某些条件执行
SELECT ?
并传入*
或id, name
。
参数化的 PostgreSQL ILIKE
查询
使用 ILIKE
时,通配符字符 (%
) 应包含在变量本身中,而不是查询 (string
) 中。此示例对 SQL 注入 ✅ 是安全的。
const userName = "Sarah";
const emailFragment = "prisma.io";
const result = await prisma.$queryRawUnsafe(
'SELECT * FROM "User" WHERE (name = $1 OR email ILIKE $2)',
userName,
`%${emailFragment}`
);
注意:使用参数
%$2
将不起作用。
MongoDB 的原生查询
对于版本 3.9.0
及更高版本的 MongoDB,Prisma Client 公开了三个方法,允许您发送原生查询。您可以使用:
$runCommandRaw
来对数据库运行命令<model>.findRaw
来查找匹配过滤器的零个或多个文档。<model>.aggregateRaw
来对集合执行聚合操作。
$runCommandRaw()
$runCommandRaw()
对数据库运行原生 MongoDB 命令。作为输入,它接受所有 MongoDB 数据库命令,但以下情况除外:
find
(请改用findRaw()
)aggregate
(请改用aggregateRaw()
)
当您使用 $runCommandRaw()
运行 MongoDB 数据库命令时,请注意以下事项:
- 您调用
$runCommandRaw()
时传递的对象必须遵循 MongoDB 数据库命令的语法。 - 您必须使用适合该 MongoDB 数据库命令的适当角色连接到数据库。
在以下示例中,一个查询插入两条具有相同 _id
的记录。这绕过了正常的文档验证。
prisma.$runCommandRaw({
insert: "Pets",
bypassDocumentValidation: true,
documents: [
{
_id: 1,
name: "Felinecitas",
type: "Cat",
breed: "Russian Blue",
age: 12,
},
{
_id: 1,
name: "Nao Nao",
type: "Dog",
breed: "Chow Chow",
age: 2,
},
],
});
不要将 $runCommandRaw()
用于包含 "find"
或 "aggregate"
命令的查询,因为您可能无法获取所有数据。这是因为 MongoDB 返回一个附加到您的 MongoDB 会话的 游标,并且您可能不会每次都命中同一个 MongoDB 会话。对于这些查询,您应该改用专门的 findRaw()
和 aggregateRaw()
方法。
返回类型
$runCommandRaw()
返回一个 JSON
对象,其形状取决于输入。
签名
$runCommandRaw(command: InputJsonObject): PrismaPromise<JsonObject>;
findRaw()
<model>.findRaw()
返回实际的数据库记录。它将在 User
集合上查找匹配过滤器的零个或多个文档。
const result = await prisma.user.findRaw({
filter: { age: { $gt: 25 } },
options: { projection: { _id: false } },
});
返回类型
<model>.findRaw()
返回一个 JSON
对象,其形状取决于输入。
签名
<model>.findRaw(args?: {filter?: InputJsonObject, options?: InputJsonObject}): PrismaPromise<JsonObject>;
aggregateRaw()
<model>.aggregateRaw()
返回聚合后的数据库记录。它将在 User
集合上执行聚合操作。
const result = await prisma.user.aggregateRaw({
pipeline: [
{ $match: { status: "registered" } },
{ $group: { _id: "$country", total: { $sum: 1 } } },
],
});
返回类型
<model>.aggregateRaw()
返回一个 JSON
对象,其形状取决于输入。
签名
<model>.aggregateRaw(args?: {pipeline?: InputJsonObject[], options?: InputJsonObject}): PrismaPromise<JsonObject>;
pipeline
:用于通过 聚合管道 处理和转换文档流的聚合阶段数组。options
:传递给aggregate
命令 的附加选项。
注意事项
处理 ObjectId
或 Date
等自定义对象时,您必须按照 MongoDB 扩展 JSON 规范 传递它们。示例:
const result = await prisma.user.aggregateRaw({
pipeline: [
{ $match: { _id: { $oid: id } } }
// ^ notice the $oid convention here
],
});