初级版一
方法:先写一个福字的图片,拉大,放到excel中,将excel的列宽调整成和行高一样,然后将笔画所在的点位标记,相当于手动点位图像素,最后用公式拼出sql
select '','','','','','','','','','','','','','','','','','','','','','','','','' from dual union all
select '','','','','','','福','福','福','','','','','','','','','','福','福','','','','','' from dual union all
select '','','','','','','','福','福','福','','','','','','福','福','福','福','福','福','','','','' from dual union all
select '','','','','','','','','福','福','','','','','福','福','福','福','','','','','','','' from dual union all
select '','','','','','','','','','','','','','','','','','','','','','','','','' from dual union all
select '','','','','','','','','','','','','','','','','','','','','','','','','' from dual union all
select '','','','','','','','','','','','','福','','','','福','福','福','福','福','','','','' from dual union all
select '','','','','','','','福','福','福','','','福','福','福','福','','','','福','','','','','' from dual union all
select '','','','','','福','福','福','','福','','','','福','','','','','','福','','','','','' from dual union all
select '','福','福','福','福','','','','福','','','','','','福','','福','福','福','福','','','','','' from dual union all
select '','','','','','','','福','福','','','','','','福','福','','','','','','','','','' from dual union all
select '','','','','','','','福','','','','','','','','','','','','','','','','','' from dual union all
select '','','','','','','福','福','','','','','','','','','','','福','福','福','福','福','','' from dual union all
select '','','','','','','福','福','福','福','','福','福','福','福','福','福','福','','','','','福','福','' from dual union all
select '','','','','','福','','福','','福','福','','福','','','','福','福','','','','','福','福','' from dual union all
select '','','','','福','','','福','','','','','福','','','','福','','','','','','福','','' from dual union all
select '','','','福','','','','福','','','','','福','','','','福','福','福','福','','','福','','' from dual union all
select '','','','福','','','','福','','','','','福','','福','福','福','','','','','','福','','' from dual union all
select '','','福','','','','','福','','','','','福','','','','福','','','','','','福','','' from dual union all
select '','福','','','','','','福','','','','','福','','','','福','','','','','','福','','' from dual union all
select '','','','','','','','福','','','','','福','','','福','福','福','福','福','','','福','','' from dual union all
select '','','','','','','福','福','','','','','福','福','福','','','','','','福','','福','','' from dual union all
select '','','','','','','福','福','','','','','','福','','','','','','','福','福','','','' from dual union all
select '','','','','','','','福','','','','','','','','','','','','','','福','','','' from dual union all
select '','','','','','','','','','','','','','','','','','','','','','','','','' from dual
初级版二
方法:同样使用方案一准备的数据,用listagg对多行数据进行拼接。注意此处listagg不需要within group是oracle18c新增的特性,如果是oracle18c以下或其他支持listagg的数据库,请将前面修改成listagg(f,chr(10)) within group(order by 1)
select listagg(f,chr(10)) from (
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' f from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||' '||' '||' '||' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||' '||' '||' '||' '||' '||'福'||'福'||'福'||'福'||'福'||'福'||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||'福'||'福'||'福'||'福'||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||'福'||'福'||'福'||'福'||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||' '||' '||'福'||'福'||'福'||'福'||' '||' '||' '||'福'||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||'福'||'福'||'福'||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' ' from dual union all
select ' '||'福'||'福'||'福'||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' '||'福'||'福'||'福'||'福'||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||'福'||'福'||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||'福'||' '||'福'||'福'||'福'||'福'||'福'||'福'||'福'||' '||' '||' '||' '||'福'||'福' from dual union all
select ' '||' '||' '||' '||' '||'福'||' '||'福'||' '||'福'||'福'||' '||'福'||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||'福'||'福' from dual union all
select ' '||' '||' '||' '||'福'||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' ' from dual union all
select ' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||'福'||'福'||'福'||' '||' '||'福'||' ' from dual union all
select ' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||'福'||'福'||'福'||' '||' '||' '||' '||' '||'福'||' ' from dual union all
select ' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' ' from dual union all
select ' '||'福'||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||'福'||'福'||'福'||'福'||'福'||' '||' '||'福'||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||'福'||'福'||'福'||' '||' '||' '||' '||' '||'福'||' '||'福'||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||'福'||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' from dual )
还可以倒过来
初级版三
方法同二,只是修改为以消息窗口输出,此sql语法为oracle语法,但是在opengauss上同样可以运行,只需要安装好即可,另外关于opengauss更多的oracle兼容支持,可以参考我的这个项目
截图所示为opengauss的data studio程序界面截图
declare
a varchar2(4000);
begin
select listagg(f,chr(10)) within group(order by 1) into a from (
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' f from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||' '||' '||' '||' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||' '||' '||' '||' '||' '||'福'||'福'||'福'||'福'||'福'||'福'||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||'福'||'福'||'福'||'福'||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||'福'||'福'||'福'||'福'||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||' '||' '||'福'||'福'||'福'||'福'||' '||' '||' '||'福'||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||'福'||'福'||'福'||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' ' from dual union all
select ' '||'福'||'福'||'福'||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' '||'福'||'福'||'福'||'福'||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||'福'||'福'||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||'福'||'福'||' '||'福'||'福'||'福'||'福'||'福'||'福'||'福'||' '||' '||' '||' '||'福'||'福' from dual union all
select ' '||' '||' '||' '||' '||'福'||' '||'福'||' '||'福'||'福'||' '||'福'||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||'福'||'福' from dual union all
select ' '||' '||' '||' '||'福'||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' ' from dual union all
select ' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||'福'||'福'||'福'||' '||' '||'福'||' ' from dual union all
select ' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||'福'||'福'||'福'||' '||' '||' '||' '||' '||'福'||' ' from dual union all
select ' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' ' from dual union all
select ' '||'福'||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||' '||'福'||' '||' '||' '||' '||' '||'福'||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||'福'||' '||' '||'福'||'福'||'福'||'福'||'福'||' '||' '||'福'||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||'福'||'福'||'福'||' '||' '||' '||' '||' '||'福'||' '||'福'||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||' '||' '||'福'||'福'||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||'福'||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||'福'||' '||' ' from dual union all
select ' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' '||' ' from dual );
dbms_output.put_line(a);
end;
以上都是手动模拟位图像素点输出,这个方法要玩什么花样也只能在像素点用什么字符以及用什么字体上做区分,接下来上高级版,让大家知道sql还能做到何种程度
进阶版一
首先我尝试了oracle21c的oml4py,在oracle中调用python,根据文字生成图片,然而oml4py环境是运行在latin下的,只能处理128个ascii,无法处理中文字符。所以我就使用了我开发的pl4py组件,只要是oracle10g以上都可以在oracle环境中编写python函数并执行
还可以让背景色和文字色进行交换
pl4py项目地址
--创建base64转blob函数
create or replace function base64toblob(base64_str clob)
return blob is
buf raw(64);
img blob;
l number;
begin
l := trunc(dbms_lob.getlength(base64_str) / 64) 1;
dbms_lob.createtemporary(img, false);
for rec in 1 .. l loop
buf := utl_encode.base64_decode(utl_raw.cast_to_raw(dbms_lob.substr(base64_str,
64,
(rec - 1) * 64 1)));
dbms_lob.append(img, buf);
end loop;
return img;
end;
/
--创建python函数
begin
pl4py.create_func(i_func_name => 'pyqstr2img.py',
i_contents => q'{def pyqstr2img(str):
import pygame,base64
pygame.init()
text = str
font = pygame.font.sysfont('microsoftyaheiui', 50)
ftext = font.render(text, true, (255, 0, 0),(227, 222, 28))
pygame.image.save(ftext, "/home/oracle/str2img.png")
img=open("/home/oracle/str2img.png","r b").read()
b=base64.b64encode(img).decode()
return b
}');
end;
/
---执行
select base64toblob(substr(a, 2, length(a) - 2)) r
from (select pl4py.call_func_eval('pyqstr2img.py', '福') a from dual) a;
同理,也可以使用oracle21c的dbms_mle功能,使用js调用来进行绘图,本篇不再赘述,懂js的,可以参考我的另一个项目
dbms_mle_ext_pkg-
使用这个包,不需要了解mle的语法及参数传输规则,只要懂plsql和js各自的语法即可轻松的在oracle中执行js代码
进阶版二
在小型点阵显示器上,比如老式的mp3屏幕,用的是字库点阵显示,根据汉字的gb2312编码来计算这个字在字库的哪个位置。如果我们把字库放到数据库里,用plsql模拟这个过程,也可以得到点阵字符输出
将背景替换成两个空格,字体替换成〇的效果
下面这个函数我为了节省代码量,提前计算好了福字所在的位置。
另外,这个函数中还使用了oracle的新特性限定表达式以及to_blob函数。
用到的字库文件为hzk16k,当然也可以使用其他字体及大小的点阵字库(字库文件来自于互联网,非本人收集)
create or replace function chr_from_hzk16 return varchar2 is
keys dbms_sql.varchar2_table := dbms_sql.varchar2_table(1 => '80',
2 => '40',
3 => '20',
4 => '10',
5 => '08',
6 => '04',
7 => '02',
8 => '01');
offsett int := 69247;
font_rect raw(2000);
ac raw(1);
flag int;
line varchar2(100);
out_str varchar2(4000);
begin
font_rect := dbms_lob.substr(lob_loc => to_blob(bfilename('db_backup_dir',
'hzk16k')),
amount => 32,
offset => offsett);
for k in 1 .. 16 loop
line:='';
for j in 1 .. 2 loop
for i in 1 .. 8 loop
ac := dbms_lob.substr(lob_loc => font_rect,
amount => 1,
offset => k * 2 j);
flag := bitand(to_number(to_char(ac),'xx'), to_number(keys (i ),'xx'));
line:=line||case when flag=0 then '.' else 'o' end;
end loop;
end loop;
out_str:=out_str||line||chr(10);
end loop;
dbms_output.put_line(out_str);
return out_str;
end;
/
select chr_from_hzk16 from dual;
进阶版三
上面几种方式,要么代码中输入的数据过多,要么就使用了外部程序或外部数据,有没有一种方案能跳出这两个缺点,而又能输出如进阶版二当中那样的显示效果呢?
进阶版二中,只使用了字库中的32字节数据,那么我们可以把这32字节数据先提取出来
000000001070098000701990e93009c0103c3bc4527492c4124411f810080000
可以看到前面后面都有0,我们可以把它截掉,下面这串就作程序的输入信息
1070098000701990e93009c0103c3bc4527492c4124411f81008
之所以不用utl_compress进行压缩,是因为这个字库本就已经压缩了,再压缩会导致数据体积变大。
这下我们只输入了长度为52的十六进制字符串(如果是二进制数据则只有26bytes),却得到了一个16*16的点阵文字信息,比文字自己显示的像素点还少
因为不再需要任何外部依赖,所以这段代码我们可以直接在墨天轮在线实训环境中执行
export nls_lang="american_america.al32utf8"
sqlplus / as sysdba
set serveroutput on;
set linesize 1000;
declare
keys dbms_sql.varchar2_table := dbms_sql.varchar2_table(1 => '80',
2 => '40',
3 => '20',
4 => '10',
5 => '08',
6 => '04',
7 => '02',
8 => '01');
zk varchar2(64) := '1070098000701990e93009c0103c3bc4527492c4124411f81008';
font_rect blob;
ac raw(1);
flag int;
line varchar2(100);
out_str varchar2(4000);
begin
zk := lpad(zk, 60, '0');
zk := rpad(zk, 64, '0');
font_rect := to_blob(zk);
for k in 1 .. 16 loop
line := '';
for j in 1 .. 2 loop
for i in 1 .. 8 loop
ac := dbms_lob.substr(lob_loc => font_rect,
amount => 1,
offset => k * 2 j);
flag := bitand(to_number(to_char(ac), 'xx'),
to_number(keys(i), 'xx'));
line := line || case
when flag = 0 then
' '
else
'〇'
end;
end loop;
end loop;
out_str := out_str || line || chr(10);
end loop;
dbms_output.put_line(out_str);
end;
/
进阶版四
有人肯定会说,出题者说是用sql,但我这有不少例子都是用的plsql,是不是犯规了?
那好,我就把上面这个版本换成select的sql。下面这个就是使用了递归sql来替代上面的循环处理(这里添加了base64编码,使输入信息更少)
with keys as
( select row_number()over(order by 1) id,column_value keys from
table(ora_mining_varchar2_nt('80','40','20','10','08','04','02','01'))) ,
zk as (select to_blob(utl_encode.base64_decode(utl_raw.cast_to_raw('aaaaabbwcyaacbmq6tajwba8o8rsdjleekqr baiaaa=='))) zk from dual),
i(line,str,i) as (
select 1 line, '' str,0 i from dual union all
select line 1, str||case when bitand(to_number(to_clob(dbms_lob.substr(lob_loc => zk,
amount => 1,
offset => trunc(line/8) 3)), 'xx'),
to_number((select keys from keys where keys.id=mod(line,8) 1), 'xx'))=0
then ' ' else '●'end||
case when mod(line 1,16)=0 then chr(10) end str,
mod(line,8) 1 i from zk,i where line<16*16)
select str from i where i.line=256;
在墨天轮实训环境中的演示效果
进阶版五
在做这些福字的时候,我发现一个问题,就是福字的像素都不够高,很难做出漂亮的,因为电脑的屏幕只有这么大,而且调程序显示字体大小有点麻烦。但是在网页上,浏览器只要ctrl 滑轮即可轻松调整页面大小,所以在墨天轮在线实训环境内,你可以凭你的想象去发挥。
最后,给大家做个大大的108p福,祝大家新的一年福气满满!
set pagesize 200
set linesize 200
with a as ( select
'1f8b0800000000000203bd9a097283300c45afe477ffcb75a685c6602d5fc2844c9724d8cf46bb608ccac1e5182f1dc6f4ef10c31977e284b9f66c4f9fe4318d77d6b5656c97c6b716f8e0f257473e1334ef5fbece52b7d84afb34e6df5c74c0336369c17803ff46739c31fd7536416ff31cafdbfc7f3fee0e68898aff7d182c7f523a229de4a55fc304162fb0a41b198cfd51831e8a8d8696855e11763f8d062a63b9490f152972b20872aa09d4677d163ac12c841bc228a1b84d122b0ab1c8d0d242c9a60cc1d2609d4eb89a2f5240cd721a61b66b7fd7620d37978fd37c547dc7544057236347816ea0f35bc7bc42169a33508fefb022db230f688d6d44da815e4872d14c4a693635965a4dba2c3954b895ab61841eabc059bcf1b044e76b07aacbb05c24366bd458be5566acc09ce9a18e8c7b036b95bee36275d491a4a7416b0ec8d9d450310573e16a8b88126b9e2b8c20d5423162d9d37aa9d399af3d6505aa28c6af4c5edef06e145358dda05662ddd5ca32e042110561aebbaa85dea234e41c25791f8b9d3ef70d6b51ce421be2ee1c6c5df74c810a6b29156cbbf6597a07ee32cdca226159d54a1abf32dfe8a4f3508f29911f8e97a8b1d6047edd89872ab28c5ac1645161d97994a700092b570da374f67cdbfa2ec8ef40c87b11599e7d0d9915f9ec0acbe905042c473724d3b60b308242526609d6b57c1e5c28a152f52fa15d589a97466525db924a15391ea75d22ea2ca2ca89a0dd95b2e27a360f272821f3ee6dc556c0e834daaebbd01a03494f2f67d942933243d4bb0051c1d54405f1396d766529799d15b6f5c2ec5fbe1945e4f5a2ee9f728726ae678764894a17743c3b28b5fdc7e683e6779b519b61d4bf3f9b85bf3e6afcbf396e629e4e492a235318f38bb303c3986e98b276656869d367f967e675fc77eee93ca1230c9eef8ba650e1cabbb2606595ee373e7a7ea6f188c95b9abe6779fd519d9bca8f1ec7a80ce65b8f2ded79faab93e73e276e126a6187726f72397e00c891656180280000'
a from dual),
b as (select to_clob(utl_compress.lz_uncompress(to_blob(a))) b from a),
c(line,str) as (select 0 line,'' str from dual
union all
select line 1,dbms_lob.substr( b,108,1 108*line) from b,c where line<=96)
select translate(str,'10',' 8') from c;
小技巧一
福字当然要大红色才喜气嘛(灵感来自于 https://www.modb.pro/db/234476 )
小技巧二
点阵放大
--横向放大2倍,用正则替换使字符翻倍
regexp_replace(str,'(\s|.)','\1\1')
--纵向放大两倍,接一个虚拟的两行记录的表产生笛卡尔积
,table(ora_mining_number_nt(1,2))
小技巧三
字符串太大,无法使用listagg聚合,可以使用oracle未公开的内置函数,理论最大可以拼4gb
select sys.avtune_log_listaggclob$(str||chr(10)) from table;
注意,此函数目前在网上找不到任何资料,本次属于独家首发公布。
更多神奇的数据库骚操作,请关注我,我不定时会在个人网站()、个人公众号()、墨天轮www.modb.pro、csdn()上发布有关数据库的新鲜玩意