借助生成式人工智能辅助编写复杂的SQL查询

使用生成式AI辅助编写复杂SQL查询

介绍

ChatGPT的推出标志着人工智能历史上一个前所未有的时刻。ChatGPT和许多其他生成式AI工具以其令人难以置信的能力,有可能彻底改变我们的工作方式。在AI革命之后,编写SQL已经成为正在发生变化的数据科学任务之一。我们将提供一个使用自然语言连接和与SQL数据库交互的示例。您将使用Python的开源包Vanna。笔记本的链接在这里。掌握使用生成式AI来编写复杂SQL查询的艺术。在这个深入指南中,了解如何使用自然语言提示简化数据库交互。

学习目标

在本文中,您将学到:

  • 为什么编写SQL是数据驱动项目中常见的挑战?
  • 生成式AI使得SQL编写更加容易和可访问的潜力
  • 如何使用自然语言提示来实现使用LLM编写SQL?
  • 如何使用Python的包Vanna连接和与SQL数据库交互?
  • Vanna和更广泛的LLM在编写SQL方面的局限性。

本文是数据科学博文马拉松的一部分。

数据驱动项目中常见的SQL挑战

SQL是最受欢迎和广泛使用的编程语言之一。大多数现代公司都采用SQL架构来存储和分析企业数据。然而,并非公司中的每个人都能够利用这些数据。他们可能缺乏技术技能或对数据库的结构和模式不熟悉。

无论原因如何,这经常是数据驱动项目中的瓶颈,因为为了回答业务问题,每个人都依赖于很少数懂得如何使用SQL数据库的人的可用性。 如果每个公司的每个人,无论他们的SQL专业知识如何,都可以在任何时间、任何地点、一次性利用这些数据,那将是多么好啊!

在生成式AI的帮助下,这可能很快成为现实。开发人员和研究人员已经开始尝试不同的方法来训练大型语言模型(LLM)——大多数生成式AI工具的基础技术——用于SQL目的。例如,LangChain是开发基于LLM的应用程序的流行框架,现在可以根据自然语言提示连接和与SQL数据库交互。

然而,这些工具仍处于初期阶段。它们经常返回不准确的结果,或者在处理大型复杂数据库时经历所谓的LLM幻觉。而且,它们可能对非技术用户来说不够直观。因此,仍有很大的改进空间。

Vanna简介

Vanna是一个旨在普及SQL使用的AI代理。它基于OpenAI和Google的第三方LLM的组合预训练模型开始,您可以对其进行微调,以针对您的数据库创建自定义模型。

一旦模型准备好,您可以用自然语言提出业务问题,模型将将其翻译成SQL查询。您还可以针对目标数据库运行查询。只需向模型提问,它将返回查询结果以及一个pandas DataFrame、一个Plotly图表和一个后续问题列表。

要创建自定义模型,Vanna必须使用上下文相关的信息进行训练,包括SQL示例、数据库文档和数据库模式,即数据定义语言(DDL)。您的模型的准确性最终将取决于训练数据的质量和数量。好消息是,该模型被设计成在使用过程中不断学习。由于生成的SQL查询将自动添加到训练数据中,模型将从以前的错误中学习并逐渐改进。

整个过程如下图所示:

阅读本文了解有关LLMs和其他类型神经网络的技术细节。

现在你已经了解了理论,让我们开始实践。

入门

与任何Python包一样,您首先需要安装Vanna。该包可以在PyPI上获得,并且应该在几秒钟内安装完毕。

一旦您在计算机上安装了Vanna,就可以使用别名vn将其导入到您的工作环境中:

# 如果需要,安装vanna
%pip install vanna

# 导入包
import pandas as pd
import vanna as vn

要使用Vanna,您必须创建一个登录并获取一个API密钥。这是一个简单的过程。使用您的电子邮件运行函数vn.get_api_key(),然后将代码发送到您的收件箱。只需输入代码,然后运行vn.set_api_key(),您就可以使用Vanna了。

# 创建登录并获取API密钥
api_key = vn.get_api_key('[email protected]') 
vn.set_api_key(api_key)

Vanna中的模型如何工作?

使用Vanna,您可以创建任意数量的自定义模型。假设您是公司市场部的一员。您的团队通常使用公司的Snowflake数据仓库和一个特定部门的PostgreSQL数据库。然后,您可以创建两个不同的模型,每个模型都在特定数据库的特征上进行训练,并具有不同的访问权限。

要创建模型,请使用函数vn.create_model(model, db_type),提供名称和数据库类型。Vanna可以与支持通过Python进行连接的任何数据库一起使用,包括SQLite,PostgreSQL,Snowflake,BigQuery和Amazon Athena。

两个数据库

假设您想为您的团队使用的两个数据库创建两个模型:

# 创建模型
vn.create_model(model="数据仓库", db_type="Snowflake")
vn.create_model(model="市场数据库", db_type="Postgres")

创建后,您可以使用函数vn.get_model()访问它们。该函数将返回一个可用模型的列表。

['数据仓库',
 '市场数据库',
 'my-dataset2',
 'demo-tpc-h',
 'tpc',
 'chinook']

您可能已经注意到,有比您刚刚创建的模型更多的模型。这是因为Vanna附带了一组预训练的模型,可用于测试目的。

我们将在本教程的其余部分中使用“chinook”模型进行操作。它是在Chinook上进行训练的,Chinook是一个虚构的SQLite数据库,包含有关音乐商店的信息。为了清晰起见,以下是组成数据库的表和关系:

选择模型

要选择该模型,请运行:

# 设置模型
vn.set_model('chinook')

