postgre sql 数据库查询优化

1.数据库建立索引

第一步:做"精华笔记" (ALTER TABLE)

SQL

复制代码
ALTER TABLE papers 
ADD COLUMN search_vector tsvector 
GENERATED ALWAYS AS ( ... ) STORED;

这一步是在干什么?

普通的 title 和 abstract 是给人看的"原文",里面有很多没用的词(比如 "the", "is", "a", "of")。

这一步是让数据库自动生成一个新列(叫 search_vector),专门存"给机器看的精华笔记"。

  1. 自动分词 (to_tsvector):

    它会自动把 "The batteries are charging" 变成 battery, charge 这种词根。它不仅分词,还去掉了废话。

  2. 划分等级 (setweight)

    • setweight(..., 'A'):把标题 里的词标记为 A级(最高级)

    • setweight(..., 'B'):把摘要 里的词标记为 B级(次级)

    • 为什么要这么做? 以后算分的时候,如果关键词出现在标题里,分数会加倍;出现在摘要里,分数正常。这就实现了"标题党"优先。

  3. 自动化 (GENERATED ALWAYS ... STORED):

    意思是:你以后不需要管这个列。

    当你修改了某篇论文的标题,数据库会自动重新计算这个"精华笔记"并存好。你只管存数据,它负责整理笔记。


第二步:编"字典目录" (CREATE INDEX ... GIN)

SQL

复制代码
CREATE INDEX idx_papers_search ON papers USING GIN(search_vector);

这一步是速度提升几千倍的核心。

没有索引时(你之前的 Python 代码):

你要找 "Lithium"。数据库必须拿起第1本书,翻一遍看有没有 "Lithium";放下,拿起第2本书... 直到看完 20万本。这叫"全表扫描"。

有了 GIN 索引后:

PostgreSQL 创建了一个类似书后索引(Inverted Index) 的结构。它大概长这样:

关键词 (Key) 出现在哪些书里 (Value / IDs)
Battery ID: 1, 5, 8, 102, ...
Lithium ID: 3, 5, 99, 2000, ...
NCM ID: 5, 8, 12, ...

当你搜 "Lithium" 时:

数据库不用去翻那 20 万本书。它直接看这个目录,找到 Lithium 这一行,瞬间就知道:"哦,第 3, 5, 99... 页有这个词。"

然后它直接把这几页抓出来给你。这就是为什么它能从 几秒钟 变成 几毫秒

总结

  1. 第一段代码:把人类读的文章,翻译成机器读的、带权重的"关键词列表",并自动存起来。

  2. 第二段代码:给这些关键词列表编一个"超级目录",查词的时候直接查目录,不用翻书。

2.匹配算法

没问题,这条 SQL 语句虽然短,但是信息量极大。它是 PostgreSQL 全文检索(Full Text Search)的核心。

我们把它拆解成 3 个核心步骤 来理解:"把词变成概念" -> "匹配" -> "打分"

这是你代码里的那段 SQL:

SQL

复制代码
SELECT 
    title, abstract, ... 
    ts_rank_cd(search_vector, query) AS score  -- 3. 打分
FROM 
    papers, 
    to_tsquery('english', %s) query            -- 1. 把你的关键词变成“查询对象”
WHERE 
    search_vector @@ query                     -- 2. 匹配:看文章里有没有这些词
ORDER BY 
    score DESC                                 -- 4. 排序:分高的在前
LIMIT %s;

第一步:准备子弹 (to_tsquery)

SQL

复制代码
to_tsquery('english', 'energy | density | 300')

这里的 %s 被我们 Python 代码里的字符串替换了(比如 'energy | density | 300')。

  • 作用 :它不仅仅是把字符串传进去,它会做自然语言处理

  • 例子:如果你搜 "running",它会自动转换成词根 "run"。如果你搜 "batteries",它会变成 "batteri"。

  • 核心符号 | :我们在 Python 里加的竖线 | 代表 OR(或) 。意思是:"只要包含 energy,或者 包含 density,或者包含 300,都算匹配成功"。

