调用存储过程
存储过程适合封装复杂的数据库逻辑——多步操作、条件分支、数据验证和跨表事务。本教程演示如何将一个业务存储过程发布为 API,并在 REST 和 GraphQL 中调用。
场景
图书管理系统需要一个功能:根据作者姓名查询该作者参与合著的所有书籍。这个查询涉及多表关联和字符串匹配,用一个存储过程封装更合适。
第一步:创建存储过程
sql
CREATE PROCEDURE dbo.stp_GetCowrittenBooksByAuthor
@author_name nvarchar(200),
@search_type nvarchar(20) = 'all',
@min_year int = null
AS
BEGIN
SELECT DISTINCT
b.id,
b.title,
b.[year],
b.pages,
a.name AS author_name
FROM dbo.Books b
INNER JOIN dbo.Book_Authors ba ON b.id = ba.book_id
INNER JOIN dbo.Authors a ON ba.author_id = a.id
WHERE a.name LIKE '%' + @author_name + '%'
AND (@search_type = 'all' OR b.genre = @search_type)
AND (@min_year IS NULL OR b.[year] >= @min_year)
ORDER BY b.[year] DESC;
END存储过程接受三个参数:
@author_name(必填):作者姓名,支持模糊匹配。@search_type(可选,默认'all'):按类型筛选。@min_year(可选):最小出版年份。
第二步:添加存储过程实体
bash
dab add GetCowrittenBooksByAuthor \
--source "dbo.stp_GetCowrittenBooksByAuthor" \
--source.type "stored-procedure" \
--parameters.name "author_name,search_type,min_year" \
--parameters.required "true,false,false" \
--parameters.default ",all,null" \
--permissions "anonymous:execute" \
--rest.methods "GET" \
--graphql.operation "query"参数说明:
author_name是必填(required: true),调用时必须提供。search_type和min_year有默认值,可以省略。- REST 使用
GET方法(更适合纯查询类存储过程)。 - GraphQL 挂载在
query下(同样因为是纯查询)。
第三步:启动并测试
bash
dab startREST 调用
GET 方式(参数通过查询字符串)
bash
# 只传必填参数
curl "http://localhost:5000/api/GetCowrittenBooksByAuthor?author_name=张三"
# 传全部参数
curl "http://localhost:5000/api/GetCowrittenBooksByAuthor?author_name=李四&search_type=fiction&min_year=2000"POST 方式(如果配置了 POST)
bash
curl -X POST http://localhost:5000/api/GetCowrittenBooksByAuthor \
-H "Content-Type: application/json" \
-d '{"author_name": "张三", "search_type": "fiction", "min_year": 2000}'GraphQL 调用
存储过程在 GraphQL 中自动加上 execute 前缀:
graphql
{
executeGetCowrittenBooksByAuthor(
author_name: "张三"
search_type: "fiction"
min_year: 2000
) {
id
title
year
author_name
}
}变量形式:
graphql
query GetBooks($author: String!, $type: String) {
executeGetCowrittenBooksByAuthor(
author_name: $author
search_type: $type
) {
id title year author_name
}
}第四步:添加语义描述
为了让 MCP 代理更好地理解这个存储过程,补充描述:
bash
dab update GetCowrittenBooksByAuthor \
--description "根据作者姓名查询该作家的全部合著书籍,包括书名、出版年份和作者名" \
--parameters.description "作者姓名(支持模糊匹配,如'张'可匹配'张三'和'张四'),书籍类型筛选(fiction/nonfiction,默认all不筛选),最小出版年份(null不设下限)"权限注意事项
存储过程使用独立的 execute 权限,与表实体的 CRUD 权限分离:
json
{
"permissions": [
{ "role": "anonymous", "actions": ["execute"] }
]
}*通配符对存储过程仅扩展为execute,不会意外授予 CRUD 权限。- 如果存储过程内部包含数据修改逻辑(INSERT/UPDATE/DELETE),用户不需要额外的
create/update权限——execute权限即允许执行过程内的全部操作。请确保授予execute的角色确实有权执行该过程。
限制和注意事项
| 限制 | 替代方案 |
|---|---|
| 不支持分页筛选排序 | 如果查询需要这些能力,将查询逻辑改写为视图 |
| 仅返回第一个结果集 | 多结果集的过程需要拆分为多个独立过程 |
需要 sys.dm_exec_describe_first_result_set 元数据 | SQL Server 需确保该函数对连接用户可执行 |
| 仅 SQL Server 支持 | PostgreSQL 和 MySQL 用户需使用视图或函数替代 |
如果需要对存储过程的输出进行筛选、分页和排序,更好的方案是将核心查询逻辑写成视图,用视图实体暴露 API,同时保留存储过程用于需要过滤后结果的特定场景。
下一步
- 从数据库表生成 API — 表实体的完整配置。
- 暴露只读视图 — 视图的配置和最佳实践。
- 自定义工具 — 将存储过程注册为 MCP 命名工具。
