
在2020年12月8日,Oracle首先在OCI云端为大家提供了Oracle Database 21c,并且在2021年8月13日发布了运行在Linux上的On-Premises版本。在21c版本中,提供了215个(截至目前)新特性,我们将通过系列文章,为大家介绍这些新特性。我们按照主题,将这些新特性分为如下几个主题:
软件安装
应用程序开发
大数据和数据仓库
高性能与高可用
数据库工具
数据库安全
数据库管理

在今天的内容中,我们将为您介绍数据库安装与应用程序开发新特性。从19c开始,大家已经喜欢使用rpm的方式安装数据库,因为通过这种方式,您只需执行几个简单的命令,就可以迅速完成数据库软件的安装及数据库的创建。接下来就为您介绍Oracle Database 21c的安装,今天我们使用的操作系统是Oracle Enterprise Linux,您也可以使用您所熟悉的其他Linux操作系统。
1、软件下载
通过如下地址下载Oracle Database 21c的RPM文件。
https://www.oracle.com/database/technologies/oracle-database-software-downloads.html

2、操作系统配置
在Linux系统上安装Oracle Database往往需要经过一个比较复杂的系统配置过程,如果配置出错将导致安装失败。在十多年前,比如在OEL5的时候,我们就为大家提供了操作系统预处理包,在Linux 7和8也不例外,您可以根据自己的操作系统,来执行下面的预安装操作。
Oracle Linux 7
# yum install oracle-database-preinstall-21c
Oracle Linux 8
# dnf install oracle-database-preinstall-21c
预安装的执行界面如下所示,一般经过几分钟的时间,就可以看到命令提示配置成功。

3、安装数据库软件
# yum -y localinstall oracle-database-ee-21c-1.0-1.ol8.x86_64.rpm

4、创建数据库
您可以根据提示,使用root身份执行如下脚本。
/etc/init.d/oracledb_ORCLCDB-21c configure

5、设定环境变量并访问数据库
关于环境变量的设置,想必大家都会非常熟悉,我们首先需要设定ORACLE_HOME,然后在PATH当中将ORACLE_HOME/bin添加进去,这样我们就可以非常方便地使用sqlplus等工具。