第二步:射击命中 (WHERE search_vector @@ query)

这是整句 SQL 最关键的地方。

  • search_vector:这是我们在数据库里预先算好存起来的那一列(就是上一条 SQL 建的那个)。它不是原本的句子,而是一个"词袋(Bag of Words)"。

    • 原文:"The high energy density."

    • Search Vector'densit':4 'energi':3 'high':2 (它记住了词根,和词出现的位置)。

  • @@ :这是 Postgres 专用的全文检索匹配符

    • 它不像 SQL 里的 LIKE '%word%' 那样傻傻地逐个字母比对。

    • 它利用 GIN 索引(倒排索引),直接去查字典:"'energy' 这个词在哪几篇文章里出现过?" ------ 瞬间定位到这 20 万篇文章里的几百篇。

人话翻译:从 20 万篇文章里,瞬间通过目录找到所有包含 "energy"、"density" 或 "300" 的文章 ID。

第三步:计算得分 (ts_rank_cd)

SQL

复制代码
ts_rank_cd(search_vector, query) AS score

现在我们找到了一堆文章,但谁排第一呢?这就需要打分。

  • ts_rank_cd:这是 Postgres 内置的高级打分算法(Cover Density Ranking)。

  • 它怎么算分?

    1. 命中次数:关键词出现的次数越多,分越高。

    2. 距离(关键) :如果你的关键词是 "energy density",文章里这两个词是挨在一起的("high energy density"),分就很高;如果一个在开头,一个在结尾,分就低。

    3. 权重 :还记得我们在建表时设置的 setweight(title, 'A') 吗?如果关键词出现在标题里,这里的算法会给它更高的分数加成。

总结

这段 SQL 的执行流程是这样的:

  1. to_tsquery:把你输入的乱七八糟的词("energy", "density")标准化成数据库能懂的查询指令。

  2. WHERE ... @@ :利用 GIN 索引 ,以毫秒级速度直接过滤掉 19.9 万篇无关文档,只把包含关键词的文档挑出来。

  3. ts_rank_cd:对挑出来的这些文档进行"考试评分",看谁跟你的关键词最亲密。

  4. ORDER BY:按分数从高到低排队,把前 10 名给你。

这就是为什么它比 Python 里的 BM25 快几千倍的原因:它是在查字典(索引),而不是在读文章(遍历)。

相关推荐
9稳2 小时前
基于PLC的生产线自动升降机设计
开发语言·网络·数据库·嵌入式硬件·plc
四七伵2 小时前
Spring Boot项目中varchar字段为什么不用NULL?告别空指针从建表开始
数据库·后端
Mr.45672 小时前
JDK17+Druid+SpringBoot3+ShardingSphere5 多表分库分表完整实践(MySQL+PostgreSQL)
java·数据库·spring boot·mysql·postgresql
Elastic 中国社区官方博客2 小时前
使用 ES|QL 变量控件将仪表板转变为调查工具
大数据·运维·服务器·数据库·elasticsearch·搜索引擎·全文检索
feng68_2 小时前
Ansible还原数据库节点
linux·运维·数据库·ansible
乐hh3 小时前
清理MySQL数据
数据库·mysql
EasyCVR3 小时前
国标GB28181/RTSP/ONVIF/RTMP视频监控平台EasyCVR视频质量诊断花屏/蓝屏/画面模糊/冻结检测
网络·数据库·音视频
C^h3 小时前
RTthread中的内存池理解
linux·数据库·c++·算法·嵌入式
fobwebs3 小时前
wordpress 网站安装了Yoast SEO,并且做了内容的优化后,如果想重置Yoast SEO,并且删除所有的优化内容,应该如何操作?
数据库·yoast seo·重置yoast seo·清空yoast seo内容