利用GPT模型将自然语言转化为SQL查询

利用GPT模型实现自然语言转化为SQL查询

 

自然语言处理(Natural Language Processing)在演进过程中发展迅猛,而GPT模型正处于这一革命的前沿。

现在,LLM模型可以在各种应用中使用。

为了避免不必要的任务并增强工作流程,我开始探索训练GPT来为我制定SQL查询的可能性。

这就是一个灵感的出现:

利用GPT模型解释自然语言,并将其转化为结构化的SQL查询。

这可能吗?

让我们一起探索吧!

让我们从头开始…

 

“少样本提示”的概念

 

你们中的一些人可能已经熟悉“少样本提示”的概念,而另一些人可能从未听说过。

那么,这是什么意思呢?

基本思想是使用一些明确的例子或样本来指导LLM以特定的方式作出回应。

这就是为什么它被称为少样本提示

简而言之,通过展示几个用户输入示例(样本提示),以及所需的LLM输出,我们可以教给模型如何生成更符合我们期望的增强输出。

通过这样做,我们扩展了模型对特定领域的知识,以生成更符合我们期望任务的输出。

让我们来举个例子!

在本教程中,我将使用一个预定义的函数chatgpt_call()来提示GPT模型。如果你想进一步了解它,可以查看以下文章。

假设我想让ChatGPT描述乐观主义这个词。

如果我简单地询问GPT来描述它,我会得到一个严肃而乏味的描述。

## 代码块response = chatgpt_call("教我关于乐观主义。保持简短。")print(response)

 

相应的输出如下:

  

然而,想象一下,我更希望得到一些更富诗意的东西。我可以在提示中添加更多详细说明,指定我希望得到一种诗歌式的定义。

## 代码块response = chatgpt_call("教我关于乐观主义。保持简短。试着创建一个诗意的定义。")print(response)

 

但是第二个输出看起来就像一首诗,与我期望的输出毫无关系。

  

我该怎么办?

我可以详细说明提示,并不断迭代,直到得到一些好的输出。然而,这需要很多时间。

相反,我可以向模型展示我喜欢的诗意描述样式的例子,并向它展示。

## 代码块prompt = """Your task is to answer in a consistent style aligned with the following style. : 教我关于坚韧的事情。: 坚韧就像一棵随风摆动而永不折断的树。它是从逆境中反弹并不断向前的能力。: 教我关于乐观主义的事情。"""response = chatgpt_call(prompt)print(response)

 

输出正是我期望的。

  

那么…我们如何将其应用到我们特定的SQL查询案例中呢?

 

利用自然语言处理进行SQL生成

 

ChatGPT已经能够从自然语言提示中生成SQL查询了。 我们甚至不需要向模型展示任何表格,只需要构建一个假设的计算,它就会帮我们完成。

## 代码块user_input = """假设我有产品表和订单表,您能生成一个包含每个产品的所有信息以及它被销售的次数的单个表格吗?"""prompt = f"""根据以下自然语言提示,在SQL中生成满足所需任务的假设查询。{user_input}"""response = chatgpt_call(prompt)print(response)

 

然而,正如您已经知道的,我们给模型提供的上下文越多,它生成的输出就越好。 

  

在本教程中,我将输入提示分成用户需求的具体部分和对模型所期望的高级行为。这是改进与LLM的交互并在提示中更加简洁的好做法。您可以在以下文章中了解更多信息。

那么,让我们假设我正在使用两个主要的表格: 产品和订单

  

如果我向GPT询问一个简单的查询,模型将立即给出解决方案,就像一开始那样,但使用特定于我的情况的具体表格。

## 代码块user_input = """在商店中最畅销的电视型号是什么?"""prompt = f"""根据以下SQL表格,您的任务是为任何用户请求提供要求的SQL查询。表格: <{sql_tables}>用户请求: ```{user_input}```"""response = chatgpt_call(prompt)print(response)

 

您可以在本文末尾找到sql_tables!

输出如下所示!

  

然而,我们可以观察到前面输出中存在一些问题。

  1. 计算部分错误,它只考虑已交付的电视,而应该将任何已下订单-无论是否交付-都视为销售。
  2. 查询的格式不符合我的要求。

所以首先让我们专注于向模型展示如何计算所需查询。

 

#1. 修正模型的一些误解

 

在这种情况下,模型仅将已交付的产品视为已售出,但事实并非如此。我们可以通过展示两个不同的示例来简单解决这个误解。

