Oracle && OceanBase 相关文档,希望互相学习,
共同进步
1.背景知识
1.1 概述
OceanBase存储过程,同Oracle 用来"封装"一组为了完成特定功能的 SQL 语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
PL/SQL 是数据库对 SQL 语句的扩展 ,在普通 SQL 语句的基础上增加了编程语言 的特点,把数据操作和查询语句组织在 PL/SQL 代码的过程化代码中,通过逻辑判断、循环等操作实现复杂的功能 使用 PL/SQL 可以编写具有很多高级功能的程序,能够把业务逻辑封装在数据库内部,提供更好的抽象或者安全性,同时减少了网络的交互,并且调用更快,从而提升整体性能。
1.2 执行理论
OceanBase PL 采用编译执行的方式,使用 LLVM 生成 native code,并直接把机器码装载到程序段执行,无需依赖外部 C 编译器和生成动态链接库。编译后的程序比解释执行性能提升 1.05 到 2.4 倍。参见文章:OceanBase存储过程基本使用
1)PL 缓存器(pl-cache)
OceanBase 会将 PL 编译后的可执行代码缓存在 pl-cache 中,以便对编译结果进行复用,降低 PL 编译的开销,提升整体的执行效率。
如果 pl-cache 中已缓存该条命令涉及到的 PL 程序的编译结果,OceanBase 会使用该缓存的编译结果执行,否则进入正常的 PL 程序编译流程,生成的编译结果将被添加到 pl-cache 中供后续 PL 命令复用。
2)语法分析(Parser)
当一条 PL 命令涉及到的 PL程序未命中 pl-cache 的时候,会进入正常的语法分析流程。语法分析的过程把输入的 PL程序字符串转换为一颗语法树。
语法树不包含语义信息,仅仅是是字面的表示。这个过程完全使用 flex 和 bison 自动生成。输出结果是以 ParseNode 为节点的树。
3)语义分析(Resolver)
语义分析:输入的语法树转换为包含 PL 程序语义的内部数据结构表示(ObPLAST)。在此过程中,还要根据 PL 程序的语义进行语义检查,及时报错。
例如,语义检查的内容包括:
1、在使用变量时,变量是否已经进行了定义,类型是否合法。
2、同一个名字空间下符号是否有重复定义,对于重复定义的符号需要及时报错。
3、PL 程序中使用的表、列、自定义类型、包、函数等对象是否已经存在。语义分析的输出为 ObPLAST,这个结构是由符号表、类型表、异常表、标签表以及多个 ObPLStmt 和的集合,多个 stmt 对象表示 PL程序中的每条指令语句,如 IF、While、Goto。
4)代码生成(Code generator)
OceanBase PL 采用编译执行的方式,因此由语义分析输出的 ObPLAST 还会进一步进行翻译,为后面编译成可执行代码做进一步的准备。OceanBase 的 PL 采用了 LLVM 作为编译器的后端,代码生成则是为通过使用 LLVM 提供的接口,把 AST 树翻译成 IR 中间码的过程。IR 码可以输出,以核对翻译过程是否正确。
5)编译(Compiler)
通过 LLVM JIT 把 IR 码生成可执行代码的过程,生成的可执行代码会缓存到 pl-cache 中,供其他语句复用。
6)PL执行(PL Execution)
PL 执行器:负责执行编译生成的可执行代码,除了执行代码外,还执行:
1、构造当前 PL的执行环境,比如设置一些权限相关上下文、准本入参。
2、通过 SPl(Server Programming Interface)与 SQL 引擎交互,将 PL 中需要执行的 SQL 语句交给 SQL 引擎执行,并从 SQL 引擎中返回结果。
3、处理 PL 的出参以及函数的返回值等信息。
说明: SPl用于在 PL 程序中执行 SQL 操作,并返回结果。广泛应用在 PL实现的各个环节,如 procedure、function、trigger 等。
PL引擎(PL Engine)和 SQL引擎(SQL Engine)可互相交互,SQL 可以直接访问 PL引擎,比如在一个 SQL 语句中使用了用户自定义函数(function)。PL 引擎可以通过 SPI 接口访问 SQL 引擎,比如在 PL 里进行表达式计算和执行 SQL 语句。
1.3 PL 的分布式执行
OceanBase 的分布式特性,天然决定 PL的执行也是分布式的。但对于 PL本身是不会分布式执行的,PL 自身的解析、编译和执行都在某一台 OBServer 节点上完成。
当 PL 里涉及到 SQL 交互时,会通过 SPI调用 SQL Engine,由 SQL Engine 执行 SQL 语句。如果该 SQL 语句是一个分布式的,那么自然而然会进行分布式执行。分布式执行的 SQL 调用了 PL 函数时,PL 函数可能会在多台 OBServer 节点上编译执行,每台OBServer 会保证在相同的环境参数下进行编译,从而编译出的 PL具有相同的行为。
1.4 存储过程优势
● 性能优化:预编译执行,减少SQL解析和编译开销
● 代码复用:一次编写,多次调用,减少代码冗余。存储过程只在创建时进行编译,以后每次执行存储过程都不需要重新编译,而一般 SQL 语句每执行一次就编译一次,使用存储过程可提高数据库执行速度。
● 安全性:通过权限控制保护数据,避免直接表操作。参数化的存储过程可以防止 SQL 注入式攻击。
● 减少网络传输:批量操作在数据库端完成,减少客户端与服务器间的数据传输。存储过程位于服务器上,调用的时候只需要传递存储过程的名称以及参数即可,降低了网络传输的数据量。
● 事务管理:支持复杂的事务控制,确保数据一致性
2. 实验
2.1 准备工作
1)创建日志表 tbl_dayend_log
创建索引 seq_tbl_dayend_log
2)创建存储过程 get_logger1
sql
create table TBL_DAYEND_LOG
(
logid NUMBER NOT NULL ,
function_name VARCHAR2(200),
table_name VARCHAR2(200),
data_issue VARCHAR2(8),
logtime DATE,
type VARCHAR2(50),
message VARCHAR2(4000)
)
;
-- 创建序列
CREATE SEQUENCE seq_TBL_DAYEND_LOG
START WITH 1
INCREMENT BY 1
MAXVALUE 9999999999999
CYCLE
CACHE 20;
sql
---创建存储过程
CREATE OR REPLACE PROCEDURE GET_LOGGER1(pDataIssue IN varchar2) is --处理信息
--插入日志
BEGIN
insert into TBL_DAYEND_LOG
(LOGID,FUNCTION_NAME, TABLE_NAME, DATA_ISSUE, LOGTIME, TYPE, MESSAGE)
values
(seq_TBL_DAYEND_LOG.nextval,'---test', '', pDataIssue, sysdate, '1', 'ok');
commit;
END GET_LOGGER1;
存储过程,单独执行,测试也没有问题

