DuckDB中Catalog与Schema在duckdb_fdw有啥用?
最近 duckdb_fdw 增加了对 Catalog 和 Schema 的支持,今天就探讨下 DuckDB 的 Catalog 与 Schema,以及它在 duckdb_fdw 里的重要作用。
数据库中 Catalog,Schema,Table 和 Column 的关系
解释:
Catalog(目录): 表示整个数据库或数据库管理系统。一个数据库服务器可以包含多个数据库,每个数据库都有自己的 Catalog。 Schema(模式): 在一个数据库中,Schema 用于组织和分类数据库对象,如表、视图、存储过程等。一个 Schema 可以包含多个表。 Table(表): 表示实际存储数据的地方。一个 Schema 可以包含多个表,每个表有自己的列。 Column(列): 表示表中的字段,定义了表中存储的数据的属性。每个表可以有多个列。
这个层次结构表明,一个数据库中可以包含多个 Schema,每个 Schema 可以包含多个表,而每个表可以包含多个列。这种结构有助于组织和管理数据库中的数据,并提供了一定程度的隔离和命名空间。
让我们和熟悉的 Excel 来做个对比来加深理解,
一个目录下可能有 0 到多个 Excel 文件,一个 Excel 文件可能有 1 到多个 Sheet,一个 sheet 可能有多个列。
Catalog
DuckDB 里执行如下语句的时候第一列是table_catalog
SELECT * FROM information_schema.tables;
table_catalog
列表示表所属的数据库目录。在大多数数据库中,数据库目录通常是数据库的名称。
另外发现这个 table_catalog 和访问的数据库文件名保持一致(duckdb允许attach时候设置别名),如果使用内存数据库,则为memory
。
运行 duckdb CLI
./duckdb default.db
查看系统视图information_schema.tables
information_schema.tables
系统视图提供了一种更加标准化的方式来获取关于数据库表(包括视图)的元数据。
当然也可以使用duckdb_tables
,它返回的结果集包含一些在information_schema.tables
中未包含的列。
SELECT table_catalog
,table_schema
,table_name,
table_type
FROM information_schema.tables;
返回
┌───────────────┬──────────────┬────────────┬────────────┐
│ table_catalog │ table_schema │ table_name │ table_type │
│ varchar │ varchar │ varchar │ varchar │
├────────────────────────────────────────────────────────┤
│ 0 rows │
└────────────────────────────────────────────────────────┘
让我们创建第一个表,
CREATE TABLE customer (
c_custkey int8 NULL,
c_name varchar NULL,
c_address varchar NULL,
c_nationkey int8 NULL,
c_phone varchar NULL,
c_acctbal numeric NULL,
c_mktsegment varchar NULL,
c_comment varchar NULL
);
添加一个新的库
ATTACH '/Users/m2max/temp/duckdb0' AS sf10;
ATTACH 语句将一个新的数据库文件添加到目录中,可以从中读取和写入数据。
再查看下系统视图,
SELECT table_catalog
,table_schema
,table_name,
table_type
FROM information_schema.tables;
┌───────────────┬──────────────┬────────────┬────────────┐
│ table_catalog │ table_schema │ table_name │ table_type │
│ varchar │ varchar │ varchar │ varchar │
├───────────────┼──────────────┼────────────┼────────────┤
│ default │ main │ customer │ BASE TABLE │
│ default │ main │ t1 │ BASE TABLE │
│ sf10 │ main │ customer │ VIEW │
│ sf10 │ main │ orders │ VIEW │
│ sf10 │ main │ part │ VIEW │
│ sf10 │ main │ supplier │ VIEW │
│ sf10 │ main │ region │ VIEW │
│ sf10 │ main │ nation │ VIEW │
│ sf10 │ main │ partsupp │ VIEW │
│ sf10 │ main │ lineitem │ VIEW │
├───────────────┴──────────────┴────────────┴────────────┤
│ 10 rows 4 columns │
└────────────────────────────────────────────────────────┘
sf10 catalog 中有 8 张视图(我用于测试 TPCH_sf10 的例子)
同时注意 customer 表有 2 个,一个在 catalog default 中,一个在 sf10 中
查看默认 Catalog
SELECT current_catalog();
┌───────────────────┐
│ current_catalog() │
│ varchar │
├───────────────────┤
│ default │
└───────────────────┘
查询默认 Catalog 的 main Schema 的 customer 表
SELECT * FROM customer LIMIT 3;
┌───────────┬─────────┬───────────┬─────────────┬─────────┬───────────────┬──────────────┬───────────┐
│ c_custkey │ c_name │ c_address │ c_nationkey │ c_phone │ c_acctbal │ c_mktsegment │ c_comment │
│ int64 │ varchar │ varchar │ int64 │ varchar │ decimal(18,3) │ varchar │ varchar │
├────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 0 rows │
└────────────────────────────────────────────────────────────────────────────────────────────────────┘
切换默认 Catalog 为 sf10,
SET SEARCH_PATH TO 'sf10,default';
查看默认 Catalog
SELECT current_catalog();
┌───────────────────┐
│ current_catalog() │
│ varchar │
├───────────────────┤
│ sf10 │
└───────────────────┘
SELECT c_name FROM customer LIMIT 3;
┌────────────────────┐
│ c_name │
│ varchar │
├────────────────────┤
│ Customer#000422881 │
│ Customer#000422882 │
│ Customer#000422883 │
└────────────────────┘
上面 SQL 等价于
SELECT sf10.main.c_name FROM customer LIMIT 3;
包含了 catalog,schema,表名,接下来我们就说下 Schema。
Schema
USE default;
CREATE SCHEMA x;
再 SCHEMA x 下创建一张 customer 表
CREATE TABLE default.x.customer as SELECT * FROM customer LIMIT 2;
这个时候如果再执行
SELECT table_catalog
,table_schema
,table_name,
table_type
FROM information_schema.tables;
会发现有三个 customer 表,属于不同的 catalog 和 schema。
┌───────────────┬──────────────┬────────────┬────────────┐
│ table_catalog │ table_schema │ table_name │ table_type │
│ varchar │ varchar │ varchar │ varchar │
├───────────────┼──────────────┼────────────┼────────────┤
│ default │ main │ customer │ BASE TABLE │
│ default │ x │ customer │ BASE TABLE │
│ sf10 │ main │ customer │ VIEW │
│ sf10 │ main │ orders │ VIEW │
│ sf10 │ main │ part │ VIEW │
│ sf10 │ main │ supplier │ VIEW │
│ sf10 │ main │ region │ VIEW │
│ sf10 │ main │ nation │ VIEW │
│ sf10 │ main │ partsupp │ VIEW │
│ sf10 │ main │ lineitem │ VIEW │
├───────────────┴──────────────┴────────────┴────────────┤
│ 10 rows 4 columns │
└────────────────────────────────────────────────────────┘
Catalog 和 Schema 在 duckdb_fdw 中的应用。
IMPORT FOREIGN SCHEMA public LIMIT TO(customer) FROM SERVER DuckDB_server INTO public;
等价于
CREATE FOREIGN TABLE customer (
c_custkey int8 NULL,
c_name varchar NULL,
c_address varchar NULL,
c_nationkey int8 NULL,
c_phone varchar NULL,
c_acctbal numeric NULL,
c_mktsegment varchar NULL,
c_comment varchar NULL
) OPTIONS (table 'default.main.customer');
就是说它导入的是当前数据库,main schema 的 customer,
如果我想使用IMPORT FOREIGN SCHEMA
导入的是 x schema 里的 customer 呢?
IMPORT FOREIGN SCHEMA x LIMIT TO(customer) FROM SERVER DuckDB_server INTO public;
但如果我想导入的是 sf10 catalog 的表呢?
# attach
SELECT duckdb_execute('duckdb_server','ATTACH ''/Users/m2max/temp/duckdb0'' AS sf10 ;');
# 修改catlog优先访问顺序
SELECT duckdb_execute('duckdb_server','SET SEARCH_PATH ''sf10,memory''');
接下来执行
IMPORT FOREIGN SCHEMA public FROM SERVER DuckDB_server INTO public;
就可以把 sf10(duckdb0)main schema 里的所有表都导入成功了。
当然如果之前导入过 customer,会因为重复而报错。