## Few_shot 示例fewshot_examples = """-------------- FIRST EXAMPLEUser: 当考虑所有已下订单时,商店中最畅销的电视型号是什么?系统: 您首先需要连接订单和产品表格,过滤出仅对应于电视的订单,并计算已下订单的数量: SELECT P.product_name AS model_of_tv, COUNT(*) AS total_soldFROM products AS PJOIN orders   AS O ON P.product_id = O.product_idWHERE P.product_type = 'TVs'GROUP BY P.product_nameORDER BY total_sold DESCLIMIT 1;-------------- SECOND EXAMPLEUser: 已交付的售出产品中最多的是什么?系统: 您首先需要连接订单和产品表格,计算已交付的订单数量,并仅保留第一个: SELECT P.product_name AS model_of_tv, COUNT(*) AS total_soldFROM products AS PJOIN orders   AS O ON P.product_id = O.product_idWHERE P.order_status = 'Delivered'GROUP BY P.product_nameORDER BY total_sold DESCLIMIT 1;"""

 

现在,如果我们再次提示模型并包含之前的示例,可以看到相应的查询不仅是正确的-之前的查询已经可以工作-而且也会按我们的要求考虑销售!

## 代码块
“`python
user_input = “””商店中销量最高的电视型号是什么?”””
prompt = f”””根据以下SQL表,您的任务是提供所需的SQL表来满足用户的需求。表格: <{sql_tables}>。按照这些示例生成答案,注意查询的结构方式和格式: <{fewshot_examples}>
用户需求: “`{user_input}“`”””
response = chatgpt_call(prompt)
print(response)
“`

 

输出如下:

 我Jupyter笔记本的截图。GPT询问。 

现在我们来检查相应的查询代码…

## 代码块
“`python
pysqldf(“””SELECT P.product_name AS model_of_tv, COUNT(*) AS total_sold
FROM PRODUCTS AS P
JOIN ORDERS AS O ON P.product_id = O.product_id
WHERE P.product_type = ‘电视’
GROUP BY P.product_name
ORDER BY total_sold DESC
LIMIT 1;”””)
“`

 

这个查询完美运行!

 我Jupyter笔记本的截图。GPT询问。 

 

#2. 格式化SQL查询

 

少量/短暂提示也可以用来自定义模型以符合我们自己的目的或风格。

如果我们回顾之前的示例,这些查询没有任何格式。我们都知道,有一些良好的习惯以及一些个人奇怪的习惯,可以让我们更好地阅读SQL查询。

这就是为什么我们可以使用少量/短暂提示,向模型展示我们喜欢查询的方式 – 遵循我们的良好习惯或者仅选用我们的奇特查询 – 并且让模型训练给我们我们所期望的格式化SQL查询。

所以,现在我将准备与之前相同的示例,但按照我的格式偏好进行。

## 代码块
“`python
fewshot_examples = “””—- 示例 1
用户: 商店中销量最高的电视型号是什么?
系统: 首先,您需要连接订单表和产品表,仅过滤出与电视对应的订单,并计算已发出的订单数:
SELECT P.product_name AS model_of_tv,
COUNT(*) AS total_sold
FROM products AS P
JOIN orders AS O ON P.product_id = O.product_id
WHERE P.product_type = ‘电视’
GROUP BY P.product_name
ORDER BY total_sold DESC
LIMIT 1;

—- 示例 2
用户: 最新的订单是哪个?
系统: 首先,您需要连接订单表和产品表,并按最新的order_creation日期时间筛选:
SELECT P.product_name AS model_of_tv
FROM products AS P
JOIN orders AS O ON P.product_id = O.product_id
WHERE O.order_creation = (SELECT MAX(order_creation) FROM orders)
GROUP BY p.product_name
LIMIT 1;”””
“`

 

一旦定义了这些示例,我们可以将它们输入模型中,以便它可以模仿所展示的风格。

正如您可以在下面的代码框中观察到的那样,向GPT展示我们对其的期望后,它会复制给定示例的风格,从而相应地产生任何新的输出。

## 代码块
“`python
user_input = “””商店中最受欢迎的产品型号是什么?”””
prompt = f”””根据以下SQL表,您的任务是提供所需的SQL表来满足用户的需求。表格: <{sql_tables}>。按照这些示例生成答案,注意查询的结构方式和格式: <{fewshot_examples}>
用户需求: “`{user_input}“`”””
response = chatgpt_call(prompt)
print(response)
“`

 

正如您可以在下面的输出中观察到的那样,它经过了!

 我Jupyter笔记本的截图。GPT询问。 

 

#3. 训练模型来计算某个特定变量。

让我们深入探讨一个具体的情景。假设我们的目标是计算哪个产品的交货时间最长。我们用自然语言向模型提出这个问题,并期望得到一个正确的SQL查询。

## 代码块user_input = """交货时间最长的产品是什么?"""prompt = f"""给定以下SQL表,你的任务是提供所需的SQL表来满足用户的任何请求。表: <{sql_tables}>。按照这些示例生成答案,注意查询结构和格式:<{fewshot_examples}>用户请求: ```{user_input}```"""response = chatgpt_call(prompt)print(response)

然而,我们得到的回答与正确答案相差甚远。

我的Jupyter Notebook的截图。启动GPT。

出了什么问题?

GPT模型试图直接计算两个datetime SQL变量之间的差异。这种计算与大多数SQL版本不兼容,尤其是对SQLite用户来说会造成问题。