查看表中结果:
sql
SELECT to_char(logtime,'yyyymmdd hh24:mi:ss') aa ,t.* FROM TBL_DAYEND_LOG t;

2.2 在命令行调用
1)连接
sql
obclient -h192.168.3.14 -P2881 -ufeng@fengoracle -p'xxx1'
2)执行
sql
call GET_LOGGER1('2025-11');
执行截图:

再次查询表中,已多一条记录,插入成功,调用成功
3)命令调用
sql
obclient -h192.168.3.14 -P2881 -ufeng@fengoracle -p'xxx' -e "call GET_LOGGER1('2025-08');"
或
obclient -h192.168.3.14 -P2881 -ufeng@fengoracle -p'XXX' -e"
set echo off;
call GET_LOGGER1(2025-11);
select count(*) from tbl_dayend_log;
"
截图:


从截图看,执行成功,插入正确。ok
2.3 脚本调用
1)创建通用的调用脚本 proc.sh
比如:入参3个,分别是 连接描述符、过程名、过程参数
bash
#!/bin/bash
source ~/.bash_profile
source /home/oceanbase/test/config/config.ini
userid=$tbl_userid
packageName=$2
params=$3
echo $userid
#echo $packageName
#echo $params
##sqlplus -S $userid <<EOF
#echo obclient $userid
echo "---调用过程开始 "
#eval "obclient $userid -e\"
#set echo off;
#call $packageName($params);
#select count(*) from tbl_dayend_log;
#\""
obclient $userid -e"
set echo off;
call $packageName($params);
select count(*) from tbl_dayend_log;
"
echo "---调用过结束 "
2)创建公共的参数初始化
bash
cat config.ini
tbl_userid="-h192.168.3.14 -P2881 -ufeng@fengoracle -p'xxx^01'"
workPath=/home/oceanbase/test
3)脚本调用实验 pro_test.sh
bash
vim pro_test.sh
bash
#! /bin/bash
source ~/.bash_profile
source /home/oceanbase/test/config/config.ini
if [ $# -eq 2 ]; then
year=$1
month=$2
else
year=`date -d" last month" +%Y`
month=`date -d" last month" +%m`
fi
#echo "1:$workPath/proc.sh"
#echo "2: $tbl_userid"
#echo "3:'GET_LOGGER1'"
#echo "4:$year-$month"
#echo "$tbl_userid"
#echo "----"
#echo ""$tbl_userid""
sh $workPath/proc.sh "$tbl_userid" "GET_LOGGER1" "$year-$month"
为了避免干扰,先清空表 ,再测试

调用:sh pro_test.sh

发现报错,提示貌似是无法登录,语法不对
4) 报错处理
简单还原试验:
oceanbase@appx-container test\]$echo $userid -h192.168.3.14 -P2881 -ufeng@fengoracle -p'xxx' \[oceanbase@appx-container test\]$ obclient $userid ERROR 1045 (42000): Access denied for user 'FENG'@'xxx.xxx.xxx.xxx' (using password: YES) \[oceanbase@appx-container test\]$ 发现此时就是上门的报错,无法直接识别,需要二次解析,这里用到eval 基本语法: eval \[arguments
arguments: 可以是一个或多个参数,串联成命令字符串
功能说明 :1)二次扫描命令行:先替换变量、命令替换、特殊字符,再执行命令
2)适用场景:处理复杂变量、动态生成命令
3)安全风险:避免处理用户输入,防止命令注入
按上面思路,修改proc.sh
bash
eval "obclient $userid -e\"
set echo off;
call $packageName($params);
select count(*) from tbl_dayend_log;
\""
执行截图:

看上图,执行成功。ok!
注意双引号中的双引号,需要转义一下哦~~~~
实验验证:ok 
项目管理--相关知识 
项目管理-项目绩效域1/2_八大绩效域和十大管理有什么联系-CSDN博客
项目管理-计算题公式【复习】_项目管理进度计算题公式:乐观-CSDN博客
项目管理-相关知识(组织通用治理、组织通用管理、法律法规与标准规范)-CSDN博客
Oracle其他文档,希望互相学习,
共同进步
Oracle-找回误删的表数据(LogMiner 挖掘日志)_oracle日志挖掘恢复数据-CSDN博客
oracle 跟踪文件--审计日志_oracle审计日志-CSDN博客
ORA-12899报错,遇到数据表某字段长度奇怪现象:"Oracle字符型,长度50"但length查却没有50_varchar(50) oracle 超出截断-CSDN博客