6个应该学习的DuckDB SQL增强功能

大家好,DuckDB的SQL最初是基于PostgreSQL的,尽管这是一个不错的、可以模仿的SQL版本,但是随着时间的推移,DuckDB已经为其SQL功能引入了一些有用的补充,以使编程工作更加轻松。

对于那些没有听说过DuckDB的人来说,它是一个用C++编写的内存数据库,专为分析型SQL工作负载而设计。它的速度也非常快,性能可以与Polars相媲美。下面让我们来看看本文所涉及的一些有用的SQL命令。

1. 使用正则表达式进行动态列选择

使用正则表达式进行动态列选择,在查询表时不需要使用表中包含的确切列名,这可以为查询带来一些有用的快捷方式。

可以在列名中使用通配符表达式,搭配COLUMNS()关键字和合适的通配符,DuckDB将只检索与COLUMNS()表达式中的通配符匹配的列数据。

假设有以下表格:

db.sql("CREATE TABLE departments (department varchar, exmployee_count INT, average_salary INT,max_salary INT,min_salary INT)")db.sql("INSERT INTO departments VALUES ('Sales', 300, 25000,40000,19000), ('HR',50, 22000,50000,18500)");db.sql("SELECT * FROM departments")+----------|--------------|--------------|-----------|----------+
|department|exployee_count|average_salary|max_salary |min_salary|
+----------|--------------|--------------|-----------|----------+
|Sales     |300           |25000         |40000      |19000     |
|HR        |50            |22000         |50000      |1850      |
+----------|--------------|--------------|-----------|----------+

现在,假如我们只想显示部门以及最高/最低薪资,可以使用以下方式:

# 排除average_salary和employee_count列
db.sql("SELECT department, COLUMNS('m.*salary') FROM departments")┌────────────┬────────────┬────────────┐
│ department │ max_salary │ min_salary │
│  varchar   │   int32    │   int32    │
├────────────┼────────────┼────────────┤
│ Sales      │      40000 │      19000 │
│ HR         │      50000 │      18500 │
└────────────┴────────────┴────────────┘

还可以在WHERE子句中使用COLUMNS表达式,DuckDB会在每个通配符产生的表达式之间隐式地添加AND,示例如下:

db.sql("SELECT department, COLUMNS('m.*salary') FROM departments \WHERE COLUMNS('m.*salary') >= 19000")# 上述WHERE子句等价于
# WHERE max_salary > =19000 AND min_salary >= 19000
#┌────────────┬────────────┬────────────┐
│ department │ max_salary │ min_salary │
│  varchar   │   int32    │   int32    │
├────────────┼────────────┼────────────┤
│ Sales      │      40000 │      19000 │
└────────────┴────────────┴────────────┘

在对列进行计算时也可以使用这种语法。仍以部门数据集为例,如果想对列执行一些聚合,可以这样使用如下方式:

db.sql("SELECT MAX(COLUMNS('m.*salary')) FROM departments")┌─────────────────────────────┬─────────────────────────────┐
│ max(departments.max_salary) │ max(departments.min_salary) │
│            int32            │            int32            │
├─────────────────────────────┼─────────────────────────────┤
│                       50000 │                       19000 │
└─────────────────────────────┴─────────────────────────────┘

2. Pivot/Unpivot

在大多数关系型数据库系统中,对数据集进行Pivot/Unpivot通常是一件很困难的事情,但这真的可以大大简化数据分析。

让我们先设置一下测试表数据:

import duckdb as dbdb.sql("CREATE TABLE purchases (productID int, year INT, sales INT)")db.sql("INSERT INTO purchases VALUES (12345, 2019, 15000), (12345,2020, 19500), (12345, 2021, 22000), (987654, 2019, 510), (987654,2020, 1900), (987654, 2021, 2100)");db.sql("SELECT * FROM purchases")┌───────────┬───────┬───────┐
│ productID │ year  │ sales │
│   int32   │ int32 │ int32 │
├───────────┼───────┼───────┤
│     12345 │  2019 │ 15000 │
│     12345 │  2020 │ 19500 │
│     12345 │  2021 │ 22000 │
│    987654 │  2019 │   510 │
│    987654 │  2020 │  1900 │
│    987654 │  2021 │  2100 │
└───────────┴───────┴───────┘

希望数据看起来像这样:

┌───────────┬────────┬────────┬────────┐
│ productID │  2019  │  2020  │  2021  │
│   int32   │ int128 │ int128 │ int128 │
├───────────┼────────┼────────┼────────┤
│     12345 │  15000 │  19500 │  22000 │
│    987654 │    510 │   1900 │   2100 │
└───────────┴────────┴────────┴────────┘

可以使用PIVOT来实现这一点:

