DuckDB简介

DuckDB是一个内存分析型关系数据库,主要用于数据分析。由于其列式存储性质(单独存储每列的数据),它被视为分析数据库。相比之下,传统的关系数据库采用基于行的存储,逐行存储数据。

DuckDB 的优点包括:

  • 快速查询——DuckDB利用列式向量化查询执行引擎来优化大批量的数据查询。
  • SQL兼容性——DuckDB支持标准SQL查询,例如聚合和窗口函数,非常适合熟悉SQL的数据分析师。
  • 快速部署 - DuckDB 具有最小的外部依赖性,并且在我们的应用程序进程中运行,无需单独的数据库实例,从而使部署和集成变得简单。
  • 免费 – DuckDB 是一个开源项目,可供所有人免费使用。其完整源代码可在 GitHub 上访问以供探索和贡献。

DuckDB安装
DuckDB 提供了各种安装选项来适应我们的环境。我们将在这里演示两种常见的安装方法。

1.命令行
对于Windows用户,我们可以使用WinGet包管理器安装DuckDB 。我们所需要做的就是使用管理员权限打开命令提示符并执行以下命令:
winget install DuckDB.cli

在 Mac OS 上,我们可以使用Homebrew安装它:
brew install duckdb

完成DuckDB CLI的安装后,brew会自行将二进制路径添加到现有环境变量中。我们可以打开一个新的 shell 会话并通过运行以下命令来运行 DuckDB CLI:
duckdb

Java
DuckDB 可以与 Java 集成,无需安装单独的数据库实例。首先,我们在pom.xml中包含以下DuckDB JDBC依赖项:

<dependency>
    <groupId>org.duckdb</groupId>
    <artifactId>duckdb_jdbc</artifactId>
    <version>0.10.0</version>
</dependency>

我们可以加载 DuckDB JDBC 驱动程序,然后通过以下JDBC URL创建 JDBC 连接:

Class.forName("org.duckdb.DuckDBDriver");
Connection conn = DriverManager.getConnection(
"jdbc:duckdb:");

当我们连接到 DuckDB 时,默认情况下它会自动创建一个内存数据库实例。但是,一旦 DuckDB 进程完成,实例中保留的所有数据都会丢失。要将数据保存到磁盘中,我们可以在连接 URL 中的冒号后面附加一个数据库名称:

Connection conn = DriverManager.getConnection("jdbc:duckdb:/test_duckdb");

在此示例中,DuckDB在根目录中创建一个名为test_duckdb的数据库文件。由于这是一个 JDBC 库,我们可以通过创建 SQL语句并执行它来获取ResultSet来查询数据。下面是一个获取当前日期的简单 JDBC 示例:

Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT current_date");
Date currentDate = rs.next() ? rs.getDate(1) : null;

在本教程的后续部分中,我们可以在 Java 中使用相同的 JDBC 方法执行 SQL 语句。

数据导入
让我们继续将一些数据导入 DuckDB。它可以处理各种数据格式,从而简化从外部数据源的导入。

1. CSV 文件
CSV 是一种用于存储表格数据的常见数据格式。假设我们有以下包含客户数据的 CSV 文件:

CustomerId,FirstName,LastName,Gender
101,John,Smith,Male
102,Sarah,Jones,Female
...

我们可以使用 SQL 函数read_csv将数据从 CSV 文件导入到 DuckDB 表customer中:

CREATE TABLE customer AS 
SELECT * FROM read_csv('customer.csv')

DuckDB 可以从 CSV 文件的标题行找出架构。标题名称被视为表列名称,而后续行则被视为数据行。

2 JSON 文件
另一种流行的存储和共享数据的方式是 JSON。例如,我们采用以下product.json JSON 文件:

[
  {
    "productId": 1,
   
"productName":"EZ Curl Bar",
   
"category": "Sports Equipment"
  },
  {
   
"productId": 2,
   
"productName": "7' Barbell",
   
"category": "Sports Equipment"
  }
]

与CSV导入类似,我们可以执行一条SQL语句将数据导入到DuckDB表product中:

CREATE TABLE product AS 
SELECT * FROM read_json('product.json')

就像 CSV 一样,DuckDB 根据 JSON 属性名称自动从 JSON 文件中找出架构。

3.插入语句
我们可以使用插入语句向 DuckDB 表添加数据,因为它是一个 SQL 关系数据库系统。下面的示例说明了创建一个定义客户和产品之间关系的购买表 并填充几行数据:

CREATE TABLE purchase(customerId BIGINT, productId BIGINT);
INSERT INTO purchase(customerId, productId) VALUES (101,1);
INSERT INTO purchase(customerId, productId) VALUES (102,1);
INSERT INTO purchase(customerId, productId) VALUES (102,2);


数据查询
加载数据后,我们现在将探索查询 DuckDB 并分析我们的数据。

1.加盟运营
除了将外部数据导入到DuckDB之外,我们还可以直接使用外部数据。根据前面的示例,我们将利用上一节中的三个数据源。现在,让我们加入这些数据源来收集有关客户产品的信息。

SELECT C.firstName, C.lastName, P.productName
FROM read_csv('customer.csv') AS C, read_json('product.json') AS P, purchase S 
WHERE S.customerId = C.customerId
AND S.productId = P.productId 

执行后,我们将看到以下查询结果,显示客户名称及其对应的产品购买情况:

名    姓    产品名称
约翰    史密斯    EZ 弯杆
莎拉    琼斯    7′杠铃
莎拉    琼斯    EZ 弯杆

2.聚合函数
DuckDB 提供了一组丰富的聚合函数来对行组执行计算。让我们探讨一下具有这些函数的示例:

SELECT P.productName, COUNT(*) AS purchaseCount
FROM customer C, product P, purchase S
WHERE S.customerId = C.customerId
AND S.productId = P.productId 
GROUP BY P.productName
ORDER BY COUNT(*) DESC

查询统计每个商品的购买次数,并按购买次数降序排列


数据导出
在数据分析任务中,我们经常需要将聚合数据导出到其他应用程序以进行进一步分析。

让我们逐步了解一下以各种格式从 DuckDB 导出数据的过程。在我们的示例中,我们首先创建一个数据库视图,以便于稍后说明导出:

CREATE VIEW purchase_view AS
SELECT P.productName, COUNT(*) AS purchaseCount
FROM customer C, product P, purchase S
WHERE S.customerId = C.customerId
AND S.productId = P.productId 
GROUP BY P.productName
ORDER BY COUNT(*) DESC;

1. CSV 文件
在 DuckDB 中将数据导出到 CSV 文件非常简单。我们可以执行以下简单的 SQL 将数据库视图buy_view中的所有数据复制到位于根目录中的 CSV 文件:

COPY purchase_view TO '/output.csv'

2. JSON 文件
要将数据导出到 JSON 文件,我们需要包含一个附加选项数组来指定将数据写入 JSON 数组。这可确保我们导出的 JSON 文件具有适当的结构:

COPY (SELECT * FROM purchase_view WHERE purchaseCount > 1) TO '/output.json' (array true);

我们可以根据选择查询的条件复制部分结果,而不是导出所有数据。