手把手教你21c新特性(2):大数据与数据仓库新特性

作者: admin 分类: 公众号存档            1 次浏览 发布时间: 2022-05-25 23:59

【转载声明】本文为转载文章,原作者「Dr.Henry」,版权归原作者所有。 原文链接:https://mp.weixin.qq.com/s/Hhp7k17hEPaXYYCHAJrXBg

公众号:杨建荣的学习笔记 · 作者:Dr.Henry · 发布:2022-05-25 23:59:51 · 原文链接

在今天的内容中,将重点介绍Oracle Database 21c 中旨在提高大数据和数据仓库工作负载性能的增强功能。包括在组内记录按位聚合函数,对Window框架子句、GROUPS 和 EXCLUDE 以及table表达式中的 WINDOW 子句进行操作的增强分析函数,通过CHECKSUM 聚合函数来检测表,通过SKEWNESS_POP 和 SKEWNESS_SAMP 聚合函数来衡量数据的不对称性,以及通过 KURTOSIS_POP 和 KURTOSIS_SAMP 聚合函数来衡量数据。

实验1:使用按位聚合函数

按位聚合函数在SQL中支持直接按位类型进行处理。通过消除不必要的数据移动和充分利用其他数据库功能(如并行处理),这些新函数可以提高整体查询性能。

在这个实验中,将为您介绍如何在组内记录的位级上使用新的BIT_AND_AGG、BIT_OR_AGG和BIT_XOR_AGG按位聚合函数。BIT_AND_AGG、BIT_OR_AGG和BIT_XOR_AGG分别返回按位和、按位或和按位异或的操作结果。这些聚合可以对单个数字列或表达式执行运算。按位聚合操作的返回类型总是一个数字。

按位AND函数

按位AND运算是一种二进制运算,它取两个相等长度的二进制数,并对每一对“对应的位”进行逻辑与(AND)运算。如果比较位置的两个值都是1,则二进制表示结果中的值是1,否则结果为0。对两个数字应用BIT_AND_AGG函数。下面示例中,数值2对应二进制10,数值3对应二进制11,于是对于2和3使用BIT_AND_AGG,得到的二进制结果为10,对应十进制数2,所以下面的结果为2。

SQL> WITH x AS (SELECT 2 c1 FROM dual UNION ALL SELECT 3 FROM dual)SELECT BIT_AND_AGG(c1) FROM x;  
BIT_AND_AGG(C1)---------------              2

按位OR函数

如果您已经掌握了上面的按位AND函数,那么按位OR函数也很容易理解,如果一对“位值”中有一个值为1,结果就为1,否则为0。因此对于下面的例子,2的二进制值为10,3的二进制值为11,于是按位OR函数处理后的结果为二进制值11,对应十进制值3。如果您对上面的说明不能很好地理解,建议您拿出纸笔,在纸上写出上面的算式,就会一目了然。

SQL> WITH x AS (SELECT 2 c1 FROM dual UNION ALL SELECT 3 FROM dual)SELECT BIT_OR_AGG(c1) FROM x;  
BIT_OR_AGG(C1)--------------             3

按位XOR函数

这个函数可以用来检查对应“位”上的值是否相同,如果相同返回0,否则返回1。对于十进制的2和3,使用XOR函数,首先将它们转换为二进制的10和11,因为第一位都是1,所以XOR函数计算的结果为0,第二位一个是0一个是1,所以XOR得到的计算结果为1,最终结果为二进制01,对应十进制1。

SQL> WITH x AS (SELECT 2 c1 FROM dual UNION ALL SELECT 3 FROM dual)SELECT BIT_XOR_AGG(c1) FROM x;  
BIT_XOR_AGG(C1)---------------              1

实验2:使用增强分析函数

在这个实验中,将为您展示如何从window框架子句GROUPS和EXCLUDE的新选项以及table表达式中的window子句中获得增强功能。

Window框架的GROUPS子句

关于Window框架是一个很有趣的功能,就好像在打印记录的时候,有一个隐形的窗口,通过滑动窗口,仅对窗口内的数据进行计算与处理。让我们首先看看表中的原始数据。