import duckdb as dbdb.sql("create table pivoted_purchases as PIVOT purchases ON year USING SUM(sales)  GROUP BY productID")
db.sql("SELECT * FROM pivoted_purchases")# 如果不想根据PIVOT创建表格
# 而只是显示值,也可以这样做
# db.sql("PIVOT purchases ON year USING SUM(sales)  GROUP BY productID")┌───────────┬────────┬────────┬────────┐
│ productID │  2019  │  2020  │  2021  │
│   int32   │ int128 │ int128 │ int128 │
├───────────┼────────┼────────┼────────┤
│     12345 │  15000 │  19500 │  22000 │
│    987654 │    510 │   1900 │   2100 │
└───────────┴────────┴────────┴────────┘

由于在PIVOT命令中进行的SUM(salary)聚合并没有改变任何数字数据,因此也可以使用UNPIVOT来反转这个过程(以及之前讨论过的COLUMNS功能)。

db.sql("UNPIVOT pivoted_purchases ON COLUMNS(* EXCLUDE productID) INTO NAME year VALUE sales")┌───────────┬─────────┬────────┐
│ productID │  year   │ sales  │
│   int32   │ varchar │ int128 │
├───────────┼─────────┼────────┤
│     12345 │ 2019    │  15000 │
│     12345 │ 2020    │  19500 │
│     12345 │ 2021    │  22000 │
│    987654 │ 2019    │    510 │
│    987654 │ 2020    │   1900 │
│    987654 │ 2021    │   2100 │
└───────────┴─────────┴────────┘

3. 联合数据类型

现在可以联合不同的数据类型,另一个SQL问题已经被解决了。

db.sql("SELECT 'I am a string' as col1 union select 100 union select 42.0")┌───────────────┐
│     col1      │
│    varchar    │
├───────────────┤
│ I am a string │
│ 42.0          │
│ 100           │
└───────────────┘

DuckDB会将不同的数据类型强制转换为“最小公分母”来支持所有检索的类型。在这里,FLOAT和INT列的值被强制转换为VARCHARS。

4. 可重用的列别名

在传统的SQL中,当在选择语句中处理增量计算表达式时,通常需要为每一列复制整个表达式或在通用表表达式(CTE)中封装每个计算步骤。但是,通过可重用的列别名,现在可以在同一条选择语句中使用列别名,包括在whereorder by子句中,从而简化该过程并减少冗余,示例如下:

db.sql("SELECT 'The quick brown fox jumped over the lazy dog' AS my_text,\
substring(my_text, 17,3) AS my_text_substr,\
length(my_text) AS my_text_len,\
my_text_len * my_text_len AS my_text_calc")┌──────────────────────────────────────────────┬────────────────┬─────────────┬──────────────┐
│                   my_text                    │ my_text_substr │ my_text_len │ my_text_calc │
│                   varchar                    │    varchar     │    int64    │    int64     │
├──────────────────────────────────────────────┼────────────────┼─────────────┼──────────────┤
│ The quick brown fox jumped over the lazy dog │ fox            │          44 │         1936 │
└──────────────────────────────────────────────┴────────────────┴─────────────┴──────────────┘

5. 列表推导式

这些是基于Python风格的列表推导式的,可用于计算列表元素上的表达式。例如有一个数字列表:

[1,2,3,4,5,6,7,8,9]

在Python中如果想输出一个新列表,使得上述列表中每个数字都平方,代码如下:

[x*x for x in [1,2,3,4,5,6,7,8,9]]# 上述Python代码的输出结果如下:
#
# [1, 4, 9, 16, 25, 36, 49, 64, 81]

在DuckDB SQL中,可以做类似操作:

db.sql("SELECT [x*x for x in nums] as squares FROM (VALUES ([1,2,3,4,5,6,7,8,9])) t(nums)")┌───────────────────────────────────┐
│              squares              │
│              int32[]              │
├───────────────────────────────────┤
│ [1, 4, 9, 16, 25, 36, 49, 64, 81] │
└───────────────────────────────────┘

FROM (VALUES ([1,2,3,4,5,6,7,8,9])) t(nums)

  • 查询的这一部分是创建一个临时表(或派生表)t,其中只有一个名为nums的列。

  • VALUES子句用于提供一个字面值。在这里,它是一个从1到9的整数列表(或数组)。

  • t(nums)是别名部分,其中派生表命名为t,并将包含该数组的列命名为nums

SELECT [x*x for x in nums] as squares

  • 这是查询的主要部分,在这里进行计算。

  • 查询使用了列表推导式[x*x for x in nums],这是Python中常见的操作。在这种情况下,对于nums数组中的每个元素x,都会计算x的平方。

  • 计算结果是一个平方值数组。对于输入数组[1,2,3,4,5,6,7,8,9],输出将是[1,4,9,16,25,36,49,64,81]

  • as squares是将计算出的数组命名为squares

总的来说,查询是创建一个从1到9的数字数组,然后计算每个数字的平方,并将结果作为名为squares的数组返回。

