利用DuckDB列表一句SQL输出乘法口诀表
先热身,用生成式列表输出几行不同长度的序列,从默认的标题列可知,生成式列表后台实际调用了list_apply函数。
D select [i for i in range(1,a+1)]from range(1,6)t(a);
┌─────────────────────────────────────────────────────┐
│ main.list_apply("range"(1, (a + 1)), (lambda i: i)) │
│ int64[] │
├─────────────────────────────────────────────────────┤
│ [1] │
│ [1, 2] │
│ [1, 2, 3] │
│ [1, 2, 3, 4] │
│ [1, 2, 3, 4, 5] │
└─────────────────────────────────────────────────────┘
再把生成式的结果表达式变为字符串连接
D select [i::text||'*'||a::text||'='||(i*a)::text for i in range(1,a+1)]from range(1,6)t(a);
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ main.list_apply("range"(1, (a + 1)), (lambda i: ((((CAST(i AS VARCHAR) || '*') || CAST(a AS VARCHAR)) || '=') || C… │
│ varchar[] │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ['1*1=1'] │
│ ['1*2=2', '2*2=4'] │
│ ['1*3=3', '2*3=6', '3*3=9'] │
│ ['1*4=4', '2*4=8', '3*4=12', '4*4=16'] │
│ ['1*5=5', '2*5=10', '3*5=15', '4*5=20', '5*5=25'] │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
其中的强制转换可以省略,DuckDB会自动转换
D select [i||'*'||a||'='||(i*a) for i in range(1,a+1)]from range(1,6)t(a);
┌───────────────────────────────────────────────────────────────────────────────────────────┐
│ main.list_apply("range"(1, (a + 1)), (lambda i: ((((i || '*') || a) || '=') || (i * a)))) │
│ varchar[] │
├───────────────────────────────────────────────────────────────────────────────────────────┤
│ ['1*1=1'] │
│ ['1*2=2', '2*2=4'] │
│ ['1*3=3', '2*3=6', '3*3=9'] │
│ ['1*4=4', '2*4=8', '3*4=12', '4*4=16'] │
│ ['1*5=5', '2*5=10', '3*5=15', '4*5=20', '5*5=25'] │
└───────────────────────────────────────────────────────────────────────────────────────────┘
字符串列表形式的输出不美观,可以转换为纯字符串。listagg函数直接嵌套unnest函数的方法不支持。
D select listagg(unnest([i||'*'||a||'='||(i*a) for i in range(1,a+1)])) from range(1,6)t(a);
Binder Error:
UNNEST not supported hereLINE 1: select listagg(unnest([i||'*'||a||'='||(i*a) for i in range(1,a+1)])) from...^
但是可以单独对列表unnest,就把列表中多个值转成多行
D select unnest([i||'*'||a||'='||(i*a) for i in range(1,a+1)]) from range(1,6)t(a);
┌───────────────────────────────────────────────────────────────────────────────────────────────────┐
│ unnest(main.list_apply("range"(1, (a + 1)), (lambda i: ((((i || '*') || a) || '=') || (i * a))))) │
│ varchar │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 1*1=1 │
│ 1*2=2 │
│ 2*2=4 │
│ 1*3=3 │
│ 2*3=6 │
│ 3*3=9 │
│ 1*4=4 │
│ 2*4=8 │
│ 3*4=12 │
│ 4*4=16 │
│ 1*5=5 │
│ 2*5=10 │
│ 3*5=15 │
│ 4*5=20 │
│ 5*5=25 │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 15 rows │
└───────────────────────────────────────────────────────────────────────────────────────────────────┘
还可以带上原来的列a
D select a,unnest([i||'*'||a||'='||(i*a) for i in range(1,a+1)]) from range(1,6)t(a);
┌───────┬───────────────────────────────────────────────────────────────────────────────────────────────────┐
│ a │ unnest(main.list_apply("range"(1, (a + 1)), (lambda i: ((((i || '*') || a) || '=') || (i * a))))) │
│ int64 │ varchar │
├───────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 1 │ 1*1=1 │
│ 2 │ 1*2=2 │
│ 2 │ 2*2=4 │
│ 3 │ 1*3=3 │
│ 3 │ 2*3=6 │
│ 3 │ 3*3=9 │
│ 4 │ 1*4=4 │
│ 4 │ 2*4=8 │
│ 4 │ 3*4=12 │
│ 4 │ 4*4=16 │
│ 5 │ 1*5=5 │
│ 5 │ 2*5=10 │
│ 5 │ 3*5=15 │
│ 5 │ 4*5=20 │
│ 5 │ 5*5=25 │
├───────┴───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 15 rows 2 columns │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────┘
用listagg访问子查询就可以了,注意要针对列a排序
D select listagg(b,' ') from(select a,unnest([i||'*'||a||'='||(i*a) for i in range(1,a+1)])b from range(1,6)t(a))group by a;
┌───────────────────────────────────┐
│ listagg(b, ' ') │
│ varchar │
├───────────────────────────────────┤
│ 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 │
│ 1*1=1 │
│ 1*4=4 2*4=8 3*4=12 4*4=16 │
│ 1*3=3 2*3=6 3*3=9 │
│ 1*2=2 2*2=4 │
└───────────────────────────────────┘
D select listagg(b,' ') from(select a,unnest([i||'*'||a||'='||(i*a) for i in range(1,a+1)])b from range(1,6)t(a))group by a order by a;
┌───────────────────────────────────┐
│ listagg(b, ' ') │
│ varchar │
├───────────────────────────────────┤
│ 1*1=1 │
│ 1*2=2 2*2=4 │
│ 1*3=3 2*3=6 3*3=9 │
│ 1*4=4 2*4=8 3*4=12 4*4=16 │
│ 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 │
└───────────────────────────────────┘
上述输出随着数字变大,算式变长,没有对齐,可以用printf函数格式化,函数的格式符按照C语言的标准格式,-8表示8位宽左对齐,但注意格式符用单引号而不是双引号括起,双引号可以包括在单引号内原样输出。
D select listagg(printf('%-8s',b),' ') from(select a,unnest([i||'*'||a||'='||(i*a) for i in range(1,a+1)])b from range(1,6)t(a))group by a order by a;
┌──────────────────────────────────────────────┐
│ listagg(printf('%-8s', b), ' ') │
│ varchar │
├──────────────────────────────────────────────┤
│ 1*1=1 │
│ 1*2=2 2*2=4 │
│ 1*3=3 2*3=6 3*3=9 │
│ 1*4=4 2*4=8 3*4=12 4*4=16 │
│ 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 │
└──────────────────────────────────────────────┘D select printf('Benchmark "%s" took %d seconds', 'CSV', 42);
┌─────────────────────────────────────────────────────┐
│ printf('Benchmark "%s" took %d seconds', 'CSV', 42) │
│ varchar │
├─────────────────────────────────────────────────────┤
│ Benchmark "CSV" took 42 seconds │
└─────────────────────────────────────────────────────┘
以下是结合递归CTE用行间列表运算输出杨辉三角
D with recursive t as(select 1 lv, [1]y union all select lv+1,[case when i=1 or i=lv+1 then 1 else y[i-1]+y[i] end for i in range(1, lv+2)] from t where lv <5 )from t;
┌───────┬─────────────────┐
│ lv │ y │
│ int32 │ int32[] │
├───────┼─────────────────┤
│ 1 │ [1] │
│ 2 │ [1, 1] │
│ 3 │ [1, 2, 1] │
│ 4 │ [1, 3, 3, 1] │
│ 5 │ [1, 4, 6, 4, 1] │
└───────┴─────────────────┘
