CREATE MACRO 语句可在目录中创建标量宏或表宏(函数)。
对于标量宏,CREATE MACRO 后跟宏名称,括号内可选择性包含参数。接下来是 AS 关键字,后跟宏的表达式文本。按设计,标量宏只能返回单个值。对于表宏,语法与标量宏类似,只是将 AS 替换为 AS TABLE。表宏可以返回任意大小和结构的表。
如果
MACRO是临时的,它仅在当前数据库连接中可用,并在连接关闭时被删除。
示例
标量宏
创建一个将两个表达式(a 和 b)相加的宏
CREATE MACRO add(a, b) AS a + b;
创建一个宏,替换可能存在的现有定义
CREATE OR REPLACE MACRO add(a, b) AS a + b;
如果宏不存在则创建,否则不做任何操作
CREATE MACRO IF NOT EXISTS add(a, b) AS a + b;
为 CASE 表达式创建一个宏
CREATE MACRO ifelse(a, b, c) AS CASE WHEN a THEN b ELSE c END;
创建一个包含子查询的宏
CREATE MACRO one() AS (SELECT 1);
宏是依赖于模式(Schema)的,并且有一个别名:FUNCTION
CREATE FUNCTION main.my_avg(x) AS sum(x) / count(x);
创建一个带有默认参数的宏
CREATE MACRO add_default(a, b := 5) AS a + b;
创建一个宏 arr_append(功能等同于 array_append)
CREATE MACRO arr_append(l, e) AS list_concat(l, list_value(e));
创建一个带有类型化参数的宏
CREATE MACRO is_maximal(a INTEGER) AS a = 2^31 - 1;
表宏
创建一个无参数的表宏
CREATE MACRO static_table() AS TABLE
SELECT 'Hello' AS column1, 'World' AS column2;
创建一个带有参数(可以是任何类型)的表宏
CREATE MACRO dynamic_table(col1_value, col2_value) AS TABLE
SELECT col1_value AS column1, col2_value AS column2;
创建一个返回多行的表宏。如果它已存在,将被替换;且它是临时的(连接结束时会自动删除)
CREATE OR REPLACE TEMP MACRO dynamic_table(col1_value, col2_value) AS TABLE
SELECT col1_value AS column1, col2_value AS column2
UNION ALL
SELECT 'Hello' AS col1_value, 456 AS col2_value;
将列表作为参数传递
CREATE MACRO get_users(i) AS TABLE
SELECT * FROM users WHERE uid IN (SELECT unnest(i));
以下是如何使用 get_users 表宏的示例
CREATE TABLE users AS
SELECT *
FROM (VALUES (1, 'Ada'), (2, 'Bob'), (3, 'Carl'), (4, 'Dan'), (5, 'Eve')) t(uid, name);
SELECT * FROM get_users([1, 5]);
若要对任意表定义宏,请使用 query_table 函数。例如,以下宏用于计算表中各列的校验和
CREATE MACRO checksum(tbl) AS TABLE
SELECT bit_xor(md5_number(COLUMNS(*)::VARCHAR))
FROM query_table(tbl);
CREATE TABLE tbl AS SELECT unnest([42, 43]) AS x, 100 AS y;
SELECT * FROM checksum('tbl');
重载
可以根据参数的类型或数量对宏进行重载;这适用于标量宏和表宏。
通过提供重载,我们可以同时拥有具有不同函数体的 add_x(a, b) 和 add_x(a, b, c)。
CREATE MACRO add_x
(a, b) AS a + b,
(a, b, c) AS a + b + c;
SELECT
add_x(21, 42) AS two_args,
add_x(21, 42, 21) AS three_args;
| 两个参数 | 三个参数 |
|---|---|
| 63 | 84 |
CREATE OR REPLACE MACRO is_maximal
(a TINYINT) AS a = 2^7 - 1,
(a INT) AS a = 2^31 - 1;
SELECT
is_maximal(127::TINYINT) AS tiny,
is_maximal(127) AS regular;
| 微型 | 常规 |
|---|---|
| true | false |
语法
宏允许您为表达式组合创建快捷方式。
CREATE MACRO add(a) AS a + b;
Binder Error:
Referenced column "b" not found in FROM clause!
此操作有效
CREATE MACRO add(a, b) AS a + b;
用法示例
SELECT add(1, 2) AS x;
| x |
|---|
| 3 |
然而,此操作会失败
SELECT add('hello', 3);
Binder Error:
Could not choose a best candidate function for the function call "add(STRING_LITERAL, INTEGER_LITERAL)". In order to select one, please add explicit type casts.
Candidate functions:
add(DATE, INTEGER) -> DATE
add(INTEGER, INTEGER) -> INTEGER
宏可以拥有默认参数。
b 是一个默认参数
CREATE MACRO add_default(a, b := 5) AS a + b;
以下操作结果为 42
SELECT add_default(37);
命名参数的顺序无关紧要
CREATE MACRO triple_add(a, b := 5, c := 10) AS a + b + c;
SELECT triple_add(40, c := 1, b := 1) AS x;
| x |
|---|
| 42 |
使用宏时,它们会被展开(即替换为原始表达式),并且展开表达式中的参数将被提供的实参替换。逐步演示:
我们在查询中使用上面定义的 add 宏
SELECT add(40, 2) AS x;
在内部,add 被替换为它的定义 a + b
SELECT a + b AS x;
然后,参数被提供的实参替换
SELECT 40 + 2 AS x;
限制
使用子查询宏
表宏以及使用标量子查询定义的标量宏不能用作表函数的参数。DuckDB 将返回以下错误
Binder Error:
Table function cannot contain subqueries
重载
宏函数的重载必须在创建时设置,除非先删除第一个定义,否则无法定义两个同名的宏。
递归函数
不支持定义递归函数。例如,以下旨在计算斐波那契数列第 n 个数字的宏会失败
CREATE OR REPLACE FUNCTION fibo(n) AS (SELECT 1);
CREATE OR REPLACE FUNCTION fibo(n) AS (
CASE
WHEN n <= 1 THEN 1
ELSE fibo(n - 1)
END
);
SELECT fibo(3);
Binder Error:
Max expression depth limit of 1000 exceeded. Use "SET max_expression_depth TO x" to increase the maximum expression depth.
第一个函数上的函数链式调用不起作用
宏不支持在第一个函数上使用点运算符进行函数链式调用。为了说明这一点,请看一个使用 lower 函数的例子,它是有效的
CREATE OR REPLACE MACRO low(s) AS lower(s);
SELECT low('AA');
然而,将 lower(s) 重写为使用函数链式调用则无效
CREATE OR REPLACE MACRO low(s) AS s.lower();
SELECT low('AA');
Binder Error:
Referenced column "s" not found in FROM clause!