接下来,我们将为您介绍在Oracle Database 21c中,关于应用程序开发的新特性。在最新版本的 Oracle 数据库 21c 中提供了新运算符、参数、表达式和 SQL 宏。Oracle 21c 引入了三个新的运算符,EXCEPT、EXCEPT ALL 和 INTEREST ALL。SQL 集合运算符现在支持 ANSI SQL 中定义的所有关键字。新的运算符 EXCEPT [ALL] 在功能上等同于 MINUS [ALL]。运算符 MINUS 和 INTERSECT 现在都支持关键字 ALL。您可以在初始化参数中使用表达式,这些表达式在数据库启动期间进行评估。您现在可以指定一个考虑到当前系统配置和环境的表达式。这在 Oracle 自治数据库环境中特别有用。您可以创建 SQL 宏 (SQM),将常见的 SQL 表达式和语句分解为可在其他 SQL 语句中使用的可重用、参数化构造。SQL 宏可以是标量表达式,通常用于 SELECT 列表、WHERE、GROUP BY 和 HAVING 子句,从而封装计算和业务逻辑,它也可以是Table表达式,通常用于 FROM 子句。
实验1:新的集合操作符
集合运算符对我们来说并不陌生,在21c之前,我们已经使用了如下这些集合运算符。
UNION
UNION ALL
INTERSECT
MINUS
在21c中,新添加了如下三个集合运算符 EXCEPT、EXCEPT ALL和INTERSECT ALL。接下来我们就通过实验的方式为您介绍。
在这个实验中,我们将使用到两张表,inventories和order_items分别表示库存的商品和客户订购商品,首先让我们查询这两张表中的记录数。
SQL> SELECT count(distinct product_id) FROM inventories;COUNT(DISTINCTPRODUCT_ID)-------------------------208SQL> SELECT count(distinct product_id) FROM order_items;COUNT(DISTINCTPRODUCT_ID)-------------------------185
EXCEPT运算符
我们现在想知道,有多少商品是在库存中,但是从来没有被过买过。我们可以通过except运算符来计算。
SELECT count(*) FROM(SELECT product_id FROM inventoriesEXCEPTSELECT product_id FROM order_items);COUNT(*)----------84
接下来,我们想了解有多少商品被订购过,但是现在已经与在库存当中。
SQL> SELECT count(*) FROM(SELECT product_id FROM order_itemsEXCEPTSELECT product_id FROM inventories);COUNT(*)----------61
EXCEPT ALL运算符
还是上面的两个语句,我们将EXCEPT换成EXCEPT ALL,看看会有什么效果?
SQL> SELECT count(*) FROM(SELECT product_id FROM inventoriesEXCEPT ALLSELECT product_id FROM order_items);COUNT(*)----------826SQL> SELECT count(*) FROM(SELECT product_id FROM order_itemsEXCEPT ALLSELECT product_id FROM inventories);COUNT(*)----------379
加上ALL之后,如果有重复的记录,也不会合并成一条记录进行计算,所以在上面结果中,两条语句得到的结果数都要比之前EXCEPT要多。
INTERSECT与INTERSECT ALL
接下来,我们通过具体SQL语句来比较INTERSECT和INTERSECT ALL的区别,是否使用ALL,结果与之前的EXCEPT一样,如果使用ALL,多条重复的记录,不会当做一条记录进行计算,其实这与我们所熟识的UNION和UNION ALL一样。
SQL> SELECT count(*) FROM(SELECT product_id FROM inventoriesINTERSECTSELECT product_id FROM order_items);COUNT(*)----------124SQL> SELECT count(*) FROM(SELECT product_id FROM order_itemsINTERSECTSELECT product_id FROM inventories);COUNT(*)----------124SQL> SELECT count(*) FROM(SELECT product_id FROM order_itemsINTERSECT ALLSELECT product_id FROM inventories);COUNT(*)----------286
实验2:在初始化参数中使用表达式
关于初始化参数,我想大家都不陌生,但有时候我们在设置这些参数的时候,难免出错,比如SGA_MAX_SIZE为10GB,而将SGA_TARGET设置为16GB,然后重启数据库,发现数据库无法启动。上过OU课程的朋友应该记得,我们当时为大家介绍了去哪里找到上一次正确的配置文件,以及如何通过导入导出的方式解决错误初始化参数配置导致数据库无法启动的问题。在21c当中,我们可以在初始化参数当中使用表达式,比如将SGA_TARGET设置为SGA_MAX_SIZE的80%,我们就可以通过如下方式完成:
SQL> ALTER SYSTEM SET sga_target = 'sga_max_size*80/100';System altered.SQL> SHOW PARAMETER sgaNAME TYPE VALUE------------------------------------ ----------- ------------------------------allow_group_access_to_sga boolean FALSElock_sga boolean FALSEpre_page_sga boolean TRUEsga_max_size big integer 1360Msga_min_size big integer 0sga_target big integer 1088M
同理,我们可以将job_queue_processes设定为 processes的10%,如下所示:
SQL> ALTER SYSTEM SET job_queue_processes='processes*10/100' SCOPE=BOTH;System altered.SQL> SHOW PARAMETER processesNAME TYPE VALUE------------------------------------ ----------- ------------------------------aq_tm_processes integer 1db_writer_processes integer 1gcs_server_processes integer 0global_txn_processes integer 1job_queue_processes integer 64log_archive_max_processes integer 4processes integer 640
在这是初始化参数时,不只可以使用上面的算术式,也可以使用函数,比如下面语句中的min,用来取得两个参数中较小的那个。
SQL> ALTER SYSTEM SET AQ_TM_PROCESSES = 'MIN(40, PROCESSES * .1)' SCOPE=BOTH;System altered.SQL> SHOW PARAMETER processesNAME TYPE VALUE------------------------------------ ----------- ------------------------------aq_tm_processes integer 40db_writer_processes integer 1gcs_server_processes integer 0global_txn_processes integer 1job_queue_processes integer 64log_archive_max_processes integer 4processes integer 640
如果我们对某个参数进行了修改,比如上面的processes,那么和它相关的参数也将自动修改。
SQL> ALTER SYSTEM SET PROCESSES = 500 SCOPE=SPFILE;SQL> shutdown immediateSQL> startupSQL> SHOW PARAMETER processesNAME TYPE VALUE------------------------------------ ----------- ------------------------------aq_tm_processes integer 40db_writer_processes integer 1gcs_server_processes integer 0global_txn_processes integer 1job_queue_processes integer 50log_archive_max_processes integer 4processes integer 500
实验3:使用 SQM 标量和table表达式
SQL 宏是一种直接在 SQL 中封装复杂处理逻辑的新方法。SQL 宏允许开发人员将复杂的处理封装在称为“宏”的新结构中,然后可以在 SQL 语句中使用。Oracle Database 21c提供两种类型的 SQL 宏:SCALAR 和 TABLE。SCALAR 表达式可用于 SELECT 列表、WHERE/HAVING、GROUP BY/ORDER BY 子句。TABLE 表达式可用在 FROM 子句中。
SQL 宏与普通 PL/SQL 函数相比有一个重要优势,因为它们使可重用的 SQL 代码对优化器完全透明,这带来了巨大的好处。它使优化器可以转换原始代码以高效执行效率,因为宏函数内部的底层查询可以合并到外部查询中。这意味着 PL/SQL 和 SQL 之间没有上下文切换,并且宏函数内部的查询,现在在与外部查询相同的快照下执行。所以我们得到了更快的执行效率。
首先我们创建一个scalar的SQM,下面的宏只有一个用于处理字符串的主要语句:
SQL> CREATE OR REPLACE FUNCTION concat_self(str varchar2, cnt pls_integer)RETURN VARCHAR2 SQL_MACRO(SCALAR)IS BEGINRETURN 'rpad(str, cnt * length(str), str)';END;/Function created.SQL>
接下来,我们在SQL语句中我们刚刚创建的宏,我们将看到,Last_Name被输出两遍。
SQL> SELECT last_name, concat_self(last_name,2) FROM hr.employees;LAST_NAME CONCAT_SELF(LAST_NAME,2)------------------------- ----------------------------------------Abel AbelAbelAnde AndeAndeAtkinson AtkinsonAtkinsonAustin AustinAustinBaer BaerBaerBaida BaidaBaidaBanda BandaBandaBates BatesBatesBell BellBellBernstein BernsteinBernsteinBissot BissotBissot
如果您愿意,您可以在查询中,修改宏中的参数,比如将它修改为3:
SQL> SELECT last_name, concat_self(last_name,3) FROM hr.employees;LAST_NAME CONCAT_SELF(LAST_NAME,3)------------------------- ----------------------------------------Abel AbelAbelAbelAnde AndeAndeAndeAtkinson AtkinsonAtkinsonAtkinsonAustin AustinAustinAustinBaer BaerBaerBaerBaida BaidaBaidaBaidaBanda BandaBandaBandaBates BatesBatesBatesBell BellBellBellBernstein BernsteinBernsteinBernsteinBissot BissotBissotBissot
接下来,我们将使用TABLE表达式宏,来实现多态视图。首先我们创建一个普通的视图:
SQL> CREATE VIEW v_budgetAS SELECT department_id, sum(salary) v_budgetFROM hr.employeesGROUP BY department_id;View created.SQL> SELECT * FROM v_budget WHERE department_id IN (10,50);DEPARTMENT_ID V_BUDGET------------- ----------50 15640010 4400
接下来,我们可以使用宏来实现相同的效果:
SQL> CREATE OR REPLACE FUNCTION budgetreturn varchar2 SQL_MACROISBEGINRETURN q'( select department_id, sum(salary) budgetfrom hr.employeesgroup by department_id )';END;/Function created.SQL> SELECT * FROM budget() WHERE department_id IN (10,50);DEPARTMENT_ID BUDGET------------- ----------50 15640010 4400
如果觉得上面的宏太简单,那么我们可以创建一个稍微复杂的宏,下面的宏,在使用时需要给出参数,因为宏内部的逻辑十分简单,所以在这里不再赘述,请注意宏中的“job_id = budget_per_job.job_id”:
SQL> CREATE OR REPLACE FUNCTION budget_per_job(job_id varchar2)return varchar2 SQL_MACROISBEGINRETURN q'( select department_id, sum(salary) budgetfrom hr.employeeswhere job_id = budget_per_job.job_idgroup by department_id )';END;/Function created.SQL> SELECT * FROM budget_per_job('SH_CLERK') WHERE department_id = 50;DEPARTMENT_ID BUDGET------------- ----------50 64300
为了弄清宏是如何运行的,我们可以重写上面的宏,然后通过DBMS_OUTPUT打印出具体执行的查询语句,通过观察数据结果,您就明白为什么使用宏,可以提高查询效率了。这次我们将上面宏中的参数由SH_CLERK修改为ST_CLERK。
SQL> CREATE OR REPLACE function budget_per_job(job_id varchar2)return varchar2 SQL_MACROisstmt varchar(2000) := q'(select department_id, sum(salary) budgetfrom hr.employeeswhere job_id = budget_per_job.job_idgroup by department_id )';begindbms_output.put_line('----------------------------------------------');dbms_output.put_line('SQM Text: ' );dbms_output.put_line('----------------------------------------------');dbms_output.put_line(' ' ||stmt);dbms_output.put_line('----------------------------------------------');return stmt;end;/Function created.SQL> SET serveroutput onSQL> SET LONG 20000SQL> SELECT * FROM budget_per_job('ST_CLERK') WHERE department_id = 50;DEPARTMENT_ID BUDGET------------- ----------50 55700----------------------------------------------SQM Text:----------------------------------------------select department_id, sum(salary) budgetfrom hr.employeeswherejob_id = budget_per_job.job_idgroup by department_id----------------------------------------------
可以使用 USER_PROCEDURES 视图来查询宏,其中 SQL_MACRO列显示了宏的类型。
SQL> SELECT object_name, sql_macro, object_type FROM user_procedures;OBJECT_NAME SQL_MA OBJECT_TYPE------------------------------ ------ -------------SECURE_DML NULL PROCEDUREADD_JOB_HISTORY NULL PROCEDURECONCAT_SELF SCALAR FUNCTIONBUDGET TABLE FUNCTIONBUDGET_PER_JOB TABLE FUNCTIONSECURE_EMPLOYEES TRIGGERUPDATE_JOB_HISTORY TRIGGER
今天的内容就到这里,在下次的21c新特性系列文章中,我们将通过5个实验为您介绍21c中关于大数据和数据仓库的新特性,感谢您的阅读,谢谢。
手把手系列文章:
手把手教你应用Oracle Database 19c新特性-创建和填充外部表到In-Memory
手把手教你:使用Oracle Data Science分析纽约民宿数据
手把手教你:使用Oracle AutoML进行预测(实战教程)
手把手教你OCI机器视觉(1):通过控制台使用OCI Vision
编辑:殷海英






