如何在SQL问答中处理大型数据库:高效生成查询的方法

88 阅读3分钟

引言

在处理大型数据库进行SQL问答时,生成有效查询需要将表名、表模式和特征值传递给模型。然而,当表、列数量庞大或列的基数较高时,我们无法在每次提示中提供数据库的完整信息。因此,我们需要找到方法,动态插入最相关的信息进行查询生成。

本文介绍了识别相关信息的方法,并演示如何将其输入到查询生成步骤中。主要内容包括:

  • 识别相关的表子集
  • 识别相关的列值子集

主要内容

1. 环境设置

首先,安装所需的软件包并设置环境变量:

%pip install --upgrade --quiet langchain langchain-community langchain-openai

安装并配置Chinook数据库,以便进行SQL操作:

# 创建Chinook.db
sqlite3 Chinook.db
.read Chinook_Sqlite.sql
SELECT * FROM Artist LIMIT 10;

2. 处理多个表

当表数量巨大时,我们无法将所有表模式放入单次提示中。解决方案是提取与用户输入相关的表名,并仅包含它们的模式。

示例中,我们使用langchain库进行此操作,并采用tool-calling方法来获取符合所需格式的输出。

from langchain_community.utilities import SQLDatabase
db = SQLDatabase.from_uri("sqlite:///Chinook.db")

llm = ChatOpenAI(model="gpt-4o-mini")
table_names = "\n".join(db.get_usable_table_names())

system = f"""Return the names of ALL the SQL tables that MIGHT be relevant to the user question. \
The tables are:\n\n{table_names}\n\nRemember to include ALL POTENTIALLY RELEVANT tables, even if you're not sure that they're needed."""

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "{input}"),
    ]
)

llm_with_tools = llm.bind_tools([Table])
output_parser = PydanticToolsParser(tools=[Table])
table_chain = prompt | llm_with_tools | output_parser

table_chain.invoke({"input": "What are all the genres of Alanis Morisette songs"})

3. 处理高基数列

对于包含专有名词如地址、歌曲名称或艺术家名的列,我们需要在过滤数据前仔细校对拼写。

一种策略是创建一个向量库,存储数据库中所有不同的专有名词,并在每次用户输入时查询向量库,将最相关的专有名词注入提示中。

import ast
import re

def query_as_list(db, query):
    res = db.run(query)
    res = [el for sub in ast.literal_eval(res) for el in sub if el]
    res = [re.sub(r"\b\d+\b", "", string).strip() for string in res]
    return res

proper_nouns = query_as_list(db, "SELECT Name FROM Artist")
# 继续从其他表格中提取专有名词

from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

vector_db = FAISS.from_texts(proper_nouns, OpenAIEmbeddings())
retriever = vector_db.as_retriever(search_kwargs={"k": 15})

代码示例

以下是处理“高基数列”的完整代码示例,使用查询构建链进行拼写纠正:

# With retrieval
query = chain.invoke({"question": "What are all the genres of elenis moriset songs"})
print(query)
db.run(query)

SELECT DISTINCT g.Name
FROM Genre g
JOIN Track t ON g.GenreId = t.GenreId
JOIN Album a ON t.AlbumId = a.AlbumId
JOIN Artist ar ON a.ArtistId = ar.ArtistId
WHERE ar.Name = 'Alanis Morissette';

# 应用API代理服务提高访问稳定性

常见问题和解决方案

  • 如何处理拼写错误?

    使用向量库匹配最接近的专有名词,以提高检索准确性。

  • 如何在提示中动态包含信息?

    结合使用工具调用和查询链,可以自动化动态信息插入。

总结和进一步学习资源

在SQL问答中,处理大型数据库需要智能选择相关信息以提高查询性能。希望本文为您提供了实用的方法和见解。更多资源请参考LangChain文档和SQL相关教程。

参考资料

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---