SQL> select * from trades order by tday;
ACNO        TID TDAY      TTYP     AMOUNT TICK---------- ---------- --------- ---- ---------- ----123          1 07-APR-22 buy        1000 CSCO123          1 07-APR-22 buy         400 JNPR123          3 09-APR-22 buy        2000 SYMC123          4 09-APR-22 buy        1200 CSCO123          5 09-APR-22 buy         500 JNPR123          6 11-APR-22 buy         200 CSCO123          7 11-APR-22 buy         100 CSCO123          9 12-APR-22 buy         400 JNPR123         10 12-APR-22 buy         200 GOOG123         11 12-APR-22 buy        1000 JNPR123         12 12-APR-22 buy        4000 JNPR123         13 15-APR-22 buy        2000 HPQ
12 rows selected.

现在我们想做这样的统计,按照ACNO分组(当前数据表中,只有ACNO=123的一组信息,您可以添加ACNO为其他的记录来观察语句生成的不同结果),然后按照TDAY升序排序,统计以当前记录位置算起,前三行及当前行的AMOUNT总和。

SQL> SELECT acno, tday, SUM (amount) OVER W  FROM   tradesWINDOW W AS (PARTITION BY acno ORDER BY tday ROWS BETWEEN 3 PRECEDING AND CURRENT ROW);   
ACNO TDAY      SUM(AMOUNT)OVERW---------- --------- ----------------123 07-APR-22             1000123 07-APR-22             1400123 09-APR-22             3400123 09-APR-22             4600123 09-APR-22             4100123 11-APR-22             3900123 11-APR-22             2000123 12-APR-22             1200123 12-APR-22              900123 12-APR-22             1700123 12-APR-22             5600123 15-APR-22             7200
12 rows selected.

现在我们解释一下上面的结果,让我们看看第四行第三列的值为4600,它是原始表有前三行:1000、400、2000和当前行1200相加得到的。让我们看看第五行的4100,原始表的第2、3、4、5行:400、2000、1200和500相加得到的。

上面的结果中TDAY列的值相同,但是对应后面的SUM值不同,这让人看起来很困扰,如果我们想按照日期进行分组,每个日期都显示相同的值,而之前的window逻辑不变。我们可以通过如下代码实现。需要注意的是,下面代码中的GROUPS BETWEEN 3 PRECEDING AND CURRENT ROW是按组算的,并不是之前按单行记录来计算的。仔细想想,下面的5100和5400是怎么来的。

SQL> SELECT acno, tday, SUM(amount) OVER W, COUNT(ticker) OVER WFROM   tradesWINDOW W AS (PARTITION BY acno ORDER BY tday GROUPS BETWEEN 3 PRECEDING AND CURRENT ROW);  
ACNO TDAY      SUM(AMOUNT)OVERW COUNT(TICKER)OVERW---------- --------- ---------------- ------------------123 07-APR-22             1400                  2123 07-APR-22             1400                  2123 09-APR-22             5100                  5123 09-APR-22             5100                  5123 09-APR-22             5100                  5123 11-APR-22             5400                  7123 11-APR-22             5400                  7123 12-APR-22            11000                 11123 12-APR-22            11000                 11123 12-APR-22            11000                 11123 12-APR-22            11000                 11123 15-APR-22            11600                 10
12 rows selected.

Window框架的EXCLUDE子句

从字面意思上看EXCLUDE是排除的意思,但我们这里要介绍的是如何将EXCLUDE与window框架一起使用。首先让我们看看用来做测试的简单表。

SQL> SELECT * FROM t;
         V----------         1         1         3         5         5         5         6
7 rows selected.

接下来我们就观察一下EXCLUDE与window框架的各种组合:

