0. 概述

Oracle先计算SQL_TEXT的md5散列值;取散列值的低64位(bits),每次取5位(最后一次4位),使用Base32将其依次转换成可见字符,就是你最终看到的SQL_ID。原理就是这样。

不过实际转换过程中有一些要注意的事项:

(a) Oracle在计算md5散列时,会在SQL_TEXT末尾加一个不可见字符\0,AWR报表中经常有这样的SQL_TEXT

(b) 注意little-endian的问题

(c) Base32转码的可见字符为0123456789abcdfghjkmnpqrstuvwxyz

(d) 编写程序的时候需要注意大数精度的问题,本文中Perl/PHP程序都使用了数学大数处理函数

1. 详细过程

1.1 示例

我们考虑如下给定SQL:

select sysdate from dual;

在Oracle 10g中执行并查询v$SQL,可以看到这个SQL的SQL_ID是

SQL > select sql_id, hash_value from v$sql2  where sql_text = 'select sysdate from dual';SQL_ID       HASH_VALUE
------------- ----------
7h35uxf5uhmm1 2343063137

1.2 SQL_ID计算的详细过程

1.2.1 散列值的计算

将SQL_TEXT末尾加上一个空字符\0,然后进行md5散列:

use Digest::MD5  qw(md5 md5_hex md5_base64);
$stmt = "select sysdate from dual\0";
$hash = md5 $stmt;

select sysdate from dual\0的MD5散列值为abd4dbb3096b15f1ebba0c78614ea88b,共128位(明明是32位,怎么说128位?),取低64位为:"ebba0c78 614ea88b"。

md5散列的字节码如下(128位):

|10101011|11010100|11011011|10110011|
|00001001|01101011|00010101|11110001||11101011|10111010|00001100|01111000|
|01100001|01001110|10101000|10001011|
1.2.2 取低64位整数

md5散列值的低64位为:

|11101011|10111010|00001100|01111000|
|01100001|01001110|10101000|10001011|

分为两部分,高32位和低32位,分别为:ebba0c78 614ea88b,对应二进制字节流为:|11101011|10111010|00001100|01111000|和|01100001|01001110|10101000|10001011|。可以直接使用unpack函数将散列值解开。这里需要注意,取模运算为整数运算,而我这里的环境是x86_64 little-endian,所以取模运算时对应的整数字节序(人读取的时候):

|01111000|00001100|10111010|11101011|
|10001011|10101000|01001110|01100001|

perl代码:

my($a,$b,$msb,$lsb) = unpack("V*",$hash);
1.2.3 Base32转换为可见字符

Oracle使用了Base32将字节流转换为可见字符。

一个Base32字符对应字节流的5位(bits),这里总计64位,所以是64/5,一共13个字符。其中12个字符为5位,有一个字符总是四位(SQL_ID的第一位)。

我们来看本案例的字节流,每五位转换为一个Base32的编码,取最后5为00001(十进制1),对应Base32编码为1;取倒数第二个五位10011(十进制19),取倒数第三个五位为010011(十进制19)...

Oracle使用的Base32对应编码字符为:

0123456789abcdfghjkmnpqrstuvwxyz

编码和字符对应关系

编码  00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
字符   0  1  2  3  4  5  6  7  8  9  a  b  c  d  f  g
-----------------------------------------------------
编码  16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
字符   h  j  k  m  n  p  q  r  s  t  u  v  w  x  y  z

所以,上面编码1、19、19对应的字符为1、m、m,这也正是SQL_ID对应的最后三位:

7h35uxf5uhmm1

3 代码片段

3P程序代码如下:

3.1 Perl

#!/usr/bin/perl -w
use Digest::MD5  qw(md5 md5_hex md5_base64);
use Math::BigInt;
my $stmt = "select sysdate from dual\0";
my $hash = md5 $stmt;
my($a,$b,$msb,$lsb) = unpack("V*",$hash);
my $sqln = $msb*(2**32)+$lsb;
my $stop = log($sqln) / log(32) + 1;
my $sqlid = '';
my $charbase32 = '0123456789abcdfghjkmnpqrstuvwxyz';
my @chars = split '', $charbase32;
for($i=0; $i < $stop-1; $i++){my $x = Math::BigInt->new($sqln);my $seq = $x->bdiv(32**$i)->bmod(32);$sqlid = $chars[$seq].$sqlid;
}
print "SQL is:\n    $stmt \nSQL_ID is\n    $sqlid\n"; 

3.2 PHP

function stmt_2_sqlid($stmt){$h = md5($stmt."\0",TRUE);$un = unpack("V*",$h);$msb = $un[3] + 0; if($msb < 0) {$msb = pow(2,32) + $msb;}$lsb = $un[4] + 0; if($lsb < 0) {$lsb = pow(2,32) + $lsb;}$sqln = bcadd(bcmul($msb , bcpow(2,32)) , $lsb);$stop = log($sqln) / log(32) + 1;$sqlid = '';$alphabet = '0123456789abcdfghjkmnpqrstuvwxyz';for($i = 0; $i < $stop-1; $i++){$seq = bcmod((bcdiv($sqln,bcpow(32,$i),5)),32);$sqlid = $alphabet[$seq].$sqlid;}return $sqlid;
}
$stmt = 'select sysdate from dual';
echo stmt_2_sqlid($stmt);