6. 函数链式调用

DuckDB使用点符号(.)来轻松地将单独的SQL函数串联起来,从而在一条SQL语句中有效地建立一种函数管道。

例如,在许多数据库系统中,都有一个类似于INITCAP的SQL函数,可以将文本字符串中的每个单词首字母大写。遗憾的是,DuckDB并没有内置这个函数,所以尝试使用函数链式调用(和列表推导式)来实现它。

下面是一个简单示例,想将“the quick brown fox jumped over the lazy dog”短语中的每个单词首字母大写。

db.sql("SELECT ([upper(x[1])||x[2:] for x in \
('the quick brown fox jumped over the lazy dog')\
.string_split(' ')]).list_aggr('string_agg',' ') as final_str")┌──────────────────────────────────────────────┐
│                  final_str                   │
│                   varchar                    │
├──────────────────────────────────────────────┤
│ The Quick Brown Fox Jumped Over The Lazy Dog │
└──────────────────────────────────────────────┘

string_split函数会将短语拆分成单个单词,并产生一个类似于Python列表的单词列表。

[the, quick, brown, fox, jumped, over, the, lazy, dog]

接下来,对单词列表运行一个列表推导式。对于列表中的每个单词,将第一个字母设置为大写,然后将剩余的字母连接上去。这就是表达式中upper(x[1])||x[2:]部分的作用。

这样中间单词列表就变成了这样:

[The, Quick, Brown, Fox, Jumped, Over, The, Lazy, Dog]

最后对上述单词列表运行list_agg函数,这只是将单词列表转换回一个由空格字符分隔的单词字符串。

以上这就是本文分享的全部内容,本文介绍了DuckDB提供的六种常规SQL扩展,以帮助数据工程师更轻松地工作。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/34526.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C 标准库 - <assert.h>

C 标准库 - <assert.h> 概述 <assert.h> 是 C 语言标准库中的一个头文件,它提供了一种用于调试程序的工具。assert 宏是 <assert.h> 中最核心的部分,它允许开发者在程序中设置断言(assertions)。断言是一种检查程序中某些假设是否为真的机制。如果断言…

《昇思 25 天学习打卡营第 4 天 | 数据集 Dataset 》

《昇思 25 天学习打卡营第 4 天 | 数据集 Dataset 》 活动地址&#xff1a;https://xihe.mindspore.cn/events/mindspore-training-camp 签名&#xff1a;Sam9029 数据集 数据是深度学习的基础&#xff0c;可以理解在 深度学习模型 中&#xff0c;数据就是模型训练的基础条件 M…

【杂记-浅谈FTP文件传输协议】

FTP文件传输协议 一、FTP协议概述二、FTP的安全隐患三、FTP服务器配置问题四、FTP的安全加固方法 一、FTP协议概述 FTP&#xff0c;File Transfer Protocol&#xff0c;即文件传输协议&#xff0c;是一种用于在网络上进行文件传输的标准协议&#xff0c;它允许用户在客户端和服…

【脚本工具库】图像位深转换-24-8(附源码)

图像位深简介&#xff1a; 在图像处理领域&#xff0c;图像位深的转换是一个非常重要的概念。不同的图像模式有着不同的用途和特点&#xff0c;下面我们详细介绍几种常见的图像模式及其转换方法。 首先&#xff0c;我们来看1位像素的二值图像&#xff08;1bit&#xff09;&…

HarmonyOS角落里的知识:“开发应用沉浸式效果”

概述 典型应用全屏窗口UI元素包括状态栏、应用界面和底部导航条。开发应用沉浸式效果主要指通过调整状态栏、应用界面和导航条的显示效果来减少状态栏导航条等系统界面的突兀感&#xff0c;从而使用户获得最佳的UI体验。 图1 界面元素示意图 开发应用沉浸式效果主要要考虑如下…

8.XSS盲打

XSS盲打 XSS盲打就是攻击者在前端提交的数据不知道后台是否存在xss漏洞的情况下&#xff0c;提交恶意JS代码在类似留言板等输入框后&#xff0c;所展现的后台位置的情况下&#xff0c;网站采用了攻击者插入的恶意代码&#xff0c;当后台管理员在操作时就会触发插入的恶意代码&…

BroadcastChannel 通讯原理

BroadcastChannel 是一种用于在同一来源&#xff08;同一协议、主机名和端口号&#xff09;下不同浏览器上下文&#xff08;如标签页、iframe、Worker、Service Worker&#xff09;之间进行消息广播的 API。它提供了一种简便、可靠的方法来实现跨上下文的实时通讯。 Broadcast…

24.面向对象编程特性