我们该如何纠正这个问题?

解决方案就在我们眼前–我们重新回到了few-shot提示。

通过向模型演示我们通常如何计算时间变量–在这种情况下,是交货时间–我们训练它,在遇到类似的变量类型时,复制这个过程。

例如,SQLite用户可以使用julianday()函数。该函数将任何日期转换为自儒略历初始时期以来经过的天数。

这有助于GPT模型更好地处理SQLite数据库中的日期差异。

## 添加一个更多的例子fewshot_examples += """------ EXAMPLE 4用户: 计算交付每个产品所需的时间?系统: 您首先需要连接订单和产品表,仅筛选出已交付的订单,并计算两个订单创建日期和交货日期之间的差异。:SELECT     P.product_name AS product_with_longest_delivery,    julianday(O.delivery_date) - julianday(O.order_creation) AS TIME_DIFF    FROM     products AS PJOIN     orders AS O ON P.product_id = O.product_idWHERE     O.order_status = 'Delivered';"""

当我们将此方法作为模型的示例使用时,它会学习到我们首选的计算交货时间的方式。这使得模型更适合生成针对我们特定环境的功能性SQL查询。

如果我们将先前的示例作为输入,该模型将复制我们计算交货时间的方式,并从现在开始为我们的具体环境提供功能性查询。

## 代码块user_input = """交货时间最长的产品是什么?"""prompt = f"""给定以下SQL表,你的任务是提供所需的SQL表来满足用户的任何请求。表: <{sql_tables}>。按照这些示例生成答案,注意查询结构和格式:<{fewshot_examples}>用户请求: ```{user_input}```"""response = chatgpt_call(prompt)print(response)

我的Jupyter Notebook的截图。启动GPT。

总结

总而言之,GPT模型是将自然语言转换为SQL查询的优秀工具。

然而,它并不完美。

模型可能无法理解有上下文的查询或特定的操作,除非经过适当的训练。

通过使用few-shot提示,我们可以引导模型理解我们的查询风格和计算偏好。

这使我们能够充分利用GPT模型在数据科学工作流中的威力,将模型转变为一个适应我们独特需求的强大工具。

从未格式化的查询到完全定制的SQL查询,GPT模型将个性化的魔力带到我们的指尖!

你可以直接在我的GitHub上检查我的代码。

## SQL TABLESsql_tables = """CREATE TABLE PRODUCTS (    product_name VARCHAR(100),    price DECIMAL(10, 2),    discount DECIMAL(5, 2),    product_type VARCHAR(50),    rating DECIMAL(3, 1),    product_id VARCHAR(100));INSERT INTO PRODUCTS (product_name, price, discount, product_type, rating, product_id)VALUES    ('UltraView QLED电视', 2499.99, 15, '电视', 4.8, 'K5521'),    ('ViewTech安卓电视', 799.99, 10, '电视', 4.6, 'K5522'),    ('SlimView OLED电视', 3499.99, 5, '电视', 4.9, 'K5523'),    ('PixelMaster Pro数码相机', 1999.99, 20, '相机和摄像机', 4.7, 'K5524'),    ('ActionX防水相机', 299.99, 15, '相机和摄像机', 4.4, 'K5525'),    ('SonicBlast无线耳机', 149.99, 10, '音频和耳机', 4.8, 'K5526'),    ('FotoSnap数码相机', 599.99, 0, '相机和摄像机', 4.3, 'K5527'),    ('CineView 4K电视', 599.99, 10, '电视', 4.5, 'K5528'),    ('SoundMax家庭音响', 399.99, 5, '音频和耳机', 4.2, 'K5529'),    ('GigaPhone 12X', 1199.99, 8, '智能手机和配件', 4.9, 'K5530');CREATE TABLE ORDERS (    order_number INT PRIMARY KEY,    order_creation DATE,    order_status VARCHAR(50),    product_id VARCHAR(100));INSERT INTO ORDERS (order_number, order_creation, order_status, delivery_date, product_id)VALUES    (123456, '2023-07-01', '发货','', 'K5521'),    (789012, '2023-07-02', '已送达','2023-07-06', 'K5524'),    (345678, '2023-07-03', '处理中','', 'K5521'),    (901234, '2023-07-04', '发货','', 'K5524'),    (567890, '2023-07-05', '已送达','2023-07-15', 'K5521'),    (123789, '2023-07-06', '处理中','', 'K5526'),    (456123, '2023-07-07', '发货','', 'K5529'),    (890567, '2023-07-08', '已送达','2023-07-12', 'K5522'),    (234901, '2023-07-09', '处理中','', 'K5528'),    (678345, '2023-07-10', '发货','', 'K5530');"""

  Josep Ferrer是来自巴塞罗那的分析工程师。他毕业于物理工程学并目前在应用于人类流动性的数据科学领域工作。他是一名兼职的内容创作者,专注于数据科学和技术。您可以通过LinkedInTwitterVoAGI与他联系。