首页 > 数据库 >Oracle如何根据SQL_TEXT生成SQL_ID

Oracle如何根据SQL_TEXT生成SQL_ID

时间:2024-09-28 19:45:35浏览次数:1  
标签:字符 TEXT sql SQL Oracle ID md5

Oracle如何根据SQL_TEXT生成SQL_ID

php中文网 发布: 2016-06-06 20:08:48 原创 1175人浏览过  

本文纯属八卦,基本没有任何实用价值。Oracle总是都会通过SQL_ID来标志一个唯一的SQL。SQL_ID与SQL_TEXT一一对应。如果两个SQL文本有任何不同,包括空格等任何不可见字符,都会导致SQL_ID不同。本文八卦的内容是:Oracle如何根据SQL_TEXT内容散列成一个13位

 

 

 

本文纯属八卦,基本没有任何实用价值。oracle总是都会通过sql_id来标志一个唯一的sql。sql_id与sql_text一一对应。如果两个sql文本有任何不同,包括空格等任何不可见字符,都会导致sql_id不同。本文八卦的内容是:oracle如何根据sql_text内容散列成一个13位的字符串。为什么这个字符串会是13位?为什么这个字符经常以数字开头?

本文参考TANEL PODER和Slavik的两篇介绍(1,2),详细介绍转换原理,顺便给出PHP/Perl实现代码。

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:

1

select sysdate from dual;

登录后复制

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

1

2

3

4

5

SQL > select sql_id, hash_value from v$sql

  where sql_text = 'select sysdate from dual';

SQL_ID        HASH_VALUE

------------- ----------

h35uxf5uhmm1 2343063137

登录后复制

1.2 SQL_ID计算的详细过程

1.2.1 散列值的计算

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

1

2

3

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位):

1

2

3

4

|10101011|11010100|11011011|10110011|

|00001001|01101011|00010101|11110001|

|11101011|10111010|00001100|01111000|

|01100001|01001110|10101000|10001011|

登录后复制
1.2.2 取低64位整数

md5散列值的低64位为:

1

2

|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,所以取模运算时对应的整数字节序(人读取的时候):

1

2

|01111000|00001100|10111010|11101011|

|10001011|10101000|01001110|01100001|

登录后复制

perl代码:

1

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对应编码字符为:

1

abcdfghjkmnpqrstuvwxyz

登录后复制

编码和字符对应关系

1

2

3

4

5

编码  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对应的最后三位:

1

h35uxf5uhmm1

登录后复制

3 代码片段

3P程序代码如下:

3.1 Perl

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#!/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 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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

function stmt_2_sqlid($stmt){

  $h = md5($stmt."\0",TRUE);

  $un = unpack("V*",$h);

  $msb = $un[3] + 0; if($msb

<h4>3.3 Python</h4>

<p>参考:Oracle sql_id and hash value</p>

<h3>4. 最后</h3>

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

<p>好了,八卦结束。</p>

    <p class="copyright">

        原文地址:Oracle如何根据SQL_TEXT生成SQL_ID, 感谢原作者分享。

    </p>

     

    

登录后复制  

标签:字符,TEXT,sql,SQL,Oracle,ID,md5
From: https://www.cnblogs.com/jijm123/p/18438314

相关文章

  • MySQL安全加固
    安装mysql下载地址:http://dev.mysql.com/get/Downloads/MySQL-5.6/mysq1-5.6.33-linux-glibc2.5x86_64.tar.gz解压:tar-zxvfmysql-5.6.33-linux-glibc2.5-x86_64.tar.gz复制解压后的mysql目录:cp-rmysql-5.6.33-linux-glibc2.5-x8664/usr/local/mysql添加用户组groupaddmysql......
  • MySQL 中的 FOREIGN KEY 约束:确保数据完整性的关键
    在MySQL数据库中,FOREIGNKEY(外键)约束是一种非常重要的机制,它可以帮助我们确保数据的完整性和一致性。那么,FOREIGNKEY约束究竟是什么呢?让我们一起来深入了解一下。一、什么是FOREIGNKEY约束?FOREIGNKEY约束是一种用于建立两个表之间关系的约束。它通过在一个表中定义一个......
  • MySQL 性能剖析全攻略
    在使用MySQL数据库的过程中,性能问题往往是让开发者和管理员头疼的难题。为了有效地解决这些问题,我们需要对MySQL进行性能剖析。那么,如何在MySQL中进行性能剖析呢?本文将为你详细介绍。一、为什么要进行性能剖析?MySQL数据库在运行过程中,可能会出现各种性能问题,如查询速度慢......
  • oracle11g启动过程中加载配置文件
    oracle指定配置文件启动,要是不指定配置文件启动的话默认找的参数文件顺序如下:在oracle11g中oracle启动过程中默认会加载相应的配置文件来启动oracle服务。检查参数文件有两个,一个是spfile<ORACLE_SID>.ora文件,另一个是inti<ORACLE_SID>.ora文件。oracle软件服务安装完成后,s......
  • 【大数据入门 | Hive】hiveSQL常用语句
    1.数据准备①在/opt/module/hive/datas/路径上创建dept.txt文件,并赋值如下内容:部门编号部门名称部门位置id[atguigu@hadoop102datas]$vimdept.txt10 行政部 170020 财务部 180030 教学部 190040 销售部 1700②在/opt/module/hive/datas/路径上创建emp.txt文件,并......
  • 【力扣 | SQL题 | 每日三题】力扣1068, 1204, 1193, 1084, 1141
    1.力扣1068:产品销售分析11.1题目:销售表 Sales:+-------------+-------+|ColumnName|Type|+-------------+-------+|sale_id|int||product_id|int||year|int||quantity|int||price|int|+-------------+-......
  • MySQL数据库基础
    一.知识框架一开始学数据库,我们要先大致了解一下整个体系是怎么样的,搭建起一个知识体系,方便记忆和学习。什么是数据库,由名可知,就是保存用户数据的程序就是数据库,那MySQL又是什么呢,其实MySQL就是一个应用软件,通过这个应用软件就可以创建数据库,能够对数据库进行增删查改等一系......
  • mysql 锁 记录
     表级别锁1.表锁locktablesxxread. 线程只能读locktablesxxwrite其他线程无法读写  2.MDL锁(元数据锁 MDL是为了对表执行CRUD操作时,防止其他线程对这个表结构做了变更,反之亦然注意 MDL是在事务提交后才会释放,因此长事务可能导致修改表操作阻塞  ......
  • PARTVI-Oracle数据库管理与开发-数据库管理员的概念
    18.数据库管理员的概念18.1.数据库管理员的职责数据库管理员(DBA)的主要责任是使企业数据对其用户可用。DBA必须与开发人员紧密合作,确保他们的应用程序有效地使用数据库,并与系统管理员合作,确保物理资源充足且使用高效。OracleDBA负责了解Oracle数据库架构以及数据库的工作原理......
  • 数据库技术提升-MySQL数据库原理、设计与应用【1.4】
    4.新增字段对于已经创建好的数据表,也可以根据业务需求利用ADD新增字段,基本语法格式如下·语法格式1:新增一个字段,并可指定其位置ALTERTABLE数据表名ADD[COLUMN]新字段名字段类型[FIRSTIAFTER字段名]语法格式2:同时新增多个字段ALTERTABLE数据表名ADD[COLUM......