此函数将设置用于Vanna API的模型。它将允许代理将您的提示发送到底层LLM,利用其与训练数据的能力,将您的问题转化为自然语言的SQL查询。

但是,如果您希望代理程序对数据库运行其生成的SQL查询,您将需要连接到数据库。根据数据库的类型,您将需要不同的连接函数。由于我们使用的是SQLite数据库,我们将使用vn.connect_to_sqlite(url)函数,其中url是数据库托管的网址:

# 连接到数据库
url= """https://github.com/lerocha/chinook-database/raw/master
/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite"""
vn.connect_to_sqlite(url=url)

Chinook模型

如前所述,Chinook模型已经预先训练并具有相关信息的上下文。Vanna最酷的事情之一是您始终对训练过程拥有完全控制。您可以随时检查模型中的数据。使用vn.get_training_data()函数可以实现这一点,它将返回一个带有训练数据的pandas DataFrame:

# 检查训练数据
training_data = vn.get_training_data()
training_data

该模型已经通过混合问题与相应的SQL查询、DDL和数据库文档进行了训练。如果想要添加更多的训练数据,可以使用vn.train()函数手动添加。根据使用的参数,该函数可以收集不同类型的训练数据:

  • vn.train(question, sql):添加新的问题-SQL查询对。
  • vn.train(ddl):添加一个DDL语句到模型中。
  • vn.train(documentation):添加数据库文档。

例如,让我们添加问题“哪些五个销售额最高的商店?”及其相关的SQL查询:

# 添加问题-查询对
vn.train(question="哪些五个销售额最高的商店?", 
         sql="""SELECT BILLINGCITY, SUM(TOTAL) 
         FROM INVOICE 
         GROUP BY 1 
         ORDER BY 2 DESC 
         LIMIT 5;""" )

手动训练模型可能会令人生畏且耗时。也可以通过告诉Vanna代理人抓取数据库元数据的方式自动训练模型。不幸的是,该功能目前还处于实验阶段,而且只适用于Snowflake数据库,所以我没有机会尝试。

提问

现在模型已经准备好了,让我们来进行最有趣的部分:提问。

要提问,需要使用vn.ask(question)函数。让我们从一个简单的问题开始:

vn.ask(question='按销售额排名前5位的爵士音乐家是哪些?')

Vanna默认会尝试返回四个元素:SQL查询、带有结果的Pandas DataFrame、用Plotly制作的图表以及一个包含后续问题的列表。运行这行代码时,结果似乎是准确的:

SELECT a.name, sum(il.quantity) as total_sales
FROM artist a 
INNER JOIN album al 
  ON a.artistid = al.artistid 
INNER JOIN track t 
  ON al.albumid = t.albumid 
INNER JOIN invoiceline il 
  ON t.trackid = il.trackid 
INNER JOIN genre g 
  ON t.genreid = g.genreid
WHERE g.name = 'Jazz'
GROUP BY a.nameORDER 
BY total_sales DESC
LIMIT 5;

保存结果

假设你想保存结果而不是打印它们。在这种情况下,可以将print_results参数设置为False,并将结果拆分到不同的变量中,然后使用常规技术(如DataFrame的pandas.to_csv()方法和可视化的plotly.write_image()方法)以所需的格式下载:

sql, df, fig, followup_questions = vn.ask(question='按销售额排名前5位的爵士音乐家是哪些?', 
                                          print_results=False)

# 保存DataFrame和图像
df.to_csv('top_jazz_artists.csv', index=False)
fig.write_image('top_jazz_artists.png')

该函数还有一个名为auto_train的参数,默认设置为True。这意味着问题将自动添加到训练数据集中。可以使用以下语法来确认:

training_data = vn.get_training_data()
training_data['question'].str.contains('按销售额排名前5位的爵士音乐家是哪些?').any()

尽管vn.ask(question)函数具有令人印象深刻的能力,但我想知道它在现实世界中的表现如何,尤其是在更大更复杂的数据库中。此外,无论底层的LLM多么强大,训练过程似乎是高准确性的关键。我们需要多少训练数据?它的表示形式应该是什么样的?是否可以加快训练过程以开发出实用和可操作的模型?

另一方面,Vanna是一个全新的项目,很多事情都有待改进。例如,plotly的可视化效果似乎不太引人注目,并且没有自定义工具。另外,文档可能需要进一步澄清,并且需要丰富具有说明性的示例。

此外,我注意到一些技术问题,应该不难解决。例如,当您只想了解一个数据点时,函数在尝试构建图形时会出错,这是有道理的,因为在这些情况下,可视化是没有意义的。但问题是您看不到后续的问题,更重要的是您无法解包元组。

例如,看看当您想知道最年长的员工时会发生什么。

vn.ask(question='谁是最年长的员工')

结论

Vanna是众多试图利用LLMs的力量使SQL对每个人都易于使用的工具之一。结果是令人鼓舞的,但要开发出能够准确回答每个业务的SQL查询的AI代理仍有很长的路要走。正如我们在本教程中所看到的,强大的LLMs在这个方程中起着重要作用,但秘密仍然在于训练数据。鉴于SQL在全球公司中的普及性,自动化编写查询任务可能会改变游戏规则。因此,值得关注如Vanna这样的AI驱动SQL工具在未来的发展。

要点

  • 生成式人工智能和LLMs正在迅速改变传统数据科学。
  • 编写SQL是一项具有挑战性和耗时的任务,常常导致数据驱动项目的瓶颈。
  • 借助下一代人工智能工具,SQL可能变得更简单和更易于使用。
  • Vanna是众多试图通过LLMs的力量解决这个问题的工具之一。

常见问题

本文中显示的媒体不属于Analytics Vidhya所有,仅根据作者的判断使用。