目录 一、面向对象语言特征1.1 对象包含数据和行为1.2 封装1.3 继承作为类型系统与代码共享 二、使用trait对象存储不同类型的值2.1 定义共有行为的trait2.2 实现trait2.3 trait对象执行动态派发2.4 trait对象必须保证对象安全 三、面向对象设计模式四、状态模式的权衡取舍五、…

python的 pyside2 安装

pip install pyside2 pip install pyqt5-tools pycharm 在pychar 的Main Menu--setings--tool--External-tools 点击 新增自定义工具 1&#xff09;自定义 QtDesigner 目的&#xff1a;用于生成.ui文件Name &#xff1a;QtDesigner Group &#xff1a;Qt Program &a…

交通 | 机器学习 + 大规模TSP/VRP求解

封面图来源&#xff1a;https://xkcd.com/399/ 推文作者&#xff1a;丁建辉&#xff0c;陈泰劼&#xff0c;张云天 本文针对旅行商问题&#xff08;Travelling salesman problem, TSP&#xff09;和车辆路径规划问题&#xff08;Vehicle routing problem, VRP&#xff09;这一类…

Python基础技能

目录 1. 掌握基础技能2. 变量与数据类型3. 条件语句4. 循环结构for循环while循环 5. 函数定义6. 列表与元组列表元组 1. 掌握基础技能 2. 变量与数据类型 在Python中&#xff0c;变量是用来存储数据的容器。我们可以给变量赋值&#xff0c;并使用这些值进行计算或操作。Pytho…

Excel 宏录制与VBA编程 —— 12、文本字符串类型相关(附示例)

字符串分割&#xff0c;文末示例&#xff08;文末代码3附有源码&#xff09; 代码1 - 基础字符串 代码2 - 字符串拆分 代码3 - 字符串分割 Option ExplicitSub WorkbooksClear()Dim DataRange As RangeSet DataRange Range("C2:E12")DataRange.Clear End SubSub Wo…

分布式系统_负载均衡

概述 大型网站都要面对庞大的用户量&#xff0c;高并发&#xff0c;海量数据等挑战。 为了提升系统整体的性能&#xff0c;可以采用垂直扩展和水平扩展两种方式。 垂直扩展&#xff1a;从单机角度扩展&#xff0c;增加单机硬件&#xff08;CPU、内存、磁盘&#xff09;处理能…

Vue进阶之Vue无代码可视化项目(五)

Vue无代码可视化项目 编排引擎smooth-dndLeftPanel.vueLayoutView.vuestores/debug.tsstores/editor.tsAppNavigator.vue添加-左侧栏添加到中间部分LayoutView.vuestore/editor.tsLeftPanel.vue移动-中间部分区域的位置更改新建文件夹utils、文件array.tsarray.tsLayoutView.vu…

基于rouyi框架的多租户改造

基于rouyi框架的多租户改造&#xff0c;重点是实现权限管理和数据隔离。权限管理相当于从原来的“顶级管理员admin-普通用户user”转变为“顶级管理员admin-租户管理员tanantAdmin-普通用户user”。数据隔离主要通过分库、分表、表内设置tenantId字段进行过滤三种方式。 本文主…

[word] word 如何在文档中进行分栏排版? #媒体#其他#媒体

word 如何在文档中进行分栏排版&#xff1f; 目标效果 将唐代诗人李白的组诗作品《清平调词》进行分栏排版&#xff0c;共分三栏&#xff0c;每一首诗作为一栏&#xff0c;参考效果如下图。

计算机图形学入门16:阴影映射

1.前言 前面几篇关于光栅化的文章中介绍了如何计算物体表面的光照&#xff0c;但是着色并不会进行阴影的计算&#xff0c;阴影需要单独进行处理&#xff0c;目前最常用的阴影计算技术之一就是Shadow Mapping技术&#xff0c;也就是俗称的阴影映射技术。 2.阴影映射 Shadow Map…

CSS中实现元素水平垂直居中的方式有哪些

在CSS中实现元素水平垂直居中的方法有很多&#xff0c;以下是一些常见的方法&#xff1a; 1. 使用Flexbox Flexbox是一个现代的布局模型&#xff0c;可以轻松实现元素的水平垂直居中。 .container {display: flex;justify-content: center; /* 水平居中 */align-items: cent…

C++在VS2022开发Windows窗口程序2:API式的Windows窗口程序设计模式

函数API式的Windows GUI程序设计模式是一种基于Windows API函数的方式来设计和开发Windows图形用户界面&#xff08;GUI&#xff09;应用程序的模式。在这种模式下&#xff0c;开发者通过调用Windows API函数来创建窗口、处理消息、绘制图形等&#xff0c;而不依赖于特定的GUI库…

mass storage:RAID Structure , Error Detection and Correction

RAID Structure RAID – redundant array of inexpensive disks multiple disk drives provides reliability via redundancyIncreases the mean time to failureMean time to repair – exposure time when another failure could cause data lossMean time to data loss bas…