0%

【由腾讯元宝辅助翻译】

文章来源:https://rafiqul.dev/blog/fastapi-deconstructed-anatomy-of-modern-asgi-framework

发布时间:2024-11-19T15:32:14.000Z


最近,我有机会在 PyCon APAC 2024 上深入探讨 FastAPI 的内部机制。演讲的标题是“FastAPI 解密:现代 ASGI 框架的剖析”。随后,我想到为什么不将这次演讲的内容写成文字呢?于是,我决定写一篇博客文章。以下就是这篇文章。

你可以在这里找到演讲的幻灯片:https://github.com/shopnilsazal/fastapi-deconstructed

注意: 为了更好地查看图表,请使用浅色模式阅读本文。


FastAPI 迅速成为 Python 开发者首选的框架之一,尤其是在需要高性能和开发者友好的 API 框架时。凭借对异步编程、依赖注入和自动生成 OpenAPI 文档的支持,FastAPI 以其速度和易用性脱颖而出。本文将分解 FastAPI 的核心组件,详细说明从 ASGI 和 Uvicorn 到 Starlette 和 Pydantic 的各个部分如何协同工作,构建出一个强大且现代的 Web 框架。

Hello World

让我们从 FastAPI 应用程序的基础开始。在 FastAPI 中,一个“Hello World”示例非常简单。

1
2
3
4
5
6
7
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def hello():
return {"message": "Hello, World!"}

通过这样一个简单的设置,FastAPI 可以处理以下任务:

  • 定义异步路由。
  • 解析和验证请求。
  • 序列化 JSON 响应。
  • 自动生成 API 文档。

我们可以通过以下方式运行这个应用程序:

1
uvicorn main:app
1
hypercorn main:app
1
granian --interface asgi main:app

可以看到,有多种方式可以运行我们的应用程序。关键在于,我们需要一个符合 ASGI 协议的服务器来运行应用程序。我们可以使用任何实现 ASGI 协议的服务器。但为了简单起见,在本文中我将使用 uvicorn 作为 ASGI 服务器的示例来解释相关内容。


构建模块

FastAPI 的功能建立在多个强大的组件之上:

  1. ASGI:异步协议层,负责处理服务器与应用程序之间的通信。
  2. Uvicorn:一个高性能的 ASGI 服务器,用于运行 FastAPI 应用程序。
  3. Starlette:一个 ASGI 框架,提供路由、中间件和请求/响应处理功能。
  4. Pydantic:用于数据验证和解析的库,FastAPI 使用它来确保数据的一致性和可靠性。
  5. 依赖注入:内置的依赖注入系统,使得注入数据库连接、服务或配置等依赖变得非常容易。
  6. 自动 API 文档:自动生成 OpenAPI 规范,提供详细的文档和交互功能。
Read more »

(本文由ChatGPT辅助编写)

来源: GitHub Copilot Chat Explained: The Life of a Prompt

GitHub Copilot 在 VS Code 中为开发者提供了强大的 AI 编程助手功能。它通过两种主要功能:内联建议Copilot Chat,帮助开发者更高效地编写代码。在与 Copilot Chat 互动时,GitHub Copilot 会通过一系列复杂的步骤处理你的请求,从本地工作区扫描到与 OpenAI 语言模型的交互。以下是 GitHub Copilot 工作流的详细解析。

GitHub Copilot 工作流

当开发者在 VS Code 中启动 Copilot Chat 并输入带有 @workspace 的问题时,Copilot 会进行以下处理:

  1. 本地上下文收集:Copilot 扩展会检查你的本地工作区,找到与问题相关的代码文件或片段。
  2. 请求发送:通过 HTTPS 协议将请求发送到 Copilot 代理服务器,经过加密处理确保安全。
  3. AI 处理:代理服务器将请求转发至后端的 OpenAI 模型,模型根据上下文生成代码建议或解释。
  4. 响应返回:经过处理的响应返回到 Copilot 扩展,开发者可以在 VS Code 中看到结果。
Read more »

(本文由ChatGPT辅助编写)

