# Databend 完整功能特性与 SQL 参考指南

## 目录

- [Databend 简介](#databend-简介)
- [Databend 生态圈](#databend-生态圈)
- [架构设计](#架构设计)
- [可视化](#可视化)
- [数据加载(上传)](#数据加载上传)
- [数据卸载(导出)](#数据卸载导出)
  - [卸载 Parquet 文件](#卸载-parquet-文件)
  - [卸载 CSV 文件](#卸载-csv-文件)
  - [卸载 TSV 文件](#卸载-tsv-文件)
  - [卸载 NDJSON 文件](#卸载-ndjson-文件)
- [数据湖集成](#数据湖集成)
- [Databend 通用参考](#databend-通用参考)
  - [数据类型](#数据类型)
  - [数据类型转换](#数据类型转换)
  - [NULL 值和 NOT NULL 约束](#null-值和-not-null-约束)
- [系统表](#系统表)
- [表引擎](#表引擎)
  - [Fuse 引擎](#fuse-引擎)
  - [内存引擎](#内存引擎)
  - [随机引擎](#随机引擎)
- [SQL 标识符](#sql-标识符)
- [SQL 变量](#sql-变量)
- [Lambda 表达式](#lambda-表达式)
- [SQL 方言一致性](#sql-方言一致性)
- [SQL 命令](#sql-命令)
  - [DDL 命令](#ddl-命令)
  - [DML 命令](#dml-命令)
  - [查询命令(SELECT)](#查询命令select)
  - [事务控制](#事务控制)
- [权限管理](#权限管理)
- [用户定义函数](#用户定义函数)
- [窗口函数与分析函数](#窗口函数与分析函数)
- [常用系统函数](#常用系统函数)
- [性能优化](#性能优化)
- [集群键与数据分布](#集群键与数据分布)

Databend 简介

Databend 是一个开源、云原生且支持实时分析型的现代数据仓库,它以其高性能、弹性扩展和低成本而闻名,旨在提供高效的数据存储和处理能力。Databend 面向云架构的设计,使得它能够充分利用云计算的弹性和可扩展性。以下是 Databend 的一些详细特点和功能:

  • 云原生设计:Databend 构建在 Kubernetes 之上,这意味着它可以在容器化环境中无缝运行,支持自动化部署、扩展和管理。它的云原生特性使得 Databend 能够轻松适应不同规模的工作负载,从小型项目到大型企业级应用都能游刃有余。

  • SQL 支持:Databend 提供了对 SQL 的全面支持,兼容 MySQL 协议,这使得从其他 SQL 数据库迁移到 Databend 变得容易,同时也使得开发者和分析师能够使用他们已经熟悉的 SQL 语法来操作 Databend。

  • 性能优化:Databend 采用了矢量化查询执行引擎和列式存储技术,这些技术可以显著提高数据处理的速度,尤其是在处理大规模数据集时。矢量化查询可以减少 I/O 操作,而列式存储则优化了数据的压缩和扫描效率。

  • 对象存储集成:Databend 能够与各种对象存储服务无缝集成,如 AWS S3、Azure Blob 存储、Google Cloud Storage 等,这使得用户可以利用云存储的高可用性和成本效益,同时实现数据的大规模存储和分析。

  • 支持实时分析:Databend 支持实时数据摄取和即时查询响应,这对于需要快速洞察数据的业务场景至关重要,如 BI 分析、日志分析和实时报表等。

  • 社区驱动:作为一个开源项目,Databend 拥有一个活跃的开发者社区,这不仅促进了项目的快速迭代,还吸引了更多的贡献者和用户,共同推动 Databend 的发展和完善。

  • 存算分离架构:Databend 采用了存算分离架构,这种架构允许存储和计算资源独立扩展,提供了极致的弹性,使得 Databend 能够在任何存储服务上运行,并直接查询任何存储服务上的数据。

  • 支持复杂数据类型:Databend 提供了对 ARRAY、MAP、JSON 等复杂数据类型的原生支持,以及对 DECIMAL 等高精度数据类型的支持,这使得它能够处理各种复杂的数据场景。

  • MVCC 列式存储引擎:Databend 构建了类似于 Git 的 MVCC 列式存储引擎,支持 Data Time Travel 和 Data Share 能力,这为数据的版本控制和共享提供了强大的支持。

  • 多租户读写共享:Databend 支持基于同一份数据的多租户读写、共享操作,这使得它在多用户环境中更加灵活和高效。

  • AI 能力:Databend 允许用户将强大的 AI 功能添加到 SQL 查询中,提升数据分析水平。

  • 数据生命周期管理:Databend 利用对象存储实现无需备份的数据管理方案,数据写入理解为增量操作,省去了日志管理,更多需要把表和对应的 snapshot 关联起来,实现数据恢复到任意时间点。

Databend 的应用场景包括但不限于数据分析、实时数据管道构建、机器学习模型训练前的数据预处理等。它的高性能、低成本和易用性使其成为处理大规模数据集的理想选择。


Databend 生态圈

(具体的集成说明以及教程,参考以下官网地址~)

开发

  • Golang
  • Python
  • Node.js
  • Java
  • Rust

ETL

  • Airbyte
  • Kafka
  • dbt
  • Addax
  • Flink CDC
  • DataX
  • Debezium

BI & 可视化

  • Metabase
  • Grafana
  • Redash
  • Tableau

Notebooks

  • Deepnote
  • Jupyter Notebook

日志收集器

  • Vector

AI

  • MindsDB

架构设计

元服务层

Databend 通过其元服务层高效支持多租户,该层在系统中起着至关重要的作用:

  • 元数据管理:处理数据库、表、计算集群、事务等的元数据。
  • 安全:管理用户认证和授权,确保安全环境。

查询层

Databend 的查询层处理查询计算,由多个计算集群组成,每个集群包含多个节点。每个节点是查询层的核心单元,由以下部分组成:

  • 计划器:使用关系代数中的元素为 SQL 语句制定执行计划,结合 Projection、Filter 和 Limit 等操作符。
  • 优化器:基于规则的优化器应用预定义规则,如"谓词下推"和"修剪未使用列",以实现最佳查询执行。
  • 处理器:根据计划器指令构建查询执行管道,遵循 Pull&Push 方法。处理器相互连接,形成一个可以在节点间分布的管道,以提高性能。

存储层

Databend 采用 Parquet,一种开源的列式格式,并引入了自己的表格式以提升查询性能。关键特性包括:

  • 二级索引:加速跨各种分析维度的数据定位和访问。
  • 复杂数据类型索引:旨在加速复杂类型(如半结构化数据)的数据处理和分析。
  • 段:Databend 有效地将数据组织成段,提高数据管理和检索效率。
  • 聚类:在段内使用用户定义的聚类键来简化数据扫描。

可视化

可视化 | Databend


数据加载(上传)

目前 Databend 支持以下方式加载数据~

从文件加载数据

  • 从 stage 加载数据
  • 从存储桶加载数据
  • 从本地文件加载数据
  • 从远程文件加载数据

使用工具加载数据

Addax, Airbyte, Datax, dbt, Dabezium, Flink CDC, Kafka, Tapdata, Vector(以上工具目前 Databend 都支持加载数据)

加载半结构化数据

  • 加载 Parquet 文件
  • 加载 CSV 文件
  • 加载 TSV 文件
  • 加载 NDJSON 文件
  • 加载 ORC 文件

数据卸载(导出)

Databend 使用 COPY INTO 命令,可以轻松地将数据以 CSV、TSV、NDJSON 和 Parquet 等文件格式导出到外部位置。

卸载 Parquet 文件

步骤 1. 创建外部 Stage

CREATE STAGE parquet_unload_stage
URL = 's3://unload/parquet/'
CONNECTION = (
    ACCESS_KEY_ID = '<your-access-key-id>'
    SECRET_ACCESS_KEY = '<your-secret-access-key>'
);

步骤 2. 创建自定义 Parquet 文件格式

CREATE FILE FORMAT parquet_unload_format
    TYPE = PARQUET;

步骤 3. 卸载到 Parquet 文件

COPY INTO @parquet_unload_stage
FROM (
    SELECT *
    FROM generate_series(1, 100)
)
FILE_FORMAT = (FORMAT_NAME = 'parquet_unload_format')
DETAILED_OUTPUT = true;

结果:

file_namefile_sizerow_count
data_a3760513-78a8-4a89-8f92-b1a17e0a61b6_0000_00000000.parquet445100

步骤 4. 验证卸载的 Parquet 文件

SELECT COUNT($1)
FROM @parquet_unload_stage
(
    FILE_FORMAT => 'parquet_unload_format',
    PATTERN => '.*[.]parquet'
);

结果:

count($1)
100

卸载 CSV 文件

步骤 1. 创建外部 Stage

CREATE STAGE csv_unload_stage
URL = 's3://unload/csv/'
CONNECTION = (
    ACCESS_KEY_ID = '<your-access-key-id>'
    SECRET_ACCESS_KEY = '<your-secret-access-key>'
);

步骤 2. 创建自定义 CSV 文件格式

CREATE FILE FORMAT csv_unload_format
    TYPE = CSV,
    RECORD_DELIMITER = '\n',
    FIELD_DELIMITER = ',',
    COMPRESSION = gzip,     -- 使用 gzip 压缩卸载
    OUTPUT_HEADER = true,   -- 卸载时包含表头
    SKIP_HEADER = 1;        -- 仅用于加载,查询时跳过第一行(如果 CSV 文件有表头)

步骤 3. 卸载到 CSV 文件

COPY INTO @csv_unload_stage
FROM (
    SELECT *
    FROM generate_series(1, 100)
)
FILE_FORMAT = (FORMAT_NAME = 'csv_unload_format')
DETAILED_OUTPUT = true;

结果:

file_namefile_sizerow_count
data_c8382216-0a04-4920-9eca-7b5debe3eed6_0000_00000000.csv.gz187100

步骤 4. 验证卸载的 CSV 文件

SELECT COUNT($1)
FROM @csv_unload_stage
(
    FILE_FORMAT => 'csv_unload_format',
    PATTERN => '.*[.]csv[.]gz'
);

结果:

count($1)
100

卸载 TSV 文件

步骤 1. 创建外部 Stage

CREATE STAGE tsv_unload_stage
URL = 's3://unload/tsv/'
CONNECTION = (
    ACCESS_KEY_ID = '<your-access-key-id>'
    SECRET_ACCESS_KEY = '<your-secret-access-key>'
);

步骤 2. 创建自定义 TSV 文件格式

CREATE FILE FORMAT tsv_unload_format
    TYPE = TSV,
    COMPRESSION = gzip;     -- 使用 gzip 压缩卸载

步骤 3. 卸载到 TSV 文件

COPY INTO @tsv_unload_stage
FROM (
    SELECT *
    FROM generate_series(1, 100)
)
FILE_FORMAT = (FORMAT_NAME = 'tsv_unload_format')
DETAILED_OUTPUT = true;

结果:

file_namefile_sizerow_count
data_99e8f5c8-79d6-43d8-80d7-13e3f4c91dd5_0002_00000000.tsv.gz160100

步骤 4. 验证卸载的 TSV 文件

SELECT COUNT($1)
FROM @tsv_unload_stage
(
    FILE_FORMAT => 'tsv_unload_format',
    PATTERN => '.*[.]tsv[.]gz'
);

结果:

count($1)
100

卸载 NDJSON 文件

步骤 1. 创建外部 Stage

CREATE STAGE ndjson_unload_stage
URL = 's3://unload/ndjson/'
CONNECTION = (
    ACCESS_KEY_ID = '<your-access-key-id>'
    SECRET_ACCESS_KEY = '<your-secret-access-key>'
);

步骤 2. 创建自定义 NDJSON 文件格式

CREATE FILE FORMAT ndjson_unload_format
    TYPE = NDJSON,
    COMPRESSION = gzip;     -- 使用 gzip 压缩卸载

步骤 3. 卸载到 NDJSON 文件

COPY INTO @ndjson_unload_stage
FROM (
    SELECT *
    FROM generate_series(1, 100)
)
FILE_FORMAT = (FORMAT_NAME = 'ndjson_unload_format')
DETAILED_OUTPUT = true;

结果:

file_namefile_sizerow_count
data_068976e5-2072-4ad8-9887-16fb9129ed80_0000_00000000.ndjson.gz263100

步骤 4. 验证卸载的 NDJSON 文件

SELECT COUNT($1)
FROM @ndjson_unload_stage
(
    FILE_FORMAT => 'ndjson_unload_format',
    PATTERN => '.*[.]ndjson[.]gz'
);

结果:

count($1)
100

数据湖集成

Databend 与三种强大的数据湖技术——Apache Hive、Apache Iceberg 和 Delta Lake 无缝集成。这种集成通过支持数据湖功能的多个方面带来了显著优势。Databend 提供了一个多功能且全面的平台,使用户在数据湖环境中处理多样数据集时具有更大的灵活性和效率。

此外,这三种技术在 Databend 中的集成采用了不同的方法。一些技术,如 Apache Hive,在目录级别进行集成,而其他技术,如 Delta Lake,则在表引擎级别进行操作。Apache Iceberg 支持在这两个级别进行集成。基于目录的集成建立了与数据湖的集中连接,简化了跨多个表的访问和管理。另一方面,表引擎级别的集成提供了更细粒度的控制,允许在单个表级别进行定制优化和微调。


Databend 通用参考

数据类型

数据类型列表

以下是 Databend 中通用数据类型的列表:

数据类型别名存储大小最小值最大值
BOOLEANBOOL1 byteN/AN/A
TINYINTINT81 byte-128127
SMALLINTINT162 bytes-3276832767
INTINT324 bytes-21474836482147483647
BIGINTINT648 bytes-92233720368547758089223372036854775807
FLOATN/A4 bytes-3.40282347e+383.40282347e+38
DOUBLEN/A8 bytes-1.7976931348623157E+3081.7976931348623157E+308
DECIMALN/A16/32 bytes-10^P / 10^S10^P / 10^S
DATEN/A4 bytes1000-01-019999-12-31
TIMESTAMPN/A8 bytes0001-01-01 00:00:009999-12-31 23:59:59.999999 UTC
VARCHARSTRINGN/AN/AN/A

以下是 Databend 中半结构化数据类型的列表:

数据类型别名示例描述
ARRAYN/A[1, 2, 3, 4]相同数据类型的值的集合,通过其索引访问。
TUPLEN/A(‘2023-02-14’,‘Valentine Day’)不同数据类型的值的有序集合,通过其索引访问。
MAPN/A{“a”:1, “b”:2, “c”:3}一组键值对,其中每个键是唯一的,并映射到一个值。
VARIANTJSON[1,{“a”:1,“b”:{“c”:2}}]不同数据类型的元素集合,包括 ARRAY 和 OBJECT。
BITMAPN/A0101010101用于表示一组值的二进制数据类型,其中每个位表示值的存在或不存在。
数据类型转换
显式转换

我们有两种表达式将一个值转换为另一种数据类型。

  • CAST 函数,如果在转换过程中发生错误,它会抛出错误。
    我们还支持 pg 风格的转换:CAST(c as INT) 与 c::Int 相同

  • TRY_CAST 函数,如果在转换过程中发生错误,它返回 NULL。

隐式转换(“强制”)

关于"强制"(自动转换)的一些基本规则:

  • 所有整数数据类型都可以隐式转换为 BIGINT(INT64)数据类型。
    例如:Int --> bigint, UInt8 --> bigint, Int32 --> bigint

  • 所有数值数据类型都可以隐式转换为 Double(Float64)数据类型。
    例如:Int --> Double, Float --> Double, Int32 --> Double

  • 所有非空数据类型 T 都可以隐式转换为 Nullable(T) 数据类型。
    例如:Int --> Nullable<Int>, String --> Nullable<String>

  • 所有数据类型都可以隐式转换为 Variant 数据类型。
    例如:Int --> Variant

  • 字符串数据类型是最低级别的数据类型,不能隐式转换为其他数据类型。

  • Array<T> --> Array<U> 如果 T --> U。

  • Nullable<T> --> Nullable<U> 如果 T--> U。

  • Null --> Nullable<T> 对于任何 T 数据类型。

  • 数值可以隐式转换为其他数值数据类型,如果不会丢失精度。

常见问题
  • 为什么数值类型不能自动转换为字符串类型?

    这在其他流行的数据库中是微不足道的,甚至可以工作。但它会引入歧义。

    例如:

    select 39 > '301';
    select 39 = '  39  ';
    

    我们不知道如何根据数值规则或字符串规则进行比较。因为根据不同的规则,它们的结果是不同的。

    select 39 > 301 是 false,而 select '39' > '301' 是 true。

    为了使语法更精确且减少歧义,我们抛出错误给用户,以获得更精确的 SQL。

  • 为什么布尔类型不能自动转换为数值类型?

    这也会带来歧义。例如:

    select true > 0.5;
    
  • 错误信息是什么:“不能从可空数据转换为非空类型”

    这意味着你的源列中有一个 null。你可以使用 TRY_CAST 函数或将目标类型设为可空类型。

  • select concat(1, col) 不工作

    你可以改进 SQL 为 select concat('1', col)。

    我们可能会在未来改进表达式,如果可能的话,将字面量 1 解析为字符串值(concat 函数只接受字符串参数)。


NULL 值和 NOT NULL 约束

NULL 值用于表示数据不存在或未知。在 Databend 中,每列本质上都可以包含 NULL 值,这意味着一列可以容纳 NULL 值和常规数据。

如果你需要一个不允许 NULL 值的列,请使用 NOT NULL 约束。如果一列在 Databend 中配置为不允许 NULL 值,并且在插入数据时没有为该列显式提供值,则会自动应用与该列数据类型关联的默认值。

数据类型默认值
整数数据类型0
浮点数据类型0.0
字符和字符串空字符串 (‘’)
日期和时间数据类型‘1970-01-01’ 对于 DATE, ‘1970-01-01 00:00:00’ 对于 TIMESTAMP
布尔数据类型False

例如,如果你创建如下表:

CREATE TABLE test(
    id Int64,
    name String NOT NULL,
    age Int32
);

DESC test;
FieldTypeNullDefaultExtra
idBIGINTYESNULL
nameVARCHARNO‘’
ageINTYESNULL
  • “id” 列可以包含 NULL 值,因为它没有 “NOT NULL” 约束。这意味着它可以存储整数或留空以表示缺失数据。
  • “name” 列必须始终有一个值,因为 “NOT NULL” 约束不允许 NULL 值。
  • “age” 列,像 “id” 一样,也可以包含 NULL 值,因为它没有 “NOT NULL” 约束,允许空条目或 NULL 值表示未知年龄。

以下 INSERT 语句插入一行,其中 “age” 列为 NULL 值。这是允许的,因为 “age” 列没有 NOT NULL 约束,因此它可以包含 NULL 值以表示缺失或未知数据。

INSERT INTO test (id, name, age) VALUES (2, 'Alice', NULL);

以下 INSERT 语句插入一行到 “test” 表中,为 “id” 和 “name” 列提供值,但没有为 “age” 列提供值。这是允许的,因为 “age” 列没有 NOT NULL 约束,因此它可以留空或分配 NULL 值以表示缺失或未知数据。

INSERT INTO test (id, name) VALUES (1, 'John');

以下 INSERT 语句尝试插入一行,但没有为 “name” 列提供值。列类型的默认值将被应用。

INSERT INTO test (id, age) VALUES (3, 45);

系统表

本文旨在一文了解 Databend~,对于 Databend 的系统表想继续研究的同学可以参考官网哈~


表引擎

Fuse 引擎

Databend 使用 Fuse 引擎作为其默认引擎,提供了一个类似于 Git 的用户友好界面的数据管理系统。用户可以轻松地在任何给定时刻查询数据,并轻松地将数据恢复到任何所需的时间点。

CREATE TABLE <table_name> (
<column_definitions>
) [ENGINE = FUSE]
[CLUSTER BY (<expr> [, <expr>, ...] )]
[<Options>];
参数描述
ENGINE如果未明确指定引擎,Databend 将自动默认使用 Fuse 引擎创建表,这等同于 ENGINE = FUSE。
CLUSTER BY指定由多个表达式组成的数据的排序方法。更多信息请参阅 Cluster Key。
Fuse 引擎提供了各种选项(不区分大小写),允许您自定义表的属性。详情请参阅 Fuse 引擎选项。

以下是可用的 Fuse 引擎选项:

选项语法描述
bloom_index_columnsbloom_index_columns = '<column> [, <column> ...]'指定用于布隆索引的列。这些列的数据类型可以是 Map、Number、String、Date 或 Timestamp。如果没有指定特定列,布隆索引默认在所有支持的列上创建。bloom_index_columns='' 禁用布隆索引。
compressioncompression = '<compression>'指定引擎的压缩方法。压缩选项包括 lz4、zstd、snappy 或 none。压缩方法在对象存储中默认为 zstd,在文件系统 (fs) 存储中默认为 lz4。
storage_formatstorage_format = '<storage_format>'指定数据的存储方式。默认情况下,storage_format 设置为 Parquet,它提供高压缩率,非常适合云原生对象存储。此外,还支持实验性的 Native 格式,优化了文件系统等存储设备的内存复制开销。
snapshot_locsnapshot_loc = '<snapshot_loc>'指定字符串格式的位置参数,允许在不复制数据的情况下轻松共享表。
block_size_thresholdblock_size_threshold = <n>指定块的最大大小(以字节为单位)。默认值为 104,857,600 字节。
block_per_segmentblock_per_segment = <n>指定段中的最大块数。默认值为 1,000。
row_per_blockrow_per_block = <n>指定文件中的最大行数。默认值为 1,000,000。
change_trackingchange_tracking = True / False在 Fuse 引擎中将此选项设置为 True 允许跟踪表的更改。为表创建流将自动将 change_tracking 设置为 True,并在表中引入额外的隐藏列作为更改跟踪元数据。更多信息请参阅 How Stream Works。
data_retention_period_in_hoursdata_retention_period_in_hours = <n>指定保留表数据的小时数。最小值为 1 小时。最大值由 databend-query.toml 配置文件中的 data_retention_time_in_days_max 设置定义,如果未指定,则默认为 2,160 小时(90 天)。

内存引擎

语法

CREATE TABLE table_name (
  column_name1 column_type1,
  column_name2 column_type2,
  ...
) ENGINE = Memory;

使用场景

此引擎仅用于开发和测试目的。不建议在生产环境中使用。

尽管内存引擎提供了几个优点,但也存在一些限制:

  • 存储容量有限:可存储的数据量受服务器可用内存量的限制。这使得内存引擎不太适合大型数据集。
  • 服务器故障时数据丢失:由于所有数据都存储在内存中,如果托管 Databend 实例的服务器发生故障,所有存储在内存中的数据都将丢失。

随机引擎

CREATE TABLE table_name (
  column_name1 column_type1,
  column_name2 column_type2,
  ...
) ENGINE = Random;

使用场景

随机引擎用于生成随机数据以进行测试。如果在查询中未指定限制,它将使用 max_block_size 作为限制值。


SQL 标识符

SQL 标识符是用于 Databend 中不同元素的名称,例如表、视图和数据库。

未引用的 & 双引号标识符

未引用的标识符以字母(A-Z,a-z)或下划线(“_”)开头,可以由字母、下划线、数字(0-9)或美元符号(“$”)组成。

示例:

mydatabend
MyDatabend1
My$databend
_my_databend

双引号标识符可以包含广泛的字符,例如数字(0-9)、特殊字符(如句号(.)、单引号(')、感叹号(!)、@符号(@)、井号(#)、美元符号($)、百分号(%)、插入符号(^)和与号(&))、扩展 ASCII 和非 ASCII 字符,以及空白字符。

示例:

"MyDatabend"
"my.databend"
"my databend"
"My 'Databend'"
"1_databend"
"$Databend"

请注意,使用双反引号(``)或双引号(")是等效的:

示例:

`MyDatabend`
`my.databend`
`my databend`
`My 'Databend'`
`1_databend`
`$Databend`

标识符大小写规则

Databend 默认将未引用的标识符存储为小写,并将双引号标识符按输入存储。换句话说,Databend 处理对象名称(如数据库、表和列)时,默认情况下不区分大小写。如果您希望 Databend 区分大小写,请使用双引号。

备注

Databend 允许您控制标识符的大小写敏感性。有两个关键设置可用:

  • unquoted_ident_case_sensitive:当设置为 1 时,此选项保留未引用标识符的字符大小写,确保它们区分大小写。如果保留默认值 0,未引用的标识符将保持不区分大小写,转换为小写。
  • quoted_ident_case_sensitive:通过将此选项设置为 0,您可以指示双引号标识符不应保留字符大小写,使其不区分大小写。

此示例演示了 Databend 在创建和列出数据库时如何处理标识符的大小写:

-- 创建名为 "databend" 的数据库
CREATE DATABASE databend;

-- 尝试创建名为 "Databend" 的数据库
CREATE DATABASE Databend;

>> SQL Error [1105] [HY000]: DatabaseAlreadyExists. Code: 2301, Text = Database 'databend' already exists.

-- 创建名为 "Databend" 的数据库
CREATE DATABASE "Databend";

-- 列出所有数据库
SHOW DATABASES;
databases_in_default
Databend
databend
default
information_schema
system

此示例演示了 Databend 如何处理表和列名称的标识符大小写,默认情况下区分大小写,并使用双引号来区分大小写不同的标识符:

-- 创建名为 "databend" 的表
CREATE TABLE databend (a INT);
DESC databend;
FieldTypeNullDefaultExtra
aINTYESNULL
-- 尝试创建名为 "Databend" 的表
CREATE TABLE Databend (a INT);

>> SQL Error [1105] [HY000]: TableAlreadyExists. Code: 2302, Text = Table 'databend' already exists.

-- 尝试创建一个表,其中一列名为 "a",另一列名为 "A"
CREATE TABLE "Databend" (a INT, A INT);

>> SQL Error [1105] [HY000]: BadArguments. Code: 1006, Text = Duplicated column name: a.

-- 双引号列名
CREATE TABLE "Databend" ("a" INT, "A" INT);
DESC "Databend";
FieldTypeNullDefaultExtra
aINTYESNULL
AINTYESNULL

字符串标识符

在 Databend 中,当管理字符串项(如文本和日期)时,必须将它们用单引号(')括起来,这是标准做法。

INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');

SELECT 'Databend';
‘databend’
Databend
SELECT "Databend";

SQL Error [1105] [HY000]: SemanticError. Code: 1065, Text = error:
–> SQL:1:73
|
1 | /* ApplicationName=DBeaver 23.2.0 - SQLEditor <Script-12.sql> */ SELECT “Databend”
| ^^^^^^^^^^ column Databend doesn’t exist, do you mean ‘Databend’?

默认情况下,Databend SQL 方言为 PostgreSQL:

SHOW SETTINGS LIKE '%sql_dialect%';
namevaluedefaultleveldescriptiontype
sql_dialectPostgreSQLPostgreSQLSESSIONSets the SQL dialect. Available values include “PostgreSQL”, “MySQL”, and “Hive”.String

您可以将其更改为 MySQL 以启用双引号("):

SET sql_dialect='MySQL';

SELECT "demo";
‘demo’
demo

SQL 变量

使用变量进行查询

本节解释了如何在查询中有效使用变量,利用 $ 进行值替换和 IDENTIFIER 访问数据库对象(如表)。

使用 $ 和 getvariable() 访问变量

你可以在 SQL 语句中使用 $ 符号或 getvariable() 函数引用变量的值。这两种方法都允许动态替换,变量的值在运行时直接嵌入到查询中。

示例:

-- 设置一个变量作为过滤值
SET VARIABLE threshold = 100;

-- 在查询中使用 $ 变量
SELECT * FROM sales WHERE amount > $threshold;

-- 或者使用 getvariable() 函数
SELECT * FROM sales WHERE amount > getvariable('threshold');
使用 IDENTIFIER 访问对象

IDENTIFIER 关键字允许你动态引用存储在变量中的数据库对象名称。请注意,BendSQL 尚不支持使用 IDENTIFIER 访问对象。

示例:

-- 创建一个包含销售数据的表
CREATE TABLE sales_data (region TEXT, sales_amount INT, month TEXT) AS 
SELECT 'North', 5000, 'January' UNION ALL
SELECT 'South', 3000, 'January';

select * from sales_data;

-- 设置表名和列名的变量
SET VARIABLE table_name = 'sales_data';
SET VARIABLE column_name = 'sales_amount';

-- 使用 IDENTIFIER 动态引用查询中的表和列
SELECT region, IDENTIFIER($column_name) 
FROM IDENTIFIER($table_name) 
WHERE IDENTIFIER($column_name) > 4000;

Lambda 表达式

Lambda 表达式是匿名函数,允许您封装逻辑并将其作为参数传递给高阶函数,例如用于处理数组、列表或其他复杂数据类型的函数。它通常接受一组输入参数和一个代码体,该代码体对集合中的每个元素或排序逻辑中的每次比较执行。

语法

-- 接受一个参数
<parameter> -> <expression>

-- 接受多个参数
(<parameter1>, <parameter2>, ...) -> <expression>
参数描述
, , …Lambda 将操作的值(例如,数组的元素)。
->将输入参数与逻辑分隔开。
应用于输入参数的逻辑,通常写为条件或计算。

示例

此 Lambda 表达式接受一个参数 n 并将其加 5:

n -> (n + 5)

此 Lambda 表达式接受一个整数 x,如果 x 大于 0 则返回 Positive,否则返回 Non-Positive:

x -> (CASE WHEN x > 0 THEN 'Positive' ELSE 'Non-Positive' END)

此 Lambda 表达式检查 num 是否为偶数。对于偶数返回 true,对于奇数返回 false:

num -> (num % 2 = 0)

此 Lambda 表达式将两个参数 x 和 y 相加:

(x, y) -> (x + y)

SQL 方言一致性

本页详细介绍了 Databend 支持的 SQL 方言及其与 SQL 标准的符合性,特别是针对 SQL:2011 特性和 Databend 中的支持状态。

支持的 SQL 方言

SQL 方言指的是结构化查询语言的特定变体或风格。Databend 默认支持 PostgreSQL 方言,并提供切换到其他支持方言的灵活性。请参阅下表了解支持的方言及其各自的简要描述:

方言介绍了解更多
PostgreSQL默认支持的方言,企业中常用PostgreSQL: The world’s most advanced open source database
MySQL开源数据库管理系统MySQL
Hive大数据处理的数据仓库Apache Hive
PrqlPRQL 是一种用于转换数据的现代语言 — 一种简单、强大、管道化的 SQL 替代方案GitHub - PRQL/prql
Experimental用于测试和研究的实验性方言N/A

要在支持的 SQL 方言之间切换或显示当前方言,请使用 sql_dialect 设置:

示例:

-- 将 SQL 方言设置为 PRQL
SET sql_dialect = 'Prql';

-- 显示当前方言
SHOW SETTINGS LIKE 'sql_dialect';

SQL 一致性概述

Databend 旨在符合 SQL 标准,特别是支持 ISO/IEC 9075:2011,也称为 SQL:2011。Databend 包含了 SQL 标准所需的许多特性,通常在语法或功能上略有不同,但 Databend 也存在一些目前不支持的 SQL 操作(详情见下表)

功能ID功能名称支持情况备注
E021-01CHARACTER 数据类型否不支持固定长度字符串类型
E021-10固定长度和可变长度字符串类型之间的隐式类型转换否不支持固定长度字符串类型
E051-07选择列表中的限定 *否
E051-09FROM 子句中重命名列否
E061-05LIKE 谓词:ESCAPE 子句否
E081-05列级别的 UPDATE 权限否
E081-06表级别的 REFERENCES 权限否
E081-07列级别的 REFERENCES 权限否
E081-08WITH GRANT OPTION否
E081-09USAGE 权限否
E081-10EXECUTE 权限否
E121-01DECLARE CURSOR否
E121-04OPEN 语句否
E121-06定位型 UPDATE 语句否
E121-07定位型 DELETE 语句否
E121-08CLOSE 语句否
E121-10FETCH 语句:隐式 NEXT否
E121-17WITH HOLD 游标否
E141基本完整性约束否
E141-02NOT NULL 列的 UNIQUE 约束否
E141-03PRIMARY KEY 约束否
E141-04基本 FOREIGN KEY 约束,对引用删除和更新操作的默认 NO ACTION否
E141-06CHECK 约束否
E141-08PRIMARY KEY 推断 NOT NULL否
E141-10外键中的名称可以按任意顺序指定否
E151-02ROLLBACK 语句否
E152基本 SET TRANSACTION 语句否
E152-01SET TRANSACTION 语句:ISOLATION LEVEL SERIALIZABLE 子句否
E152-02SET TRANSACTION 语句:READ ONLY 和 READ WRITE 子句否
E171SQLSTATE 支持否
E182宿主语言绑定否

SQL 命令

DDL 命令

数据库相关命令
CREATE [ OR REPLACE ] DATABASE [ IF NOT EXISTS ] <database_name>

SHOW CREATE DATABASE database_name

USE <database_name>

ALTER DATABASE [ IF EXISTS ] <name> RENAME TO <new_db_name>

显示数据库列表:

SHOW [ FULL ] DATABASES 
    [ LIKE '<pattern>' | WHERE <expr> ]
参数描述
FULL列出带有附加信息的结果。更多详情请参见示例。
LIKE使用区分大小写的模式匹配过滤结果名称。
WHERE使用 WHERE 子句中的表达式过滤结果。
DROP { DATABASE | SCHEMA } [ IF EXISTS ] <database_name>

UNDROP DATABASE <database_name>
表相关命令

创建表:

CREATE [ OR REPLACE ] TABLE [ IF NOT EXISTS ] [ <database_name>. ]<table_name>
(
    <column_name> <data_type> [ NOT NULL | NULL ]
                              [ { DEFAULT <expr> } ]
                              [ AS (<expr>) STORED | VIRTUAL ]
                              [ COMMENT '<comment>' ],
    <column_name> <data_type> ...
    ...
)

创建一个与现有表具有相同列定义的表:

现有表的列名、数据类型及其非空约束将被复制到新表中。

CREATE TABLE [IF NOT EXISTS] [db.]table_name
LIKE [db.]origin_table_name

创建一个表并使用 SELECT 命令计算的数据填充它:

CREATE TABLE [IF NOT EXISTS] [db.]table_name
AS SELECT query

列可空性

默认情况下,Databend 中的所有列都是可空的 (NULL)。如果您需要一个不允许 NULL 值的列,请使用 NOT NULL 约束。

创建一个在会话结束时自动删除的临时表:

  • 临时表仅在创建它的会话中可见,并在会话结束时自动删除,所有数据被清理。
  • 创建或操作临时表不需要权限。
  • Databend 支持使用 Fuse Engine 和 Memory Engine 创建临时表。
CREATE [ OR REPLACE ] { TEMPORARY | TEMP } TABLE 
       [ IF NOT EXISTS ] 
       [ <database_name>. ]<table_name>
       ...

创建一个不存储历史数据以支持时间回溯的表:

瞬态表用于保存不需要数据保护或恢复机制的临时数据。Databend 不为瞬态表保留历史数据,因此您将无法使用时间回溯功能查询瞬态表的先前版本。请注意,您仍然可以删除和恢复瞬态表。

警告
对瞬态表的并发修改(包括写操作)可能会导致数据损坏,使数据无法读取。此缺陷正在解决中。在修复之前,请避免对瞬态表进行并发修改。

CREATE [ OR REPLACE ] TRANSIENT TABLE 
       [ IF NOT EXISTS ] 
       [ <database_name>. ]<table_name>
       ...

创建外部表:

CREATE TABLE ... CONNECTION = (...) 语句创建一个表,并指定一个兼容 S3 的存储桶用于数据存储,而不是使用默认的本地存储。

然后,fuse 表引擎表将存储在指定的兼容 S3 的存储桶中。

好处

  • 您可以确定表数据的存储位置。
  • 利用高性能存储,如 Amazon S3 Express One Zone,以提高性能。

语法

CREATE TABLE [IF NOT EXISTS] [db.]table_name (
    <column_name> <data_type> [NOT NULL | NULL] [{ DEFAULT <expr> }],
    <column_name> <data_type> [NOT NULL | NULL] [{ DEFAULT <expr> }],
    ...
)
LOCATION = 's3://<bucket>/[<path>]'
CONNECTION = (
    ENDPOINT_URL = 'https://<endpoint-URL>'
    ACCESS_KEY_ID = '<your-access-key-ID>'
    SECRET_ACCESS_KEY = '<your-secret-access-key>'
    REGION = '<region-name>'
    ENABLE_VIRTUAL_HOST_STYLE = 'true' | 'false'
)
|
CONNECTION = (
    CONNECTION_NAME = '<your-connection-name>'
);

描述表信息:

DESC|DESCRIBE [TABLE] [ <database_name>. ]<table_name>

表重命名:

ALTER TABLE [ IF EXISTS ] <name> RENAME TO <new_table_name>

修改表的列:

-- 在表末尾添加列
ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name> 
ADD [ COLUMN ] <column_name> <data_type> [ NOT NULL | NULL ] [ DEFAULT <constant_value> ]

-- 在指定位置添加列
ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name> 
ADD [ COLUMN ] <column_name> <data_type> [ NOT NULL | NULL ] [ DEFAULT <constant_value> ] [ FIRST | AFTER <column_name> ]

-- 添加虚拟计算列
ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name> 
ADD [ COLUMN ] <column_name> <data_type> AS (<expr>) VIRTUAL

-- 将存储计算列转换为常规列
ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name> 
MODIFY [ COLUMN ] <column_name> DROP STORED

-- 重命名列
ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name>
RENAME [ COLUMN ] <column_name> TO <new_column_name>

-- 更改数据类型和/或注释
-- 如果只想修改或添加列的注释,仍需在命令中指定该列的当前数据类型
ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name> 
MODIFY [ COLUMN ] <column_name> <new_data_type> [ DEFAULT <constant_value> ] [ COMMENT '<comment>' ]
       [ , [ COLUMN ] <column_name> <new_data_type> [ DEFAULT <constant_value> ] [ COMMENT '<comment>' ] ]
       ...

-- 为列设置/取消掩码策略
ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name>
MODIFY [ COLUMN ] <column_name> SET MASKING POLICY <policy_name>

ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name>
MODIFY [ COLUMN ] <column_name> UNSET MASKING POLICY

-- 删除列
ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name> 
DROP [ COLUMN ] <column_name>

修改表的备注:

ALTER TABLE [ IF EXISTS ] [ <database_name>. ]<table_name> 
COMMENT = '<comment>'

修改表的引擎选项:

-- 设置 Fuse Engine 选项
ALTER TABLE [ <database_name>. ]<table_name> SET OPTIONS (<options>)

-- 取消 Fuse Engine 选项,恢复为默认值
ALTER TABLE [ <database_name>. ]<table_name> UNSET OPTIONS (<options>)

压缩表数据:

OPTIMIZE TABLE [database.]table_name COMPACT SEGMENT [LIMIT <segment_count>]

通过将小段合并为较大的段来压缩表数据。

  • 选项 LIMIT 设置要压缩的最大段数。在这种情况下,Databend 将选择并压缩最新的段。

使用快照 ID 或时间戳将表回滚到较早的版本:

-- 使用快照 ID 恢复
ALTER TABLE <table> FLASHBACK TO (SNAPSHOT => '<snapshot-id>');

-- 使用快照时间戳恢复
ALTER TABLE <table> FLASHBACK TO (TIMESTAMP => '<timestamp>'::TIMESTAMP);

查询建表语句:

SHOW CREATE TABLE [ <database_name>. ]<table_name>

列出当前或指定数据库中已删除的表:

SHOW DROP TABLES [ FROM <database_name> ] [ LIKE '<pattern>' | WHERE <expr> ]

指定表的列信息:

SHOW FIELDS FROM [ <database_name>. ]<table_name>

SHOW  [ FULL ] COLUMNS
    {FROM | IN} tbl_name
    [ {FROM | IN} db_name ]
    [ LIKE '<pattern>' | WHERE <expr> ]

查询库表状态:

SHOW TABLE STATUS
    [ {FROM | IN} <database_name> ]
    [ LIKE 'pattern' | WHERE expr ]

删除表中所有行:

TRUNCATE TABLE [ <database_name>. ]table_name

删除一张表:

DROP TABLE [ IF EXISTS ] [ <database_name>. ]<table_name>

恢复已删除表的最近版本:

UNDROP TABLE [ <database_name>. ]<table_name>

DML 命令

COPY INTO

COPY INTO 允许您将数据从表或查询中卸载到一个或多个文件中,这些文件位于以下位置之一:

  • 用户/内部/外部阶段(stage):stage 是 Databend 中的虚拟文件存放位置。stage 中的文件可以直接查询或加载到表中。或者,您可以将表中的数据卸载到 stage 中作为文件。
  • 在存储服务中创建的存储桶或容器。

语法

COPY INTO { internalStage | externalStage | externalLocation }
FROM { [<database_name>.]<table_name> | ( <query> ) }
[ FILE_FORMAT = (
         FORMAT_NAME = '<your-custom-format>'
         | TYPE = { CSV | TSV | NDJSON | PARQUET } [ formatTypeOptions ]
       ) ]
[ copyOptions ]
[ VALIDATION_MODE = RETURN_ROWS ]
[ DETAILED_OUTPUT = true | false ]

示例:卸载到内部阶段

此示例将数据卸载到内部阶段:

-- 创建内部阶段
CREATE STAGE my_internal_stage;

-- 使用 PARQUET 文件格式将表中的数据卸载到阶段
COPY INTO @my_internal_stage
    FROM canadian_city_population
    FILE_FORMAT = (TYPE = PARQUET);
COPY INTO <表>

COPY INTO 允许您从以下位置之一的文件中加载数据:

  • 用户 / 内部 / 外部阶段(stage): stage 是 Databend 中的虚拟文件存放位置。stage 中的文件可以直接查询或加载到表中。或者,您可以将表中的数据卸载到 stage 中作为文件。
  • 存储服务中创建的存储桶或容器。
  • 远程服务器,您可以通过其 URL(以 “https://…” 开头)访问文件。

语法

COPY INTO [<数据库名>.]<表名>
     FROM { userStage | internalStage | externalStage | externalLocation |
            ( SELECT [<文件列> ... ]
              FROM { userStage | internalStage | externalStage } ) }
[ FILES = ( '<文件名>' [ , '<文件名>' ] [ , ... ] ) ]
[ PATTERN = '<正则表达式模式>' ]
[ FILE_FORMAT = (
         FORMAT_NAME = '<您的自定义格式>'
         | TYPE = { CSV | TSV | NDJSON | PARQUET | ORC } [ formatTypeOptions ]
       ) ]
[ copyOptions ]

示例:从内部阶段加载

这些示例展示了从不同类型的阶段将数据加载到 Databend 中:

COPY INTO mytable
    FROM @my_internal_stage
    PATTERN = '.*[.]parquet'
    FILE_FORMAT = (TYPE = PARQUET);
DELETE 语法
DELETE FROM <table_name> [AS <table_alias>] 
[WHERE <condition>]

示例

示例 1:直接删除行

此示例展示了使用 DELETE 命令直接从 “bookstore” 表中删除 ID 为 103 的图书记录。

-- 创建表并插入5条图书记录
CREATE TABLE bookstore (
  book_id INT,
  book_name VARCHAR
);

INSERT INTO bookstore VALUES (101, 'After the death of Don Juan');
INSERT INTO bookstore VALUES (102, 'Grown ups');
INSERT INTO bookstore VALUES (103, 'The long answer');
INSERT INTO bookstore VALUES (104, 'Wartime friends');
INSERT INTO bookstore VALUES (105, 'Deconstructed');

-- 删除图书 (Id: 103)
DELETE FROM bookstore WHERE book_id = 103;

-- 删除后显示所有记录
SELECT * FROM bookstore;
book_idbook_name
101After the death of Don Juan
102Grown ups
104Wartime friends
105Deconstructed

示例 2:基于子查询的删除

当使用子查询来标识要删除的行时,可以使用子查询运算符和比较运算符来实现所需的删除。

本节中的示例基于以下两个表:

-- 创建'employees'表
CREATE TABLE employees (
  id INT,
  name VARCHAR,
  department VARCHAR
);

-- 向'employees'表插入值
INSERT INTO employees VALUES (1, 'John', 'HR');
INSERT INTO employees VALUES (2, 'Mary', 'Sales');
INSERT INTO employees VALUES (3, 'David', 'IT');
INSERT INTO employees VALUES (4, 'Jessica', 'Finance');

-- 创建'departments'表
CREATE TABLE departments (
  id INT,
  department VARCHAR
);

-- 向'departments'表插入值
INSERT INTO departments VALUES (1, 'Sales');
INSERT INTO departments VALUES (2, 'IT');

使用 IN / NOT IN 子句删除子查询

DELETE FROM EMPLOYEES
WHERE DEPARTMENT IN (
    SELECT DEPARTMENT
    FROM DEPARTMENTS
);

这将删除部门与 departments 表中任何部门匹配的员工。它将删除 ID 为 2 和 3 的员工。

使用 EXISTS / NOT EXISTS 子句删除子查询

DELETE FROM EMPLOYEES
WHERE EXISTS (
    SELECT *
    FROM DEPARTMENTS
    WHERE EMPLOYEES.DEPARTMENT = DEPARTMENTS.DEPARTMENT
);

-- 或者,当他们的部门匹配时,可以使用'EMPLOYEES'表的别名'e'和'DEPARTMENTS'表的别名'd'删除员工。
DELETE FROM EMPLOYEES AS e
WHERE EXISTS (
    SELECT *
    FROM DEPARTMENTS AS d
    WHERE e.DEPARTMENT = d.DEPARTMENT
);

这将删除属于 departments 表中存在的部门的员工。在这种情况下,它将删除 ID 为 2 和 3 的员工。

使用 ALL 子句删除子查询

DELETE FROM EMPLOYEES
WHERE DEPARTMENT = ALL (
    SELECT DEPARTMENT
    FROM DEPARTMENTS
);

这将删除部门与 department 表中所有部门匹配的员工。在这种情况下,不会删除任何员工。

使用 ANY 子句删除子查询

DELETE FROM EMPLOYEES
WHERE DEPARTMENT = ANY (
    SELECT DEPARTMENT
    FROM DEPARTMENTS
);

这将删除部门与 departments 表中任何部门匹配的员工。在这种情况下,它将删除 ID 为 2 和 3 的员工。

使用子查询结合多个条件删除

DELETE FROM EMPLOYEES
WHERE DEPARTMENT = ANY (
    SELECT DEPARTMENT
    FROM DEPARTMENTS
    WHERE EMPLOYEES.DEPARTMENT = DEPARTMENTS.DEPARTMENT
)
   OR ID > 2;

这将删除 employees 表中的员工,如果 department 列的值与 departments 表的 department 列中的任何值匹配,或者 id 列的值大于 2。在这种情况下,它将删除 ID 为 2、3 和 4 的行,因为 Mary 的部门是 “Sales”,存在于 departments 表中,并且 ID 3 和 4 大于 2。

INSERT 语法

示例 1:使用 OVERWRITE 插入值

在此示例中,使用 INSERT OVERWRITE 语句截断 employee 表并插入新数据,用提供的值替换所有现有记录,为 ID 为 100 的员工插入新数据。

INSERT { OVERWRITE | INTO } <table>
    -- 可选地指定要插入的列
    ( <column> [ , ... ] )
    -- 插入选项:
    {
        -- 直接插入值或默认值
        VALUES ( <value> | DEFAULT ) [ , ... ] |
        -- 插入查询结果
        SELECT ...
    }
参数描述
OVERWRITE指示在插入之前是否应截断现有数据。
VALUES允许直接插入特定值或列的默认值。

示例 2:插入查询结果

当插入 SELECT 语句的结果时,列的映射遵循它们在 SELECT 子句中的位置。因此,SELECT 语句中的列数必须等于或大于 INSERT 表中的列数。在 SELECT 语句和 INSERT 表中的列数据类型不同的情况下,将根据需要进行类型转换。

-- 创建一个名为 'employee_info' 的表,包含三列:'employee_id'、'employee_name' 和 'department'
CREATE TABLE employee_info (
    employee_id INT,
    employee_name VARCHAR(50),
    department VARCHAR(50)
);

-- 向 'employee_info' 表插入一条记录
INSERT INTO employee_info VALUES ('101', 'John Doe', 'Marketing');

-- 创建一个名为 'employee_data' 的表,包含三列:'ID'、'Name' 和 'Dept'
CREATE TABLE employee_data (
    ID INT,
    Name VARCHAR(50),
    Dept VARCHAR(50)
);

-- 将 'employee_info' 中的数据插入到 'employee_data' 中
INSERT INTO employee_data SELECT * FROM employee_info;
UPDATE 语法
UPDATE <table_name> 
SET <column_name> = <expression> [, <column_name> = <expression> ...]
[ FROM <table_name> [ AS <alias> ] ]
[ WHERE <condition> ]

示例

-- 创建示例表
CREATE TABLE products (
    product_id INT,
    product_name VARCHAR,
    price DECIMAL(10, 2),
    category VARCHAR
);

INSERT INTO products VALUES 
(1, 'Laptop', 999.99, 'Electronics'),
(2, 'Mouse', 29.99, 'Electronics'),
(3, 'Desk Chair', 199.99, 'Furniture');

-- 直接更新单列
UPDATE products 
SET price = price * 1.1 
WHERE category = 'Electronics';

-- 基于 JOIN 更新(使用 FROM 子句)
UPDATE products p
SET price = p.price * 0.9
FROM (
    SELECT product_id FROM products WHERE price > 500
) high_price
WHERE p.product_id = high_price.product_id;
MERGE 语法

MERGE INTO 语句用于将源表的数据合并到目标表中,根据匹配条件执行插入、更新或删除操作。

MERGE INTO <target_table> [ AS <target_alias> ]
USING <source_table> [ AS <source_alias> ] ON <join_condition>
[ WHEN MATCHED [ AND <condition> ] THEN { UPDATE SET <col> = <expr> [, ...] | DELETE } ]
[ WHEN NOT MATCHED [ AND <condition> ] THEN INSERT [ ( <col> [, ...] ) ] VALUES ( <expr> [, ...] ) ]

示例

-- 创建目标表和源表
CREATE TABLE target_employees (
    id INT,
    name VARCHAR,
    salary DECIMAL(10, 2),
    department VARCHAR
);

CREATE TABLE source_updates (
    id INT,
    name VARCHAR,
    salary DECIMAL(10, 2),
    department VARCHAR,
    action VARCHAR  -- 'UPDATE', 'INSERT', 'DELETE'
);

-- 插入示例数据
INSERT INTO target_employees VALUES 
(1, 'Alice', 5000, 'Engineering'),
(2, 'Bob', 6000, 'Sales'),
(3, 'Charlie', 5500, 'Marketing');

INSERT INTO source_updates VALUES 
(1, 'Alice Smith', 5500, 'Engineering', 'UPDATE'),
(2, 'Bob Johnson', 6500, 'Sales', 'UPDATE'),
(4, 'David', 7000, 'Engineering', 'INSERT');

-- 执行 MERGE 操作:匹配则更新,不匹配则插入
MERGE INTO target_employees t
USING source_updates s ON t.id = s.id
WHEN MATCHED THEN UPDATE SET 
    t.name = s.name,
    t.salary = s.salary,
    t.department = s.department
WHEN NOT MATCHED THEN INSERT (id, name, salary, department)
    VALUES (s.id, s.name, s.salary, s.department);
REPLACE INTO 语法

REPLACE INTO 语句类似于 INSERT,但如果存在主键或唯一键冲突,会先删除旧行再插入新行。

REPLACE INTO <table_name> [ ( <column> [, ...] ) ]
VALUES ( <value> [, ...] ) | SELECT ...

示例

CREATE TABLE users (
    user_id INT,
    username VARCHAR,
    email VARCHAR
);

-- 插入数据
INSERT INTO users VALUES (1, 'alice', 'alice@example.com');

-- 使用 REPLACE INTO 更新已存在的数据
REPLACE INTO users (user_id, username, email)
VALUES (1, 'alice_new', 'alice_new@example.com');

查询命令(SELECT)

基本 SELECT 语法
SELECT [ ALL | DISTINCT ]
    <select_list>
    [ FROM <table_reference> [, <table_reference> ...] ]
    [ WHERE <condition> ]
    [ GROUP BY <grouping_element> [, ...] ]
    [ HAVING <condition> ]
    [ ORDER BY <expression> [ ASC | DESC ] [ NULLS FIRST | NULLS LAST ] [, ...] ]
    [ LIMIT <count> [ OFFSET <start> ] ]
    [ WITH { CTE ... } ]
子查询

标量子查询

返回单个值的子查询,可用于 SELECT 列表、WHERE 子句等。

SELECT 
    product_name,
    (SELECT AVG(price) FROM products) AS avg_price,
    price - (SELECT AVG(price) FROM products) AS diff
FROM products;

相关子查询

引用外部查询列的子查询。

SELECT 
    e.name,
    e.salary,
    (SELECT AVG(salary) 
     FROM employees 
     WHERE department = e.department) AS dept_avg
FROM employees e;

EXISTS / NOT EXISTS

-- 查找有订单的客户
SELECT customer_name 
FROM customers c
WHERE EXISTS (
    SELECT 1 FROM orders o 
    WHERE o.customer_id = c.customer_id
);

-- 查找没有订单的客户
SELECT customer_name 
FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o 
    WHERE o.customer_id = c.customer_id
);
公共表表达式(CTE)

非递归 CTE

WITH 
    sales_summary AS (
        SELECT 
            region,
            SUM(amount) AS total_sales,
            COUNT(*) AS order_count
        FROM sales
        GROUP BY region
    ),
    top_regions AS (
        SELECT region 
        FROM sales_summary 
        WHERE total_sales > 100000
    )
SELECT * FROM top_regions;

递归 CTE

用于查询层次结构数据。

WITH RECURSIVE employee_hierarchy AS (
    -- 锚点成员:顶级经理
    SELECT 
        employee_id,
        name,
        manager_id,
        0 AS level
    FROM employees
    WHERE manager_id IS NULL
    
    UNION ALL
    
    -- 递归成员:下属员工
    SELECT 
        e.employee_id,
        e.name,
        e.manager_id,
        eh.level + 1
    FROM employees e
    INNER JOIN employee_hierarchy eh ON e.manager_id = eh.employee_id
)
SELECT * FROM employee_hierarchy;
JOIN 操作
-- INNER JOIN
SELECT * FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id;

-- LEFT JOIN
SELECT * FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;

-- RIGHT JOIN
SELECT * FROM orders o
RIGHT JOIN customers c ON o.customer_id = c.customer_id;

-- FULL OUTER JOIN
SELECT * FROM customers c
FULL OUTER JOIN orders o ON c.customer_id = o.customer_id;

-- CROSS JOIN
SELECT * FROM products
CROSS JOIN categories;

-- 自连接
SELECT 
    e1.name AS employee,
    e2.name AS manager
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.employee_id;
集合操作
-- UNION(去重)
SELECT city FROM customers
UNION
SELECT city FROM suppliers;

-- UNION ALL(保留重复)
SELECT city FROM customers
UNION ALL
SELECT city FROM suppliers;

-- EXCEPT(差集)
SELECT product_id FROM products_2023
EXCEPT
SELECT product_id FROM products_2024;

-- INTERSECT(交集)
SELECT product_id FROM products_2023
INTERSECT
SELECT product_id FROM products_2024;

事务控制

开始一个新的事务。BEGIN commit/rollback 必须一起使用,以开始然后保存或撤销一个事务。

注意
Databend 不支持嵌套事务,因此不匹配的事务语句将被忽略。

示例:

BEGIN; -- 开始一个事务

MERGE INTO ... -- 此语句属于该事务

BEGIN; -- 在事务中执行 BEGIN 被忽略,不会开始新的事务,不会引发错误

INSERT INTO ... -- 此语句也属于该事务

COMMIT; -- 结束事务

INSERT INTO ... -- 此语句属于单语句事务

COMMIT; -- 在多语句事务外执行 COMMIT 被忽略,不会执行提交操作,不会引发错误

BEGIN; -- 开始另一个事务
... 

DDL 语句在事务中的行为

当在多语句事务中执行 DDL 语句时,它将提交当前的多语句事务,并将后续语句作为单语句事务执行,直到发出另一个 BEGIN。

示例:

BEGIN; -- 开始一个多语句事务

-- 这里的 DML 语句属于当前事务
INSERT INTO table_name (column1, column2) VALUES (value1, value2);

-- 在事务中执行 DDL 语句
CREATE TABLE new_table (column1 data_type, column2 data_type); 
-- 这将提交当前事务

-- 后续语句作为单语句事务执行
UPDATE table_name SET column1 = value WHERE condition;

BEGIN; -- 开始一个新的多语句事务

-- 新的 DML 语句属于新的事务
DELETE FROM table_name WHERE condition;

COMMIT; -- 结束新的事务

ROLLBACK 示例

ROLLBACK 语句撤销了事务期间所做的所有更改。因此,最后的 SELECT 查询应显示一个空的 employees 表,确认没有提交任何更改。

-- 首先创建一个表
CREATE TABLE employees (
    id INT,
    name VARCHAR(50),
    department VARCHAR(50)
);

-- 开始一个多语句事务
BEGIN;

-- 事务中的第一个语句:插入新员工
INSERT INTO employees (id, name, department) VALUES (1, 'Alice', 'HR');

-- 事务中的第二个语句:插入另一个新员工
INSERT INTO employees (id, name, department) VALUES (2, 'Bob', 'Engineering');

-- 事务中的第三个语句:更新第一个员工的部门
UPDATE employees SET department = 'Finance' WHERE id = 1;

-- 回滚事务
ROLLBACK;

-- 验证表为空
SELECT * FROM employees;

权限管理

用户管理

创建用户

CREATE USER [ IF NOT EXISTS ] <user_name> 
IDENTIFIED BY '<password>' 
[ WITH 
    [ DEFAULT_ROLE = '<role_name>' ]
    [ DEFAULT_DATABASE = '<database_name>' ]
    [ DEFAULT_WAREHOUSE = '<warehouse_name>' ]
]

修改用户

ALTER USER <user_name> 
[ IDENTIFIED BY '<new_password>' ]
[ WITH 
    [ DEFAULT_ROLE = '<role_name>' ]
    [ DEFAULT_DATABASE = '<database_name>' ]
    [ DEFAULT_WAREHOUSE = '<warehouse_name>' ]
]

删除用户

DROP USER [ IF EXISTS ] <user_name>

查看用户

SHOW USERS;

角色管理

创建角色

CREATE ROLE [ IF NOT EXISTS ] <role_name>;

授予角色给用户

GRANT ROLE <role_name> TO USER <user_name>;

撤销角色

REVOKE ROLE <role_name> FROM USER <user_name>;

删除角色

DROP ROLE [ IF EXISTS ] <role_name>;

查看角色

SHOW ROLES;

权限授予与撤销

权限类型

权限适用对象描述
ALL所有所有权限
SELECT表/视图查询数据
INSERT表插入数据
UPDATE表更新数据
DELETE表删除数据
CREATE数据库/表创建对象
DROP数据库/表删除对象
ALTER数据库/表修改对象
USAGE数据库/warehouse使用对象

授予权限

GRANT <privilege> [ , ... ] ON <object_type> <object_name> TO { ROLE <role_name> | USER <user_name> } [ WITH GRANT OPTION ]

撤销权限

REVOKE [ GRANT OPTION FOR ] <privilege> [ , ... ] ON <object_type> <object_name> FROM { ROLE <role_name> | USER <user_name> }

示例

-- 创建角色并授权
CREATE ROLE analyst;
GRANT SELECT ON DATABASE sales TO ROLE analyst;
GRANT ROLE analyst TO USER alice;

-- 直接授权给用户
GRANT SELECT, INSERT ON TABLE orders TO USER bob;

-- 撤销权限
REVOKE INSERT ON TABLE orders FROM USER bob;

用户定义函数

创建 UDF

CREATE [ OR REPLACE ] FUNCTION [ IF NOT EXISTS ] <function_name> 
    AS ( <input_param_names> ) -> <lambda_expression> 
    [ DESC='<description>' ]

查看 UDF

SHOW USER FUNCTIONS

修改 UDF

ALTER FUNCTION [ IF NOT EXISTS ] <function_name> 
    AS (<input_param_names>) -> <lambda_expression> 
    [ DESC='<description>' ]

示例

-- 创建一个 UDF
CREATE FUNCTION a_plus_3 AS (a) -> a+3+3;

-- 修改 UDF 的 lambda 表达式
ALTER FUNCTION a_plus_3 AS (a) -> a+3;

删除 UDF

DROP FUNCTION [ IF EXISTS ] <function_name>

窗口函数与分析函数

窗口函数语法

<function_name>([ <expression> [, ...] ]) 
OVER (
    [ PARTITION BY <expression> [, ...] ]
    [ ORDER BY <expression> [ ASC | DESC ] [ NULLS FIRST | NULLS LAST ] [, ...] ]
    [ { ROWS | RANGE } BETWEEN <frame_start> AND <frame_end> ]
)

排名函数

函数描述
ROW_NUMBER()为分区中的每一行分配唯一且连续的整数
RANK()为分区中的每一行分配排名,排名相同则跳过后续排名
DENSE_RANK()为分区中的每一行分配排名,排名相同不跳过后续排名
NTILE(n)将分区中的行分配到 n 个桶中

示例

SELECT 
    employee_id,
    department,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num,
    RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_num,
    DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rank_num
FROM employees;

聚合窗口函数

函数描述
SUM()计算窗口内表达式的总和
AVG()计算窗口内表达式的平均值
COUNT()计算窗口内行的数量
MIN() / MAX()计算窗口内表达式的最小/最大值

示例

SELECT 
    date,
    sales_amount,
    SUM(sales_amount) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS rolling_7d_sum,
    AVG(sales_amount) OVER (ORDER BY date ROWS BETWEEN 29 PRECEDING AND CURRENT ROW) AS rolling_30d_avg
FROM daily_sales;

取值函数

函数描述
LAG(expr, offset, default)返回分区中当前行之前 offset 行的值
LEAD(expr, offset, default)返回分区中当前行之后 offset 行的值
FIRST_VALUE(expr)返回窗口框架中的第一个值
LAST_VALUE(expr)返回窗口框架中的最后一个值
NTH_VALUE(expr, n)返回窗口框架中的第 n 个值

示例

SELECT 
    date,
    close_price,
    LAG(close_price, 1) OVER (ORDER BY date) AS prev_day_price,
    close_price - LAG(close_price, 1) OVER (ORDER BY date) AS price_change,
    LEAD(close_price, 5) OVER (ORDER BY date) AS price_5d_future
FROM stock_prices;

常用系统函数

字符串函数

函数描述示例
CONCAT(str1, str2, …)连接字符串CONCAT('Hello', ' ', 'World')
SUBSTRING(str, start, length)提取子字符串SUBSTRING('Hello World', 1, 5)
LENGTH(str)返回字符串长度LENGTH('Hello')
UPPER(str) / LOWER(str)转换大小写UPPER('hello')
TRIM(str)去除两端空格TRIM(' hello ')
SPLIT(str, delimiter)分割字符串为数组SPLIT('a,b,c', ',')
REPLACE(str, old, new)替换子字符串REPLACE('Hello World', 'World', 'Databend')
REGEXP_REPLACE(str, pattern, replacement)正则替换REGEXP_REPLACE('abc123', '[0-9]', '')

数值函数

函数描述示例
ABS(x)绝对值ABS(-10)
ROUND(x, d)四舍五入ROUND(3.14159, 2)
CEIL(x) / FLOOR(x)向上/向下取整CEIL(3.2)
POWER(x, y)幂运算POWER(2, 3)
SQRT(x)平方根SQRT(16)
MOD(x, y)取模MOD(10, 3)
RAND()随机数RAND()
SIGN(x)符号函数SIGN(-5)

日期时间函数

函数描述示例
NOW()当前日期时间NOW()
TODAY() / CURRENT_DATE()当前日期TODAY()
EXTRACT(part FROM date)提取日期部分EXTRACT(YEAR FROM NOW())
DATE_ADD(date, interval)日期加法DATE_ADD('2024-01-01', INTERVAL 1 MONTH)
DATE_SUB(date, interval)日期减法DATE_SUB('2024-01-01', INTERVAL 7 DAY)
DATEDIFF(end, start)日期差DATEDIFF('2024-01-15', '2024-01-01')
DATE_FORMAT(date, format)格式化日期DATE_FORMAT(NOW(), '%Y-%m-%d')
TO_TIMESTAMP(str)字符串转时间戳TO_TIMESTAMP('2024-01-01 00:00:00')

数组函数

函数描述示例
ARRAY_AGG(expr)聚合为数组ARRAY_AGG(column_name)
ARRAY_CONCAT(arr1, arr2)连接数组ARRAY_CONCAT([1,2], [3,4])
ARRAY_CONTAINS(arr, elem)检查包含ARRAY_CONTAINS([1,2,3], 2)
ARRAY_SLICE(arr, start, end)切片ARRAY_SLICE([1,2,3,4], 1, 3)
ARRAY_LENGTH(arr)数组长度ARRAY_LENGTH([1,2,3])
ARRAY_SORT(arr)排序数组ARRAY_SORT([3,1,2])
ARRAY_UNIQUE(arr)去重ARRAY_UNIQUE([1,1,2,2,3])
FLATTEN(arr)展平嵌套数组FLATTEN([[1,2], [3,4]])

半结构化数据函数(JSON/VARIANT)

函数描述示例
PARSE_JSON(str)解析 JSON 字符串PARSE_JSON('{"a": 1}')
GET_PATH(variant, path)按路径提取值GET_PATH(data, 'a.b.c')
JSON_PATH_EXISTS(variant, path)检查路径是否存在JSON_PATH_EXISTS(data, 'a.b')
OBJECT_KEYS(variant)获取对象键OBJECT_KEYS(data)
AS_INTEGER(variant) / AS_STRING(variant)类型转换AS_INTEGER(data)
TYPEOF(variant)获取类型TYPEOF(data)

条件函数

函数描述示例
IF(condition, true_value, false_value)条件判断IF(salary > 5000, 'High', 'Low')
COALESCE(val1, val2, …)返回第一个非 NULL 值COALESCE(NULL, NULL, 'default')
NULLIF(val1, val2)相等则返回 NULLNULLIF(5, 5)
CASE WHEN … THEN … END多条件判断CASE WHEN score >= 90 THEN 'A' WHEN score >= 80 THEN 'B' ELSE 'C' END
IS_NULL(expr)检查 NULLIS_NULL(column)
IS_NOT_NULL(expr)检查非 NULLIS_NOT_NULL(column)

性能优化

EXPLAIN 分析查询计划

-- 查看查询执行计划
EXPLAIN <query>;

-- 查看优化后的执行计划
EXPLAIN OPTIMIZED <query>;

-- 查看管道执行计划
EXPLAIN PIPELINE <query>;

-- 以图形格式查看(如果客户端支持)
EXPLAIN GRAPH <query>;

查询优化建议

  1. 使用 CLUSTER KEY:对于经常用于过滤的列,设置 CLUSTER KEY 可以显著提高查询性能。

  2. 合理设置布隆索引:对于高基数列,启用布隆索引可以加速等值查询。

    CREATE TABLE events (
        user_id BIGINT,
        event_type VARCHAR,
        event_time TIMESTAMP
    ) ENGINE = FUSE
    BLOOM_INDEX_COLUMNS = 'user_id';
    
  3. **避免 SELECT ***:只查询需要的列,减少 I/O 开销。

  4. 使用分区裁剪:在 WHERE 子句中使用分区列,让查询引擎只扫描相关分区。

  5. 合理使用物化视图:对于复杂且频繁执行的查询,考虑使用物化视图(如果支持)。

  6. 数据类型优化:选择合适的数据类型,例如使用 INT 而不是 BIGINT 如果数值范围允许。

  7. 压缩设置:根据数据特性选择合适的压缩算法(lz4、zstd、snappy)。


集群键与数据分布

在创建表时设置集群键。

集群键旨在通过物理上将数据聚集在一起来提高查询性能。例如,当您将某一列设置为表的集群键时,表数据将按您设置的列进行物理排序。如果您的多数查询都按该列进行过滤,这将最大化查询性能。

注意:对于字符串列,集群统计仅使用前 8 个字节。您可以使用子字符串来提供足够的基数。

语法

CREATE TABLE <name> ... CLUSTER BY ( <expr1> [ , <expr2> ... ] )

示例

此命令创建按列聚簇的表:

CREATE TABLE t1(a int, b int) CLUSTER BY(b,a);

CREATE TABLE t2(a int, b string) CLUSTER BY(SUBSTRING(b, 5, 6));

修改集群键

ALTER TABLE [ IF EXISTS ] <name> CLUSTER BY ( <expr1> [ , <expr2> ... ] )

示例

-- 创建表
CREATE TABLE IF NOT EXISTS playground(a int, b int);

-- 按列添加集群键
ALTER TABLE playground CLUSTER BY(b,a);

INSERT INTO playground VALUES(0,3),(1,1);
INSERT INTO playground VALUES(1,3),(2,1);
INSERT INTO playground VALUES(4,4);

SELECT * FROM playground ORDER BY b,a;
SELECT * FROM clustering_information('db1','playground');

-- 删除集群键
ALTER TABLE playground DROP CLUSTER KEY;

-- 按表达式添加集群键
ALTER TABLE playground CLUSTER BY(rand()+a); 

删除集群键

ALTER TABLE [ IF EXISTS ] <name> DROP CLUSTER KEY

示例

此命令删除表 test 的计算集群键:

ALTER TABLE test DROP CLUSTER KEY

连接(Connection)

什么是连接?

在 Databend 中,连接指的是一个指定的配置,它封装了与外部存储服务交互所需的详细信息。它作为一个集中且可重用的参数集,如访问凭证、端点 URL 和存储类型,便于 Databend 与各种存储服务的集成。

连接可用于创建外部阶段、外部表和附加表,提供了一种通过 Databend 管理和访问存储在外部存储服务中的数据的简化和模块化方法。

本节中的示例首先使用连接到 Amazon S3 所需的凭证创建一个连接。随后,它们利用这个已建立的连接来创建一个外部阶段并附加一个现有表。

创建连接

语法

CREATE [ OR REPLACE ] CONNECTION [ IF NOT EXISTS ] <connection_name> 
    STORAGE_TYPE = '<type>' 
    [ <storage_params> ]
参数描述
STORAGE_TYPE存储服务的类型。可能的值包括:s3, azblob, gcs, oss, cos, hdfs, 和 webhdfs。
storage_params根据存储类型和认证方法而变化。

示例

此语句启动与 Amazon S3 的连接,指定必要的连接参数:

CREATE CONNECTION toronto 
    STORAGE_TYPE = 's3' 
    SECRET_ACCESS_KEY = '<your-secret-access-key>' 
    ACCESS_KEY_ID = '<your-access-key-id>';

此示例展示了使用先前定义的名为 ‘toronto’ 的连接创建一个名为 ‘BOOKS’ 的外部表:

CREATE TABLE BOOKS (
    id BIGINT UNSIGNED,
    title VARCHAR,
    genre VARCHAR DEFAULT 'General'
) 
's3://databend-toronto' 
CONNECTION = (CONNECTION_NAME = 'toronto');

查看连接

DESC CONNECTION <connection_name>

SHOW CONNECTIONS

删除连接

DROP CONNECTION [ IF EXISTS ] <connection_name>

变量

语法

-- 设置一个变量
SET VARIABLE <variable_name> = <expression>

-- 设置多个变量
SET VARIABLE (<variable1>, <variable2>, ...) = (<expression1>, <expression2>, ...)

示例

以下示例设置一个变量:

-- 将变量 a 设置为字符串 'databend'
SET VARIABLE a = 'databend'; 

以下示例设置一个变量,并使用 IDENTIFIER 根据该变量动态查询表:

CREATE TABLE monthly_sales(empid INT, amount INT, month TEXT) AS SELECT 1, 2, '3';

-- 将变量 't' 设置为表名 'monthly_sales'
SET VARIABLE t = 'monthly_sales';

-- 使用 IDENTIFIER 动态引用存储在变量 't' 中的表名
SELECT * FROM IDENTIFIER($t);
empidamountmonth
123

以下示例在一个语句中从查询设置多个变量。查询必须返回一行,并且值的数量与要设置的变量数量相同。

-- 将变量 a 设置为 3,b 设置为 55
SET VARIABLE (a, b) = (SELECT 3, 55); 

以下示例将多个变量设置为常量:

-- 将变量 x 设置为 'xx',y 设置为 'yy'
SET VARIABLE (x, y) = ('xx', 'yy');

查看变量

SHOW VARIABLES [ LIKE '<pattern>' | WHERE <expr> ]

删除变量

-- 移除一个变量
UNSET VARIABLE <variable_name>

-- 移除多个变量
UNSET VARIABLE (<variable1>, <variable2>, ...)

结语

Databend 作为一个现代化的云原生数据仓库,凭借其存算分离架构、高性能的向量化查询引擎、丰富的 SQL 支持以及活跃的开源社区,正在大数据领域展现出强大的竞争力。本文尽可能全面地整理了 Databend 的功能特性和 SQL 参考,希望能为您的学习和工作提供帮助。如需了解更多细节,建议访问 Databend 官方文档。


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