SQL> SELECT v,sum(v) OVER (o ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS current_row,sum(v) OVER (o ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS the_group,sum(v) OVER (o ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS ties,sum(v) OVER (o ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE NO OTHERS) AS no_othersFROM tWINDOW o AS (ORDER BY v);  
V CURRENT_ROW  THE_GROUP       TIES  NO_OTHERS---------- ----------- ---------- ---------- ----------1           1                     1          21           4          3          4          53           6          6          9          95           8          3          8         135          10                     5         155          11          6         11         166           5          5         11         11
7 rows selected.

上面结果中的第一列v是原始值,current_row是当前行的前一行加上当前行的后一行,但是不包含当前行,我们看看上面结果的第二行current_row对应的4,它是原始记录的第一行(值为1)加上第三行(值为3),但是不包含当前行(值为1)的值,4=1+3。the_group表示:当前行的前一行加上后一行,但是不包含当前组中的数据。我们看看上面结果第二行the_group列的那个3,在原始表中,它的前一行是1,后一行是3,但是前一行的1和当前行的值相同,属于同一组,所以这个前一行的1,不加入。最后得到3。EXCLUDE NO OTHERS是默认值,表示不删除。EXCLUDE TIES很有趣,以上面的情况为例,前一行加上后一行,如果有数据与当前行是同组的,那么将删除除了当前行以外的所有同组数据。比如结果倒数第三行TIES对应的那个5,对于这一行来说,它的前一行是5,后一行也是5,当前行还是5,按照规则,它们是一组的,那么删除除了当前以外所有的数据,那么只剩下当前行的值,为5。如果您觉得还是不太理解,我将前后行的参数由1改为2,您自己好好思考一下。比如想想,下面倒数第三行TIES列的那个14是怎么来的。

SQL> SELECT v,sum(v) OVER (o ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE CURRENT ROW) AS current_row,sum(v) OVER (o ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE GROUP) AS the_group,sum(v) OVER (o ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE TIES) AS ties,sum(v) OVER (o ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE NO OTHERS) AS no_othersFROM tWINDOW o AS (ORDER BY v);  
V CURRENT_ROW  THE_GROUP       TIES  NO_OTHERS---------- ----------- ---------- ---------- ----------1           4          3          4          51           9          8          9         103          12         12         15         155          14          4          9         195          19          9         14         245          16          6         11         216          10         10         16         16
7 rows selected.

上面我们使用的是ROWS BETWEEN,接下来我们看看稍微复杂的RANGE BETWEEN,这个RANGE解释起来有些麻烦。我们以下面结果中第四行CURRENT ROW列的那个16为例,EXCLUDE CURRENT ROW是不包含当前行的意思,与之前一样。而这个RANGE  1 PRECEDING1 和 1 FOLLOWING表示大于当前这行的V值 1,及小于这个当前V值1的范围,这样的V值是5,那么这个范围就是4到6。我们看看这行上一行(也可以认为是当前范围的上一个范围)的值是3,不在4到6的范围内,所以不考虑加总,当前行及下两行都是5,这三行都是5算作当前的range,再往下一range是6,满足4到6的范围,所以要加总进来,于是就有了5+5+6=16。我们再看看倒数第二行的那个16,倒数第二行的V值是5,它的上一个range对应的值是单个的3,不在4到6之间,所以不计入加总,它的下一个range是6,正好下一行的值是6,算入加总,于是就有了倒数第一行的这个6,加上当前range 3个5,但不算当前行的5,得到5+5+6=16,您自己尝试计算,倒数第一行在CURRENT_ROW的那个15是怎么计算出来的。

SELECT v,sum(v) OVER (o RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS current_row,sum(v) OVER (o RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS the_group,                    sum(v) OVER (o RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS ties,sum(v) OVER (o RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE NO OTHERS) AS no_othersFROM tWINDOW o AS (ORDER BY v); 
         V CURRENT_ROW  THE_GROUP       TIES  NO_OTHERS---------- ----------- ---------- ---------- ----------         1           1                     1          2         1           1                     1          2         3                                 3          3         5          16          6         11         21         5          16          6         11         21         5          16          6         11         21         6          15         15         21         21
7 rows selected.

接下来我们可以将 GROUPS和EXCLUDE一起使用,我在这里不做解释,看看您是否可以自己找到它们的计算方法,小小的提示,之前的ROWS是简单的按行计算,RANGE是上下的范围,而GROUPS是按照什么呢?比如结果中第四行第二列的那个19,它的上一组V值是3,下一组V值是6,当前组有3个5,计算的时候不算当前行的5,19=3+5+5+6。您自己算算,最后一行第二例的那个15是怎么算出来。

SQL> SELECT v,sum(v) OVER (o GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS current_row,sum(v) OVER (o GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS the_group,sum(v) OVER (o GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS ties,sum(v) OVER (o GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE NO OTHERS) AS no_othersFROM tWINDOW o AS (ORDER BY v); 
V CURRENT_ROW  THE_GROUP       TIES  NO_OTHERS---------- ----------- ---------- ---------- ----------1           4          3          4          51           4          3          4          53          17         17         20         205          19          9         14         245          19          9         14         245          19          9         14         246          15         15         21         21
7 rows selected.

实验3:使用CHECKSUM函数检测数据篡改

在这个实验中,将为您介绍如何使用CHECKSUM聚合函数来检测表中的更改。该函数可以应用于列、常量、绑定变量或涉及它们的表达式。支持除 ADT 和 JSON 之外的所有数据类型。表中行的顺序不影响检测结果。

关于数据的审计和防篡改,我们可以通过数据库选件或者AVDF等软件来实现。但如果您想通过在数据库内部查询的方式知道数据是否被篡改,就可以使用CHECKSUM函数。

让我们首先看看原始数据的CHECKSUM值。

SQL> SELECT CHECKSUM(amount_sold) FROM sh.sales sJOIN sh.times t ON (s.time_id = t.time_id)WHERE fiscal_month_number = 12 AND fiscal_year = 1998;  
CHECKSUM(AMOUNT_SOLD)---------------------793409

通过查询,我们得到满足查询条件的amount_sold字段值的CHECKSUM为793409。接下来,让我们在另外一个Terminal对这张表中的数据做更新,然后提交更新。之后再次执行上面的CHECKSUM检查。

Connected to:Oracle Database 21c EE High Perf Release 21.0.0.0.0 - ProductionVersion 21.5.0.0.0

525 rows updated.

Commit complete.SQL> SELECT CHECKSUM(amount_sold) FROM sh.sales sJOIN sh.times t ON (s.time_id = t.time_id)WHERE fiscal_month_number = 12 AND fiscal_year = 1998;  
CHECKSUM(AMOUNT_SOLD)---------------------               835564

通过观察,相同的SQL查询,得到的CHECKSUM值与之前不同,说明数据已经被篡改。

那么接下来,我们看看,如果在一个会话中,我们修改数据之后,回滚,那么CHECKSUM值是否发生变化。

SQL> UPDATE sh.sales SET amount_sold = amount_sold*2 WHERE time_id='30-NOV-98';
525 rows updated.
SQL> SELECT CHECKSUM(amount_sold) FROM sh.sales sJOIN sh.times t ON (s.time_id = t.time_id)WHERE fiscal_month_number = 12 AND fiscal_year = 1998;  
CHECKSUM(AMOUNT_SOLD)---------------------366195
SQL> rollback;
Rollback complete.
SQL> SELECT CHECKSUM(amount_sold) FROM sh.sales sJOIN sh.times t ON (s.time_id = t.time_id)WHERE fiscal_month_number = 12 AND fiscal_year = 1998;  
CHECKSUM(AMOUNT_SOLD)---------------------835564

通过观察,我们发现如果我们对数据进行修改,不提交,然后查询CHECKSUM的值,这个值会发生变化,但是如果我们rollback,那么CHECKSUM的值又恢复成原来的值。

接下来让我们做另外一个查询,这是一个特殊情况。这个查询的结果只有一条记录。也就是说quantity_sold的唯一值只有一个1。

SQL>  SELECT DISTINCT quantity_sold FROM sh.sales sJOIN sh.times t ON (s.time_id = t.time_id)WHERE fiscal_month_number = 12 AND fiscal_year = 1998;  
QUANTITY_SOLD-------------            1

SQL> SELECT CHECKSUM(quantity_sold) FROM sh.sales sJOIN sh.times t ON (s.time_id = t.time_id)WHERE fiscal_month_number = 12 AND fiscal_year = 1998;  
CHECKSUM(QUANTITY_SOLD)-----------------------                      0                      

在上面的查询中,我们得到的校验值为0,而0这个值,让我们无法判断数据是否发生篡改。

那么让我们在查询中添加上distinct,看看结果如何。

SELECT CHECKSUM(DISTINCT quantity_sold) FROM sh.sales sJOIN sh.times t ON (s.time_id = t.time_id)WHERE fiscal_month_number = 12 AND fiscal_year = 1998;  
CHECKSUM(DISTINCTQUANTITY_SOLD)-------------------------------863352

这回的CHECKSUM值不再是0,可以进行区分了。

在下面的代码中,我们将quantity_sold的值由1修改为2,并且提交我们的修改。然后查询CHECKSUM值。

SQL> UPDATE sh.sales SET quantity_sold = 2;
918843 rows updated.
SQL> COMMIT;
Commit complete.
SQL> SELECT CHECKSUM(quantity_sold) FROM sh.sales sJOIN sh.times t ON (s.time_id = t.time_id)WHERE fiscal_month_number = 12 AND fiscal_year = 1998;  
CHECKSUM(QUANTITY_SOLD)-----------------------0
SQL> SELECT CHECKSUM(DISTINCT quantity_sold) FROM sh.sales sJOIN sh.times t ON (s.time_id = t.time_id)WHERE fiscal_month_number = 12 AND fiscal_year = 1998; 
CHECKSUM(DISTINCTQUANTITY_SOLD)-------------------------------                          65515

通过观察发现,如果我们还是对quantity_sold进行CHECKSUM查询,我们依旧得到0,即便我们已经修改并提交,这并不能通过CHECKSUM函数看出数据是否被修改。但是如果我们加上distinct,我们发现CHECKSUM的值与之前不同,说明数据已经被篡改。所以如果当您看到CHECKSUM值为0时,您应该使用其他手段对数据进行校验,比如我们上面用的distinct。

接下来,让我们看看,如果在更新的过程中,我们将数据更新成NULL,通过CHECKSUM是否可以发现数据被篡改。这次,我们对字符串型的数据进行修改,我们将客户的邮件地址改成NULL,然后检查修改前后的CHECKSUM值。

首先,我们看看满足特定条件的记录有多少条,稍后我们将这些记录的email都改成NULL。

SQL> select count(cust_email) FROM sh.customers WHERE cust_income_level ='L: 300,000 and above';
COUNT(CUST_EMAIL)-----------------             1684

我们发现有1684条记录满足我们的条件,接下来我们先查询一下CHECKSUM值。

SQL> SELECT CHECKSUM(cust_email) FROM sh.customers;
CHECKSUM(CUST_EMAIL)--------------------              107013
SQL> SELECT CHECKSUM(DISTINCT cust_email) FROM sh.customers;
CHECKSUM(DISTINCTCUST_EMAIL)----------------------------                      227092

接下来我们将满足我们特定条件的1684条记录的email都修改为NULL并提交。然后检查是不是修改成功。通过最后一行的查询,我们发现,没有记录打印出来,其实系统打印的是NULL。

SQL> UPDATE sh.customers SET cust_email = NULL         WHERE cust_income_level ='L: 300,000 and above';  2
1684 rows updated.
SQL> commit;
Commit complete.
SQL> SELECT DISTINCT cust_email FROM sh.customers WHERE cust_income_level ='L: 300,000 and above';
CUST_EMAIL--------------------------------------------------

现在让我们看看CHECKSUM值。

SQL> SELECT CHECKSUM(cust_email) FROM sh.customers;
CHECKSUM(CUST_EMAIL)--------------------              577487
SQL>  SELECT CHECKSUM(DISTINCT cust_email) FROM sh.customers;
CHECKSUM(DISTINCTCUST_EMAIL)----------------------------                      141231

通过观察,两个CHECKSUM值都发生了变化,说明数据已经被篡改。

实验4:使用SKEWNESS函数测量数据中的不对称性

如果您和我一样,经常要处理数据科学相关的工作,或者您经常完成统计学相关的工作,那么您一定不会对数据的偏度陌生。虽然我们可以通过均值和方差了解数据集的数据分布情况,当如果要详细了解数据的分布,我们经常使用直方图和手动评估曲线。在下面的实验中,将为您介绍如何通过Oracle的偏度函数了解数据的分布。我们将为您介绍SKEWNESS_POP 和 SKEWNESS_SAMP 聚合函数,第一个函数用来计算总体偏度,第二个函数用来计算样本偏度。

让我们首先查看一下我们的数据集,其中HOUSE列表示房屋的类型。

SQL> SELECT * FROM houses;
HOUSE PRICE_BIG_CITY PRICE_SMALL_CITY PRICE_DAT---------- -------------- ---------------- ---------1         100000            10000 09-APR-221         200000            15000 10-APR-221         300000            25000 10-APR-221         400000            28000 11-APR-221         500000            30000 12-APR-221         600000            32000 12-APR-221         700000            35000 13-APR-221         800000            38000 13-APR-221         900000            40000 14-APR-222        2000000          1000000 15-APR-222         200000            20000 09-APR-222         400000            35000 10-APR-222         600000            55000 10-APR-222         800000            48000 11-APR-223         400000            40000 12-APR-223         500000            42000 12-APR-223         600000            45000 13-APR-223         700000            48000 13-APR-223         800000            49000 14-APR-22
19 rows selected.

接下来我们按房屋类型进行分组,然后看看每组中的房屋数量。

SQL> SELECT house, count(house) FROM houses GROUP BY house ORDER BY 1;
HOUSE COUNT(HOUSE)---------- ------------1            92            5         3            5

接下来让我们看看每种房型的总体偏度和样本偏度。

SQL> SELECT house, SKEWNESS_POP(price_big_city), SKEWNESS_POP(price_small_city) FROM housesGROUP BY house;  
HOUSE SKEWNESS_POP(PRICE_BIG_CITY) SKEWNESS_POP(PRICE_SMALL_CITY)---------- ---------------------------- ------------------------------1                            0                     -.668640122                   1.13841996                     1.49637083         3                            0                     -.12735442SQL> SELECT house, SKEWNESS_SAMP(price_big_city), SKEWNESS_SAMP(price_small_city) FROM housesGROUP BY house;  
HOUSE SKEWNESS_SAMP(PRICE_BIG_CITY) SKEWNESS_SAMP(PRICE_SMALL_CITY)---------- ----------------------------- -------------------------------1                             0                      -.810514222                    1.69705627                      2.230657933                             0                      -.18984876       

在结果中,我们看到偏度为0的记录,表示它符合正态分布。

接下来,我们向表中添加更多的重复记录,然后我们观察它的总体偏度和样本偏度,看看这些值是否有变化。

SQL> INSERT INTO houses SELECT * FROM houses;
19 rows created.
SQL> /
38 rows created.
SQL> /
76 rows created.
SQL> /
152 rows created.
SQL> commit;
Commit complete.SQL> SELECT house, SKEWNESS_POP(price_big_city), SKEWNESS_POP(price_small_city) FROM housesGROUP BY house ORDER BY 1;  
     HOUSE SKEWNESS_POP(PRICE_BIG_CITY) SKEWNESS_POP(PRICE_SMALL_CITY)---------- ---------------------------- ------------------------------         1                            0                     -.66864012         2                   1.13841996                     1.49637083         3                            0                     -.12735442
SQL> SELECT house, SKEWNESS_SAMP(price_big_city), SKEWNESS_SAMP(price_small_city) FROM housesGROUP BY house ORDER BY 1;  
     HOUSE SKEWNESS_SAMP(PRICE_BIG_CITY) SKEWNESS_SAMP(PRICE_SMALL_CITY)---------- ----------------------------- -------------------------------         1                             0                      -.67569912         2                     1.1602897                      1.52511703         3                             0                      -.12980098

因为我们插入的是重复的值,所以总体的偏度没有变化,而样本偏度发生了变化,由原来的1.697变为1.160。

接下来让我们看看PRICE_BIG_CITY 和 PRICE_SMALL_CITY 列中唯一值的偏度。

SQL> SELECT house,SKEWNESS_POP(DISTINCT price_big_city) pop_big_city,SKEWNESS_SAMP(DISTINCT price_big_city) samp_big_city,SKEWNESS_POP(DISTINCT price_small_city) pop_small_city,SKEWNESS_SAMP(DISTINCT price_small_city) samp_small_cityFROM housesGROUP BY house; 
HOUSE POP_BIG_CITY SAMP_BIG_CITY POP_SMALL_CITY SAMP_SMALL_CITY---------- ------------ ------------- -------------- ---------------         1            0             0     -.66864012      -.81051422         2   1.13841996    1.69705627     1.49637083      2.23065793         3            0             0     -.12735442      -.18984876

让我们与不适用distinct的查询结果进行比较,看看偏度是否有很大不同?

SQL> SELECT house,SKEWNESS_POP(price_big_city) pop_big_city,SKEWNESS_SAMP(price_big_city) samp_big_city,SKEWNESS_POP(price_small_city) pop_small_city,SKEWNESS_SAMP(price_small_city) samp_small_cityFROM housesGROUP BY house; 
HOUSE POP_BIG_CITY SAMP_BIG_CITY POP_SMALL_CITY SAMP_SMALL_CITY---------- ------------ ------------- -------------- ---------------         1            0             0     -.66864012      -.67569912         2   1.13841996     1.1602897     1.49637083      1.52511703         3            0             0     -.12735442      -.12980098

大家通过观察可以发现,总体偏度不变,但是样本偏度发生了比较大的改变,关于其中的原理,建议大家去网路上检索关于偏度的统计学解释。

我们这里的总体偏度不变,是因为刚才我们插入的数据都是来自于表本身的。下面让我们插入一下不同的数据,然后再次观察总体偏度和样本偏度的变化。


SQL> INSERT INTO houses (house, price_big_city, price_small_city)SELECT house, price_big_city*0.5, price_small_city*0.1FROM houses WHERE house=1; 
144 rows created.
SQL> /
288 rows created.
SQL> /
576 rows created.
SQL> /
1152 rows created.
SQL> /
2304 rows created.

首先我们看看数据的分布情况。我们发现房屋类型1的数量明显比其他两种房屋类型要多很多。

SQL> SELECT house, count(house) FROM houses GROUP BY house ORDER BY 1;
HOUSE COUNT(HOUSE)---------- ------------1         46082           803           80

接下来让我们看看数据的总体偏度与样本偏度。通过观察,房屋类型1的总体偏度也不再符合正态分布。

SQL> SELECT house,SKEWNESS_POP(price_big_city) pop_big_city,SKEWNESS_SAMP(price_big_city) samp_big_city,SKEWNESS_POP(price_small_city) pop_small_city,SKEWNESS_SAMP(price_small_city) samp_small_cityFROM housesGROUP BY house;  
HOUSE POP_BIG_CITY SAMP_BIG_CITY POP_SMALL_CITY SAMP_SMALL_CITY---------- ------------ ------------- -------------- ---------------         1   2.57050631    2.57134341      5.7418481      5.74371797         2   1.13841996     1.1602897     1.49637083      1.52511703         3            0             0     -.12735442      -.12980098

实验5:使用KURTOSIS函数测量数据的峰度

与刚才介绍的数据偏度一样,峰度也是用来描述数据分布的指标。关于峰度的具体介绍,在网路上有很多非常详尽的介绍材料。

首先依旧是让我们看看数据的基本情况。

SQL>  SELECT * FROM houses;
HOUSE PRICE_BIG_CITY PRICE_SMALL_CITY PRICE_DAT---------- -------------- ---------------- ---------1         100000            10000 09-APR-221         200000            15000 10-APR-221         300000            25000 10-APR-221         400000            28000 11-APR-221         500000            30000 12-APR-221         600000            32000 12-APR-221         700000            35000 13-APR-221         800000            38000 13-APR-221         900000            40000 14-APR-222        2000000          1000000 15-APR-222         200000            20000 09-APR-222         400000            35000 10-APR-222         600000            55000 10-APR-222         800000            48000 11-APR-223         400000            40000 12-APR-223         500000            42000 12-APR-223         600000            45000 13-APR-223         700000            48000 13-APR-223         800000            49000 14-APR-22
19 rows selected.

接下来让我们通过KURTOSIS_POP检查总体峰度,并使用KURTOSIS_SAMP检查样本峰度。

SQL> SELECT house, kurtosis_pop(price_big_city), kurtosis_pop(price_small_city) FROM houses     GROUP BY house;  
HOUSE KURTOSIS_POP(PRICE_BIG_CITY) KURTOSIS_POP(PRICE_SMALL_CITY)---------- ---------------------------- ------------------------------         1                        -1.23                      -.7058169         2                        -.212                     .245200191         3                         -1.3                     -1.5417881
SQL> SELECT house, kurtosis_samp(price_big_city), kurtosis_samp(price_small_city) FROM houses     GROUP BY house;  
HOUSE KURTOSIS_SAMP(PRICE_BIG_CITY) KURTOSIS_SAMP(PRICE_SMALL_CITY)---------- ----------------------------- -------------------------------         1                          -1.2                        -.201556         2                         3.152                      4.98080076         3                          -1.2                      -2.1671526

按照实验4的做法,我们先向数据集中加入重复的记录,然后看看总体峰度和样本峰度的变化。


SQL> INSERT INTO houses SELECT * FROM houses;
19 rows created.
SQL> /
38 rows created.
SQL> /
76 rows created.
SQL> /
152 rows created.SQL> SELECT house, KURTOSIS_POP(price_big_city), KURTOSIS_POP(price_small_city) FROM houses     GROUP BY house ORDER BY 1;  2
     HOUSE KURTOSIS_POP(PRICE_BIG_CITY) KURTOSIS_POP(PRICE_SMALL_CITY)---------- ---------------------------- ------------------------------         1                        -1.23                      -.7058169         2                        -.212                     .245200191         3                         -1.3                     -1.5417881
SQL> SELECT house, KURTOSIS_SAMP(price_big_city), KURTOSIS_SAMP(price_small_city) FROM houses     GROUP BY house ORDER BY 1;  2
     HOUSE KURTOSIS_SAMP(PRICE_BIG_CITY) KURTOSIS_SAMP(PRICE_SMALL_CITY)---------- ----------------------------- -------------------------------         1                    -1.2309485                      -.68809876         2                    -.14695105                      .340165838         3                    -1.3061439                      -1.5637533

通过观察,我们发现总体峰度没有发生变化,而样本峰度发生了变化,关于背后的统计学原理,大家可以去网路检索。

接下来我们观察distinct值的峰度。

SQL> SELECT house,KURTOSIS_POP(DISTINCT price_big_city) pop_big_city,KURTOSIS_SAMP(DISTINCT price_big_city) samp_big_city,KURTOSIS_POP(DISTINCT price_small_city) pop_small_city,KURTOSIS_SAMP(DISTINCT price_small_city) samp_small_cityFROM housesGROUP BY house;  
HOUSE POP_BIG_CITY SAMP_BIG_CITY POP_SMALL_CITY SAMP_SMALL_CITY---------- ------------ ------------- -------------- ---------------         1        -1.23          -1.2      -.7058169        -.201556         2        -.212         3.152     .245200191      4.98080076         3         -1.3          -1.2     -1.5417881      -2.1671526

然后与未使用distinct进行对比。

SQL> SELECT house,                    KURTOSIS_POP(price_big_city) pop_big_city,                    KURTOSIS_SAMP(price_big_city) samp_big_city,                    KURTOSIS_POP(price_small_city) pop_small_city,                    KURTOSIS_SAMP(price_small_city) samp_small_city               FROM houses               GROUP BY house;  
     HOUSE POP_BIG_CITY SAMP_BIG_CITY POP_SMALL_CITY SAMP_SMALL_CITY---------- ------------ ------------- -------------- ---------------         1        -1.23    -1.2309485      -.7058169      -.68809876         2        -.212    -.14695105     .245200191      .340165838         3         -1.3    -1.3061439     -1.5417881      -1.5637533

与上面一样,让我们向数据表中插入新数据,并且这个新数据将引起数据的分布不平衡。


SQL> INSERT INTO houses (house, price_big_city, price_small_city)               SELECT house, price_big_city*0.5, price_small_city*0.1               FROM houses WHERE house=1; 
144 rows created.
SQL> /
288 rows created.
SQL> /
576 rows created.
SQL> /
1152 rows created.
SQL> /
2304 rows created.SQL> SELECT house, count(house) FROM houses GROUP BY house ORDER BY 1;
     HOUSE COUNT(HOUSE)---------- ------------         1         4608         2           80         3           80

重新观察数据的总体峰度和样本峰度。

SQL> SELECT house,                    KURTOSIS_POP(price_big_city) pop_big_city,                    KURTOSIS_SAMP(price_big_city) samp_big_city,                    KURTOSIS_POP(price_small_city) pop_small_city,                    KURTOSIS_SAMP(price_small_city) samp_small_city               FROM houses               GROUP BY house;  
     HOUSE POP_BIG_CITY SAMP_BIG_CITY POP_SMALL_CITY SAMP_SMALL_CITY---------- ------------ ------------- -------------- ---------------         1   9.12746931    9.13868421     33.7452495      33.7831972         2        -.212    -.14695105     .245200191      .340165838         3         -1.3    -1.3061439     -1.5417881      -1.5637533

与上面未插入数据之前的峰度相比,房屋类型1的峰度发生了巨大的变化。

其实上面的峰度和偏度,可以通过Python很容易地实现,但是在同等的硬件环境下,使用数据库查询技术所用的资源要比Python少得多,所以返回的速度也会快很多。

今天的内容就到这里,将在下次的文章中为大家介绍Oracle Database 21c性能与高可用方面的新特性,感谢您的阅读,谢谢。

手把手系列文章:

手把手教你21c新特性(1):软件安装与应用程序开发

手把手教你升级到Database 19c(1)

手把手教你升级到Database 19c(2)

手把手教你升级到Database 19c(3)

手把手教你19c新特性:自动索引

手把手教你19c新特性:实时统计信息收集

手把手教你19c新特性:混合分区表

手把手教你19c新特性:SQL隔离

手把手教你:使用Kettle实现数据同步

手把手教你:使用Kettle实现数据同步(2)

手把手教你应用Oracle Database 19c新特性-创建和填充外部表到In-Memory

手把手教你:搭建Data Science环境

手把手教你:使用Oracle Data Science分析纽约民宿数据

手把手教你:使用Oracle AutoML进行预测(实战教程)

手把手教你OCI机器视觉(1):通过控制台使用OCI Vision

编辑:殷海英

admin

杨建荣,《Oracle DBA工作笔记》《MySQL DBA工作笔记》作者,dbaplus社群发起人之一,腾讯云TVP,现任竞技世界系统部经理,拥有十多年数据库开发和运维经验,目前专注于开源技术、运维自动化和性能调优

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

更多阅读