3.3 Python

参考:Oracle sql_id and hash value

4. 最后

一个略有趣的事实,SQL_ID的第一位经常会是数字。这是因为是64位(bits),按照5位一个字符划分,最后一个字符总是只有4位,范围总是0到15,对应字符为0123456789abcdfg,也就是说超过50%的SQL_ID都是以数字开头的。

好了,八卦结束。

Oracle如何根据SQL_TEXT生成SQL_ID相关推荐

  1. oracle 查询本周数据生成下周数据

    CreateTime--2018年1月4日16:38:01 Author:Marydon oracle 查询本周数据生成下周数据 第一步:查询指定区间数据 第二步:改变查询结果的值 查询日期字段+7 ...

  2. linux创建特殊文件rules,RHEL5 Oracle Linux 5上生成正确的udev rule 规则文件

    RHEL5 Oracle Linux 5上生成正确的udev rule 规则文件 1.确认在所有RAC节点上已经安装了必要的UDEV包 [root@rh2 ~]# rpm -qa|grep udev ...

  3. oracle 生成 sql语句,Oracle使用SQL语句生成日历的实现方法

    Oracle使用SQL语句可以实现日历的功能,下面就为您详细介绍Oracle使用SQL语句生成日历的实现方法,希望对您能有所帮助. 1 要构造某年某月的日历,必须先知道这个月的开始时间,结束时间及天数 ...

  4. oracle+生成+sql语句,Oracle使用SQL语句生成日历的实现方法

    Oracle使用SQL语句可以实现日历的功能,下面就为您详细介绍Oracle使用SQL语句生成日历的实现方法,希望对您能有所帮助. 1 要构造某年某月的日历,必须先知道这个月的开始时间,结束时间及天数 ...

  5. Oracle使用SQL*Plus生成html文件

    Oracle使用SQL*Plus生成html文件 SQL*Plus生成html文件 2016-12-27 12:12 by 潇湘隐者, 556 阅读, 9 评论, 收藏, 编辑 最近使用SQL*Plu ...

  6. Oracle工具类-生成数据库现有Job的创建脚本

    生成Oracle数据库现有Job的创建脚本 -- 生成现有Job的创建脚本create or replace procedure proc_generate_job_create_sql isbegi ...

  7. oracle中创建实体,生成实体-SqlSugar 4.x-文档园

    注意:使用DbFirst数据库账户要有系统表的权限,否则无法读取表的结构 1.将库里面所有表都生成实体类文件db.DbFirst.CreateClassFile("c:\\Demo\\1&q ...

  8. oracle导出建表主键,oracle主键自动生成 配合hibernate的生成策略详解

    hibernate配合oracle自动生成主键策略有两种方法: A)设置ID的增长策略是sequence,同时指定sequence的名字,最好每个表建一个sequence,此种做法就如同MS-SQL, ...

  9. 【转】有关Oracle随机字符串的生成方法及具体应用

    Oracle生成随机字符串的方法是通过dbms_random.string实现的. 1.dbms_random.string用法 Oracle官方文档参考链接:http://download.orac ...

最新文章

  1. vs2005中文的,可是有180天的适用期,哪位高手能破了啊
  2. tomcat7 https 拒绝连接_Ubuntu上运行Docker提示权限拒绝,如何处理?
  3. java 下拉列表监听_javascript对下拉列表框(select)的操作
  4. 【php7扩展开发六】zval的操作
  5. java 枚举类型enum用法_别再说Python没有枚举类型了,好好看看
  6. linux fb设备驱动,linux设备驱动归纳总结(八):1.总线、设备和驱动
  7. 小程序毕设作品之微信校园维修报修小程序毕业设计成品(7)中期检查报告
  8. 计算机页面底端插入页码,word中怎么插入页码_word增加页码教程
  9. win7计算机自动关机设置在哪里设置方法,win7系统怎么设置自动关机
  10. 超声波测距原理与实现
  11. 知识点 - 后缀数组
  12. 吴恩达亲述:如何高效阅读论文,开启一个新的领域!
  13. 腾讯云IM回调接口-单聊发言后回调
  14. mysql join 一对多_Join 一对多连接
  15. Activiti7笔记(二)Activiti7一共涉及到25张表,哪些操作会涉及哪些表,每张表的作用是什么
  16. 伯通过计算机网络,用于互联的计算机网络的安全系统
  17. MySQL报错问题:2509
  18. 分布式鲁棒优化初学1
  19. Eigen的介绍、安装
  20. 黑马编程开发各种免费资源-安排一下

热门文章

  1. 移动端点击出现阴影 css解决方案
  2. cx_Oracle.DatabaseError: DPI-1047: 64-bit Oracle Client library cannot be loaded 解决方法
  3. 树莓派VI命令大全(附vim使用异常,卸载重新安装步骤)
  4. 解决Ubuntu系统“无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系”的有效方法
  5. 使用kibana可视化报表实时监控你的应用程序,从日志中找出问题,解决问题
  6. 爬坑系列----Redis查询key报空指针异常,而redis中确实存在该key
  7. 如何从JQuery中的each()函数中断/退出? [重复]
  8. jQuery获取select onChange的值
  9. win10游戏版如何安装 Windows10安装游戏版的步骤方法
  10. Linux:sudo命令实例讲解