随着大型语言模型(LLM)的广泛应用,将网页内容转换为干净、结构化的文本格式已成为数据预处理的重要环节。2024 年 4 月,Jina AI 正式发布了 Jina Reader(https://jina.ai/reader),该 API 通过在 URL 前添加特定前缀,即可将任意网页转换为对 LLM 更友好的 Markdown 格式。随后,在同年 9 月,Jina 又推出了两款专门用于 HTML 转 Markdown 的小型语言模型—— reader-lm-0.5breader-lm-1.5b。而在最新版本中,Jina 又正式发布了拥有 15 亿参数的 ReaderLM-v2,它不仅能精确转换 HTML 为格式美观的 Markdown,还可以直接生成 JSON 数据,实现了更高的准确性和长文本处理能力(支持高达 512K tokens 的输入输出组合)。

1. Jina Reader API 与 ReaderLM-v2 简介

1.1 背景与发展历程

  • Jina Reader API:只需在目标 URL 前添加 https://r.jina.ai/ 前缀,即可自动抓取网页并转换为清洁的文本格式。这一方式无需复杂配置或 API 密钥,操作简单、响应迅速。
  • 小型语言模型:初期发布的 reader-lm-0.5breader-lm-1.5b 专注于 HTML 到 Markdown 的转换任务,但在长文本处理和复杂格式转换上存在一定局限。
  • ReaderLM-v2:作为第二代模型,ReaderLM-v2 在准确性、长文本支持以及多语言适配方面都有显著提升。它不仅能输出高质量的 Markdown,还能直接生成结构化 JSON 数据,方便后续数据提取和 LLM 调用[citeturn0search11]。

1.2 技术亮点

  • 高准确性:通过全新的训练策略和对比损失,ReaderLM-v2 能够避免在生成长序列时出现重复或循环问题,确保输出内容的连贯性和准确性。
  • 多语言支持:支持包括中文、英文、日语、韩语、法语等 29 种语言,满足全球化应用需求。
  • 扩展性:除了 Markdown 转换,还能直接生成 JSON 数据,为特定领域的数据提取提供便捷接口。

2. 技术原理与工作流程

Jina Reader API 通过以下几个关键步骤实现 HTML 到 Markdown 的转换:

  1. 网页抓取与解析
    利用代理服务抓取目标网页内容,通过 HTML 解析器构建 DOM 树,提取网页的主要文本、标题、列表、代码块、表格等结构信息,同时自动过滤掉冗余的样式、脚本和广告信息。

  2. 内容清洗与结构化
    在提取原始内容后,利用正则表达式和启发式规则对文本进行清洗,保留语义关键部分,并根据 HTML 结构转换为符合 Markdown 语法的文本格式。ReaderLM-v2 甚至支持通过指令方式定制输出,例如提取特定部分的内容或生成符合预定义 JSON Schema 的结构化数据。

  3. 输出转换
    用户可以通过 API 请求直接获得 Markdown 格式的输出,或通过指定参数生成 JSON 数据,便于后续处理或直接供 LLM 进行调用。

3. 实践示例:转换 Python 3.13 新特性文档

下面以 Python 3.13 的新特性文档为例,演示如何使用 Jina Reader API 将 HTML 转换为 Markdown 文件。假设目标 URL 为 https://docs.python.org/3/whatsnew/3.13.html,只需在前面添加 https://r.jina.ai/ 前缀即可。

3.1 使用 curl 进行转换

1
2
3
curl "https://r.jina.ai/https://docs.python.org/3/whatsnew/3.13.html" \
-H "x-engine: readerlm-v2" \
-H "Accept: text/event-stream"

上述命令中:

  • x-engine 请求头指定使用 ReaderLM-v2 引擎进行转换;
  • Accept: text/event-stream 启用流式传输模式,适用于处理大型或动态网页内容。

执行后,API 将返回一个干净、结构化的 Markdown 文本,其中保留了原文的标题、段落、列表、代码块等结构信息,去除了 HTML 标签、内联样式以及脚本代码。

3.2 在 Google Colab 中测试

为了方便实验,Jina 官方还提供了 Google Colab Notebook 示例。该 Notebook 展示了如何加载目标网页,调用 ReaderLM-v2 模型进行转换,并输出 Markdown 格式的结果。Notebook 已针对 Colab 的 T4 GPU 进行了优化,适合快速体验和验证转换效果。

4. 应用场景与优势

4.1 优化 LLM 调用

  • 减少 Token 数量:通过转换 HTML 为 Markdown,自动剔除冗余标签和无用内容,从而大幅降低后续 LLM 调用时的 Token 数量,提升调用速度和成本效率。
  • 提升模型理解:干净且结构化的文本更容易被 LLM 理解,有助于提高生成质量和问答准确率。

4.2 数据预处理和信息提取

  • 内容聚合:开发者可以利用该接口批量转换各类网页内容,构建新闻摘要、知识库、学术文献分析等应用。
  • 自动化工作流:结合 Zapier、Notion 等工具,可实现自动采集、转换并存储网页数据,构建企业级的知识管理系统[citeturn0search14]。

4.3 多格式输出

除了 Markdown 格式外,ReaderLM-v2 还支持直接生成 JSON 数据,对于需要进一步数据清洗和结构化处理的场景具有显著优势。

5. 总结与展望

Jina Reader API 及其背后的 ReaderLM-v2 模型为网页内容转换提供了高效、精准的解决方案。从简单地在 URL 前添加前缀,到支持定制化指令和多格式输出,这一工具极大地简化了数据预处理流程。对于后续的 LLM 调用、知识库构建以及自动化工作流而言,都能带来显著的效能提升。

展望未来,Jina AI 将继续优化 ReaderLM 系列产品,计划扩展多模态处理能力(例如图片自动标注、视频摘要等),并持续提升转换速度和稳定性。无论是企业级应用还是个人开发者,都可以借助这一开源工具打造更高效的 AI 系统。


以上就是关于如何使用 Jina.ai 接口将 HTML 转换为 Markdown 文件的详细技术分享。希望这篇博客能为大家在数据预处理、知识库构建和 LLM 应用中提供新的思路与灵感。欢迎在评论区分享你的使用体验和改进建议!


参考资料

原文:https://blog.logrocket.com/working-urls-javascript/
使用ChatGPT协助翻译

URL 是任何 Web 应用的重要组成部分。如果您的应用需要向 API 发送请求,那么构造正确的 URL 就尤为重要。现代浏览器都支持的 URL API,为解析和操作 URL 提供了一种方便的方法。它可以轻松访问 URL 的各个部分。

了解 URL 的组成部分

使用 JavaScript 操作 URL

让我们看以下示例 URL:

1
https://example.com/api/search?query=foo&sort=asc#results

这个 URL 包括以下几个部分:

  • 协议 (Protocol): https
  • 主机 (Host): example.com
  • 路径名称 (Path name): /api/search
  • 查询字符串 (Query string): ?query=foo&sort=asc
  • 哈希 (Hash): #results

通过现代 JavaScript,我们可以轻松解析这些不同的部分。


解析 URL

在支持 URL API 之前,开发者经常使用 <a> 元素解析 URL。例如,提取查询字符串可以这样写:

1
2
3
4
5
function getQueryString(url) {
const link = document.createElement("a");
link.href = url;
return link.search;
}

缺点:

  1. 需要 DOM 环境,因此不能在 Node.js 环境中运行。
  2. 缺乏错误处理,例如传入无效的 URL 时不会抛出错误。

使用 URL API,我们可以更安全、更简单地解析 URL。只需将 URL 传递给 URL 构造函数。如果 URL 是有效的,构造函数会返回一个包含该 URL 各部分属性的对象:

1
2
3
4
const url = new URL("https://example.com/api/search?query=foobar");
console.log(url.host); // example.com
console.log(url.pathname); // /api/search
console.log(url.search); // ?query=foobar
Read more »

Mixtral AI 推出的 AI 聊天助手Le Chat具备 Canvas 功能,支持交互式代码编辑。我利用此功能开发了一款网页绘画应用,代码已托管于 GitHub 并部署在 Vercel 平台。

paint app

通过实践,我总结了以下经验与技巧:

  1. 明确需求: 清晰具体地描述需求,以确保 AI 准确响应。

  2. 分步实施: 将项目拆分为小任务,逐步引导 AI 完成,保障项目流畅进行。

  3. 及时反馈: 在项目进程中持续向 AI 提供反馈,促进其不断优化解决方案。

  4. 善用搜索: 结合在线资源,对比 AI 答案,整合解决方案以获得更佳效果。

GitHub 仓库
应用演示
对话历史

(本文由腾讯混元模型和ChatGPT辅助编写)

在本篇博客中,我们将探讨如何在Docker环境中部署一个基于PyTorch和Transformers库的AI模型,用于图像描述生成。我们将使用一个简单的Flask应用作为后端服务,并通过Docker容器化该应用,以便于部署和管理。

项目概述

本项目实现了一个图像描述生成功能,用户可以通过POST请求上传图片,服务器将返回对应的图像描述。项目代码仓库为duyixian1234/itt

关键技术栈

  • Python: 主要编程语言
  • Flask: 轻量级Web框架
  • PyTorch & Transformers: 深度学习框架和预训练模型库
  • Docker: 容器化技术,用于部署和管理应用

推理代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from flask import Flask, request
from PIL import Image
from transformers import BlipForConditionalGeneration, BlipProcessor

processor = BlipProcessor.from_pretrained("Salesforce/blip-image-captioning-base")
model = BlipForConditionalGeneration.from_pretrained(
"Salesforce/blip-image-captioning-base"
)
print("Model loaded")

app = Flask(__name__)


@app.post("/recognize")
def recognize():
file = request.files["file"]
raw_image = Image.open(file.stream).convert("RGB")
inputs = processor(raw_image, return_tensors="pt")
out = model.generate(**inputs, max_new_tokens=256)
return processor.decode(out[0], skip_special_tokens=True)
Read more »

(本文由腾讯混元模型辅助编写)

在软件开发中,业务逻辑的实现位置一直是一个值得探讨的话题。传统的做法是将业务逻辑放在应用层,例如 Python、Java 等编程语言中。然而,将部分业务逻辑实现在数据库中,如使用 PostgreSQL 的 PL/pgSQL 函数,也有其独特的优势。本文将以一个具体的例子来说明在数据库中实现业务逻辑在效率、安全性和可维护性等方面的优势。

示例场景

假设我们有一个系统,需要限制不同级别的用户在指定时间段内使用某个功能的次数。我们可以根据用户的会员级别来控制使用上限。下面是一个使用 PostgreSQL 函数实现该逻辑的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE OR REPLACE FUNCTION public.update_usage(uid uuid, idx integer, lim integer, cnt integer)
RETURNS TABLE(used integer, "limit" integer)
LANGUAGE plpgsql
AS $function$
BEGIN
-- 插入或更新数据,确保操作是原子的
RETURN QUERY
INSERT INTO usage (user_id, index, used, "limit")
VALUES (uid, idx, cnt, lim)
ON CONFLICT (user_id, index)
DO UPDATE SET
used = usage.used + EXCLUDED.used
,
"limit" = EXCLUDED."limit"
WHERE usage.used + EXCLUDED.used <= usage."limit"
RETURNING usage.used, usage."limit";
END;
$function$;

效率优势

  1. 减少网络开销:在数据库中实现业务逻辑可以减少网络传输的数据量。例如,直接在数据库中进行插入或更新操作,而不是在应用层进行多次数据库交互。

安全性优势

  1. 原子性操作:使用数据库事务和锁机制,可以确保操作的原子性,避免并发问题。
  2. 权限控制:数据库层可以更细粒度地控制权限,确保只有授权的用户才能执行特定的操作。
  3. 防止 SQL 注入:在数据库层实现业务逻辑,可以使用参数化查询,有效防止 SQL 注入攻击。

可维护性优势

  1. 集中管理:将业务逻辑集中在数据库层,可以减少应用层的代码复杂度,便于维护。
  2. 一致性:数据库层的业务逻辑可以确保数据的一致性,避免因应用层逻辑不一致导致的数据错误。

对比示例

直接在 Python 代码中进行 SQL 查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import psycopg2

def update_usage(uid, idx, lim, cnt):
conn = psycopg2.connect(database="yourdb", user="youruser", password="yourpass", host="yourhost", port="yourport")
cur = conn.cursor()

try:
cur.execute("BEGIN")
cur.execute("INSERT INTO usage (user_id, index, used, limit) VALUES (%s, %s, %s, %s) ON CONFLICT (user_id, index) DO UPDATE SET used = usage.used + EXCLUDED.used, limit = EXCLUDED.limit WHERE usage.used + EXCLUDED.used <= usage.limit RETURNING usage.used, usage.limit", (uid, idx, cnt, lim))
result = cur.fetchone()
conn.commit()
except Exception as e:
conn.rollback()
raise e
finally:
cur.close()
conn.close()

return result

直接调用 PostgreSQL 函数

以 supabase 为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from supabase import create_client
import os

SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_KEY = os.getenv("SUPABASE_KEY")

supabase = create_client(SUPABASE_URL, SUPABASE_KEY)


def update_usage(uid, idx, lim, cnt):
result = supabase.rpc(
"update_usage", {"uid": uid, "idx": idx, "lim": lim, "cnt": cnt}
)
return result

结论

在数据库中实现业务逻辑,如使用 PostgreSQL 的 PL/pgSQL 函数,可以在效率、安全性和可维护性等方面带来显著优势。通过减少网络开销、确保操作的原子性、细粒度的权限控制、防止 SQL 注入以及集中管理和一致性保证,数据库层的业务逻辑实现可以大大提升系统的整体性能和可靠性。当然,具体选择哪种方式还需根据实际业务需求和系统架构进行权衡。

近期,我们项目组的小程序成功上线,为了方便社群运营,将小程序链接集成至企业微信群的欢迎语中。小程序链接由后端接口动态生成,且设置了最长30天的有效期限制。为简化链接获取流程,我专门编写了一段 Deno 脚本,实现链接自动获取并通过飞书机器人同步至群聊。鉴于 Deno 运行时对定时任务的内置支持,此次我选择直接采用 Deno Cron 进行任务调度,而非传统的 crontab 管理方式。

Deno Cron 简介

Deno 是一个现代的 JavaScript 和 TypeScript 运行时,它内置了对 cron 表达式的支持,使得定时任务的编写和管理变得非常简单。Deno 的 Deno.cron 函数允许你定义定时任务,并指定执行的时间间隔。

与 Crontab 的对比

传统的 crontab 是 Unix/Linux 系统中用于定时执行任务的工具。与 Deno cron 相比,crontab 更加底层,需要手动管理任务的执行时间和脚本路径。而 Deno cron 提供了更高层次的抽象,使得任务的定义和执行更加直观和便捷。

使用 Docker 运行 Deno Cron

作为一个长期运行的后台进程,我使用了Docker容器作为定时任务的运行时。以下是一个示例命令,展示了如何使用 Docker 运行 Deno cron 任务:

1
docker run -v $(pwd):/app -w /app --name deno-schedule -d denoland/deno deno run -A --unstable-cron task.ts

这个命令做了以下几件事:

  1. 使用 denoland/deno 镜像。
  2. 将当前目录挂载到 Docker 容器的 /app 目录。
  3. 设置工作目录为 /app
  4. 后台运行容器,并命名为 deno-schedule
  5. 运行 task.ts 文件,并启用 unstable cron 功能。

示例代码解析

下面是一个简单的 Deno 定时任务示例,它每周一上午 11 点生成一个小程序链接并报告到飞书:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
async function getAccessToken() {
const resp = await fetch(
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=XXX&secret=XXX",
);
const data = await resp.json();
return data.access_token;
}

async function genMiniAppURL() {
const accessToken = await getAccessToken();
const resp = await fetch(
`https://api.weixin.qq.com/wxa/generate_urllink?access_token=${accessToken}`,
{
method: "POST",
body: JSON.stringify({
"is_expire": true,
"expire_type": 0,
"expire_time": new Date().getTime() + 1000 * 60 * 60 * 24 * 30,
}),
},
);
const data = await resp.json();
return data.url_link;
}

async function reportToFeishu() {
const url = await genMiniAppURL();
await fetch(
"https://open.feishu.cn/open-apis/bot/v2/hook/XXX",
{
method: "POST",
body: JSON.stringify({
msg_type: "interactive",
card: {
config: {
wide_screen_mode: true,
enable_forward: true,
},
header: {
title: {
content: "小程序链接",
},
},
elements: [
{
tag: "div",
text: {
content: `小程序链接: ${url}`,
},
},
],
},
}),
},
);
}

async function work(){
console.log("Fetch Miniapp URL.");
reportToFeishu();
}

Deno.cron("Fetch Miniapp URL", "0 11 1 * *", async () => {
await work();
});

work();

在这个示例中,我们定义了一个每周一上午 11 点执行的任务,它会生成一个小程序链接并通过飞书报告。

总结

总的来说,Deno 在服务端领域表现出色,其内置的 cron 支持让定时任务管理变得简单高效,相比传统 crontab 更直观便捷。示例代码清晰展示了其强大的功能和易用性,极大地简化了开发流程,提升了系统的稳定性和可维护性。

微软近期在其Windows Terminal的Canary版本中引入了Terminal Chat功能,这一创新允许用户与AI服务(例如GitHub Copilot)进行交互,从而获得与上下文紧密相关的智能建议。经过初步体验,我发现其效果颇为显著。

要开始使用,可以下载Canary版本的Windows Terminal,并在设置页面的Terminal Chat选项卡中完成对GitHub Copilot的配置。这样就可以在不离开终端的情况下,轻松地与Copilot进行对话,获取命令建议与解释。

作为一名后端工程师,知晓如何在PowerShell中查询使用特定端口的进程、可执行文件的路径以及该进程的运行命令是很有价值的技能。目前,我所知的方法仅限于使用netstat来查询使用特定端口的进程PID。

以下是我与GitHub Copilot的对话记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
User:
找到使用8000端口的进程、可执行文件地址和运行命令
Assistant:
To find the process, executable file path, and running command for the process using port 8000 in Windows Terminal with pwsh.exe, you can use the `Get-NetTCPConnection` and `Get-Process` cmdlets. Please follow the steps below:
1. Open Windows Terminal and launch pwsh.exe.
2. Run the following command to find the process using port 8000:
Get-NetTCPConnection | Where-Object {$_.LocalPort -eq 8000}
3. Note the value in the `OwningProcess` column. This is the Process ID (PID) of the process using port 8000.
4. Run the following command to find the executable file path of the process:
(Get-Process -Id [PID]).Path
Replace `[PID]` with the actual Process ID obtained in the previous step.
5. Run the following command to find the running command of the process:
(Get-Process -Id [PID] | Select-Object -ExpandProperty CommandLine)
Replace `[PID]` with the actual Process ID obtained in the first step.
These commands will allow you to identify the process, executable file path, and running command for the process using port 8000 in Windows Terminal with pwsh.exe.

为了验证这些步骤,我首先使用miniserve运行了一个使用8000端口的静态HTTP服务:

1
miniserve -p 8000 .

随后,我严格按照Copilot的指示操作,并成功地获取了进程的PID、可执行文件的路径以及该进程的运行命令。

terminal-chat

总的来说,这次体验相当令人满意。

在 SQL 的世界里,递归公共表表达式(Recursive Common Table Expressions,简称 CTE)是一种强大的工具,它允许我们在查询中执行递归操作,从而解决一些传统 SQL 难以处理的问题。

什么是递归 CTE

递归 CTE 通过定义一个初始查询和一个递归查询,不断地将结果集与自身进行联合,直到满足某个终止条件。

递归 CTE 的基本语法如下:

1
2
3
4
5
6
7
8
9
10
11
WITH RECURSIVE cte_name (column_list) AS (
-- 初始查询
SELECT ...
UNION ALL
-- 递归查询
SELECT ...
FROM cte_name
WHERE ...
)
SELECT ...
FROM cte_name;

递归 CTE 有两个主要部分:

  1. 初始查询:定义递归的起始点。
  2. 递归查询:定义如何将当前结果集与自身联合以生成下一层的结果。

递归 CTE 有一些限制:

  • 递归查询必须引用 CTE 自身。
  • 必须有一个明确的终止条件,以防止无限递归。
  • UNION ALL用于合并初始查询和递归查询的结果,因为UNION会去除重复行,而递归过程中可能会产生重复。

使用递归 CTE 实现一些常见的算法

递归 CTE 在数据分析中的常见用法是查询层次结构的数据,例如,可以查询一个组织结构中的所有员工及其上级,或者查询一个文件系统中的所有文件及其父目录。

以下是一个简单的递归 CTE 示例,用于查询一个组织结构中的所有员工及其上级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
WITH RECURSIVE org_hierarchy AS (
-- 初始查询:选择顶层员工
SELECT employee_id, manager_id, employee_name
FROM employees
WHERE manager_id IS NULL

UNION ALL

-- 递归查询:选择每个员工的下属
SELECT e.employee_id, e.manager_id, e.employee_name
FROM employees e
INNER JOIN org_hierarchy oh ON e.manager_id = oh.employee_id
)
SELECT * FROM org_hierarchy;

另一方面,递归 CTE 使 SQL 成为了一门图灵完备的语言,因而也可以实现几乎所有算法。

模拟 for 循环

递归 CTE 最简单的算法应用就是生成一系列数字,用于模拟 for 循环:

1
2
3
4
5
6
7
8
9
WITH RECURSIVE count(n) AS (
SELECT 0
UNION ALL
SELECT n + 1
FROM count
WHERE n < 20
)
SELECT n
FROM count;

阶乘计算

在公共表中的每一行维护多列数据,就可以在循环的基础上进行更多的操作,例如计算阶乘。

1
2
3
4
5
6
7
8
9
WITH RECURSIVE factorial(n, fact) AS (
SELECT 1, 1
UNION ALL
SELECT n + 1, (n + 1) * fact
FROM factorial
WHERE n < 10
)
SELECT n, fact
FROM factorial;
Read more »