[20240320]空格与sqlpus的sql语句.txt
--//优化sql语句时遇到的问题,自己上我发现我手工执行获得的sql_id与程序里面的sql_id不一致,原因很多sqlplus下如果是dos文本格
--//式,计算的文本会将\r\n字符变成\n,如果多行,文本结尾的空格会删除等等,通过例子说明:
1.环境:
scott@book> @ver1
port_string version banner
------------------------------ -------------- --------------------------------------------------------------------------------
x86_64/linux 2.4.xx 11.2.0.4.0 oracle database 11g enterprise edition release 11.2.0.4.0 - 64bit production
2.测试1:
--//开头存在空格.
scott@book> select * from dept where deptno=10;
deptno dname loc
---------- -------------- -------------
10 accounting new york
scott@book> @ hash
hash_value sql_id child_number kgl_bucket plan_hash_value hash_hex sql_exec_start sql_exec_id
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
832478979 agw1v10stx7s3 0 40707 2852011669 319e9f03 2024-03-20 08:31:08 16777216
scott@book> select * from dept where deptno=10;
deptno dname loc
---------- -------------- -------------
10 accounting new york
scott@book> @ hash
hash_value sql_id child_number kgl_bucket plan_hash_value hash_hex sql_exec_start sql_exec_id
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
911274289 4xamnunv51w9j 0 61745 2852011669 3650f131 2024-03-20 08:31:14 16777216
scott@book> select sql_text from v$sql where sql_id in ('agw1v10stx7s3','4xamnunv51w9j');
sql_text
------------------------------------------------------------
select * from dept where deptno=10
select * from dept where deptno=10
--//并没有删除开头的sql语句。
3.测试2:
--//执行脚本的结尾存在空格.
scott@book> select * from dept where deptno=10 ;
deptno dname loc
---------- -------------- -------------
10 accounting new york
scott@book> @ hash
hash_value sql_id child_number kgl_bucket plan_hash_value hash_hex sql_exec_start sql_exec_id
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
2103677907 8qprua5yq74ym 0 103379 2852011669 7d6393d3 2024-03-22 15:58:00 16777216
scott@book> select * from dept where deptno=10;
deptno dname loc
---------- -------------- -------------
10 accounting new york
scott@book> @ hash
hash_value sql_id child_number kgl_bucket plan_hash_value hash_hex sql_exec_start sql_exec_id
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
911274289 4xamnunv51w9j 0 61745 2852011669 3650f131 2024-03-22 15:58:05 16777216
scott@book> select sql_text||'a' c60 from v$sql where sql_id in ('8qprua5yq74ym','4xamnunv51w9j');
c60
------------------------------------------------------------
select * from dept where deptno=10a
select * from dept where deptno=10 a
--//很明显分号结尾的空格计算在内.
4.测试3:
--//建立文件b1.txt,unix文件格式。
$ cat -ev b1.txt
select * from dept where $
deptno=20;$
--//where后面有4个空格。
scott@book> @ b1.txt
deptno dname loc
---------- -------------- -------------
20 research dallas
scott@book> @ hash
hash_value sql_id child_number kgl_bucket plan_hash_value hash_hex sql_exec_start sql_exec_id
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
561182986 4rj7xbhhr5y8a 0 63754 2852011669 2172f90a 2024-03-20 09:04:38 16777221
--//记下sql_id=4rj7xbhhr5y8a
--//首先看看v$sql视图sql_text与sql_fulltext的区别:
scott@book> select sql_text from v$sql where sql_id='4rj7xbhhr5y8a';
sql_text
------------------------------------------------------------
select * from dept where deptno=20
scott@book> select sql_fulltext c60 from v$sql where sql_id='4rj7xbhhr5y8a';
c60
------------------------------------------------------------
select * from dept where
deptno=20
--//可以看出v$sql视图sql_text与sql_fulltext的区别,sql_text删除结尾的空格,并且过滤了\n字符,而sql_fulltext基本原样输出。
--//另外注意一点两者sql_text,sql_fulltext数据类型不一样,前者varchar2(1000),后者clob类型。
--//sql_id的计算问题:
scott@book> spool b31.txt
scott@book> @ sql_idz 4rj7xbhhr5y8a
--sql_id = 4rj7xbhhr5y8a
select * from dept where
deptno=20;
scott@book> spool off
$ cat -ev b31.txt
scott@book> @ sql_idz 4rj7xbhhr5y8a$
$
--sql_id = 4rj7xbhhr5y8a$
$
select * from dept where$
deptno=20;$
$
scott@book> spool off$
--//注意看where后面的空格丢失了,也许输出忘记考虑trimspool的设置问题。看看计算如何.
$ ~/bin/sql_idx.sh b1.txt 1 > bb.txt
sql_text = select * from dept where $
deptno=20\0$
full_hash_value(16) = c141e01e5d8842bb39e5fafe98fc8176 $
hash_value(10) = 2566685046 $
sql_id(32) = 3mtguzucgt0bq$
sql_id(32) = 3mtguzucgt0bq$
sql_id(32) = 3mtguzucgt0bq$
--//很明显使用我写的脚本计算sql_id=3mtguzucgt0bq与sqlplus程序执行的sql_id=4rj7xbhhr5y8a不同.
--//改写如下:
$ cp b1.txt b2.txt
$ vi b2.txt
$ cat -ev b2.txt
select * from dept where$
deptno=20;$
$ ~/bin/sql_idx.sh b2.txt 1 >| bb1.txt
$ cat -ev bb1.txt
sql_text = select * from dept where$
deptno=20\0$
full_hash_value(16) = e375f34fdcd15d104bc4fd5c2172f90a $
hash_value(10) = 561182986 $
sql_id(32) = 4rj7xbhhr5y8a$
sql_id(32) = 4rj7xbhhr5y8a$
sql_id(32) = 4rj7xbhhr5y8a$
--//很明显sql_id计算使用的是b2.txt的文件计算的,where后面的空格取消了。
5.测试4:
--//再来看看文件格式问题。
$ unix2dos b1.txt
unix2dos: converting file b1.txt to dos format ...
scott@book> @ b1.txt
deptno dname loc
---------- -------------- -------------
20 research dallas
scott@book> @ hash
hash_value sql_id child_number kgl_bucket plan_hash_value hash_hex sql_exec_start sql_exec_id
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
561182986 4rj7xbhhr5y8a 0 63754 2852011669 2172f90a 2024-03-20 09:24:04 16777224
--//还是一样,说明还是按照b2.txt的文件计算sql_id。
6.测试5:
--//建立文件bx.sql,unix文件格式。
$ cat -ev bx.sql
select ^m * ^m from ^m dept ^mwhere ^mdeptno=20;$
scott@book> @ bx.sql
deptno dname loc
---------- -------------- -------------
20 research dallas
scott@book> @ hash
hash_value sql_id child_number kgl_bucket plan_hash_value hash_hex sql_exec_start sql_exec_id
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
1668358914 cymmqupjr28s2 0 74498 2852011669 63712302 2024-03-20 15:16:34 16777221
scott@book> select sql_text from v$sql where sql_id='cymmqupjr28s2';
sql_text
------------------------------------------------------------
deptno=20
scott@book> select sql_fulltext from v$sql where sql_id='cymmqupjr28s2';
sql_fulltext
-----------------------------------------------------------
deptno=20
--//^m的输出导致输出看的非常怪异!!
scott@book> @ sql_idx cymmqupjr28s2
--sql_id = cymmqupjr28s2
deptno=20;
scott@book> @ sql_idz cymmqupjr28s2
--sql_id = cymmqupjr28s2
deptno=20;
scott@book> @ sql_id cymmqupjr28s2
--sql_id = cymmqupjr28s2
select * from dept where deptno=20;
--//注:我写的3个查看sql_id的版本,sql_id.sql版本过滤掉^m.sql_idz.sql的保持原样输出.
scott@book> spool by.txt
scott@book> select sql_fulltext from v$sql where sql_id='cymmqupjr28s2';
sql_fulltext
-------------------------------------------------------------------------
deptno=20
scott@book> spool off
--//编辑by.txt后如下:
$ cat -ev by.txt
select ^m * ^m from ^m dept ^mwhere ^mdeptno=20$
$ ~/bin/sql_idx.sh by.txt 1 | cat -ev
sql_text = select ^m * ^m from ^m dept ^mwhere ^mdeptno=20\0$
full_hash_value(16) = 0e6c6bf775d7aa8dcf4e76d563712302 $
hash_value(10) = 1668358914 $
sql_id(32) = cymmqupjr28s2$
sql_id(32) = cymmqupjr28s2$
sql_id(32) = cymmqupjr28s2$
$ ~/bin/sql_idx.sh bx.sql 1 | cat -ev
sql_text = select ^m * ^m from ^m dept ^mwhere ^mdeptno=20\0$
full_hash_value(16) = 0e6c6bf775d7aa8dcf4e76d563712302 $
hash_value(10) = 1668358914 $
sql_id(32) = cymmqupjr28s2$
sql_id(32) = cymmqupjr28s2$
sql_id(32) = cymmqupjr28s2$
--//可以看出中间出现的^m不过过滤.
7.继续测试:
--//重新编辑bx.sql,内容如下:
$ cat -ev bx.sql
select ^m$
* $
^m $
from ^m $
dept ^mwhere ^mdeptno=20;$
scott@book> set sqlblanklines off
scott@book> @ bx.sql
sp2-0042: unknown command "from" - rest of line ignored.
wher..." - rest of line ignored.ing "dept
--//存在一个空行。
scott@book> set sqlblanklines on
scott@book> @ bx.sql
deptno dname loc
---------- -------------- -------------
20 research dallas
scott@book> @ hash
hash_value sql_id child_number kgl_bucket plan_hash_value hash_hex sql_exec_start sql_exec_id
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
987480685 a9mmzpcxdrhmd 0 115309 2852011669 3adbc26d 2024-03-20 15:27:59 16777220
scott@book> select sql_text from v$sql where sql_id='a9mmzpcxdrhmd';
sql_text
------------------------------------------------------------
deptno=20* from dept
scott@book> select sql_fulltext c60 from v$sql where sql_id='a9mmzpcxdrhmd';
c60
------------------------------------------------------------
select
*
from
deptno=20
scott@book> @ sql_id a9mmzpcxdrhmd
--sql_id = a9mmzpcxdrhmd
select
*
from
dept where deptno=20;
scott@book> @ sql_idx a9mmzpcxdrhmd
--sql_id = a9mmzpcxdrhmd
select
*
from
deptno=20;
scott@book> @ sql_idz a9mmzpcxdrhmd
--sql_id = a9mmzpcxdrhmd
select
*
from
deptno=20;
scott@book> spool by.txt
scott@book> select sql_fulltext c60 from v$sql where sql_id='a9mmzpcxdrhmd';
c60
------------------------------------------------------------
select
*
from
deptno=20
scott@book> spool off
$ cat -ev by.txt
scott@book> select sql_fulltext c60 from v$sql where sql_id='a9mmzpcxdrhmd';$
c60$
------------------------------------------------------------$
select$
*$
$
from$
dept ^mwhere ^mdeptno=20$
$
$
scott@book> spool off$
--//为了对比方便,复制上面的cat -ev bx.sql输出:
$ cat -ev bx.sql
select ^m$
* $
^m $
from ^m $
dept ^mwhere ^mdeptno=20;$
--//对比bx.sql的内容:
--//第1行select后面的空格,^m消失。
--//第2行*后面的空格消失。
--//第3行全部删除。
--//第4行from后面的空格以及^m删除.
--//第5行保留中间出现的的^m。
--//编辑by.txt
$ cat -ev by.txt
select$
*$
$
from$
dept ^mwhere ^mdeptno=20$
$ ~/bin/sql_idx.sh by.txt 1 | cat -ev
sql_text = select$
*$
$
from$
dept ^mwhere ^mdeptno=20\0$
full_hash_value(16) = d830d3e1d201c57aa4ce7fab3adbc26d $
hash_value(10) = 987480685 $
sql_id(32) = a9mmzpcxdrhmd$
sql_id(32) = a9mmzpcxdrhmd$
sql_id(32) = a9mmzpcxdrhmd$
--//可以确定sql_id计算使用的是v$sql.sql_fulltext计算的。
scott@book> spool bz.txt
scott@book> @ sql_idz a9mmzpcxdrhmd
--sql_id = a9mmzpcxdrhmd
select
*
from
deptno=20;
scott@book> spool off
$ cat -ev bz.txt
select$
*$
$
from$
dept ^mwhere ^mdeptno=20;$
$ ~/bin/sql_idx.sh bz.txt 1 | cat -ev
sql_text = select$
*$
$
from$
dept ^mwhere ^mdeptno=20\0$
full_hash_value(16) = d830d3e1d201c57aa4ce7fab3adbc26d $
hash_value(10) = 987480685 $
sql_id(32) = a9mmzpcxdrhmd$
sql_id(32) = a9mmzpcxdrhmd$
sql_id(32) = a9mmzpcxdrhmd$
8.总结:
--//我以前一直认为都是按照sqlplus的方式计算的,实际上并不是这样的情况,其它程序比如pb开发的程序或者w3wp.exe的程序,里面
--//sql语句是保持"原样",大部分应该如此.
--//这样就好解析我生产系统遇到的问题。
--//我生成的脚本过滤掉chr(13)变成unix格式,在这种情况下计算的sql_id肯定不一样,另外结尾的空格也保留下来,总之sqlplus下执
--//行获得sql_id 与其它程序的sql_id不同是很正常的情况。
--//你可以测试最新的toad下结尾的空格是保留的,早期的toad版本是在执行前在最后一行加入1个空格,最新的版本没有这个问题.
--//参考:
--//[20120327]toad与sqlplus下执行sql语句的一个细节.txt ->https://blog.itpub.net/267265/viewspace-719592/
--//[20150803]toad 12版本1个小变化.txt -> https://blog.itpub.net/267265/viewspace-1760071/
9.附上相关脚本:
$ cat sql_idz.sql
set linesize 32767
--set linesize 4000
var v_sql_fulltext clob
col sql_fulltext for a4000 word_wrap
set feedback off
set serveroutput on
prompt
prompt --sql_id = &&1
prompt
declare
v_sql_fulltext clob;
v_count number;
begin
select count(*) into v_count from gv$sqlarea where sql_id = '&&1' and rownum=1;
if v_count=1
then
select sql_fulltext||';' sql_fulltext into v_sql_fulltext from gv$sqlarea where sql_id = '&&1' and rownum = 1;
--select replace (sql_fulltext||';', '', '') sql_fulltext into v_sql_fulltext from gv$sqlarea where sql_id = '&&1' and rownum = 1;
--select replace (sql_fulltext||';', chr(13), '') sql_fulltext into v_sql_fulltext from gv$sqlarea where sql_id = '&&1' and rownum = 1;
--select replace (sql_fulltext||';', chr(13), chr(13)) sql_fulltext into v_sql_fulltext from gv$sqlarea where sql_id = '&&1' and rownum = 1;
dbms_output.put_line (v_sql_fulltext);
else
select count(*) into v_count from dba_hist_sqltext where sql_id='&&1' and rownum=1;
if v_count=1
then
select sql_text||';' into v_sql_fulltext from dba_hist_sqltext where sql_id='&&1' and rownum=1;
--select replace (sql_text||';','','') into v_sql_fulltext from dba_hist_sqltext where sql_id='&&1' and rownum=1;
--select replace (sql_text||';',chr(13),'') into v_sql_fulltext from dba_hist_sqltext where sql_id='&&1' and rownum=1;
--select replace (sql_text||';',chr(13),chr(13)) into v_sql_fulltext from dba_hist_sqltext where sql_id='&&1' and rownum=1;
dbms_output.put_line (v_sql_fulltext);
end if;
end if;
exception when no_data_found then
null;
end;
/
prompt
set serveroutput off
set feedback 6
set linesize 277
$ cat ~/bin/sql_idx.sh
#! /bin/bash
# calcucate sql_text of full_hash_value(16),hash_value(10),sql_id(32).
# argv1 sql statement or sql of text file
# argv2 flag: 0= sql statement 1=sql of text file
odebug=${odebug:-0}
oflag=${2:-0}
if [ $oflag -eq 0 ]
then
sql_text=${1}'\0'
fi
if [ $oflag -eq 1 ]
then
# sql_text="$( cat $1 | dos2unix | sed '$s/;\s*//')"'\0'
$//" | sed '$s/;\s*//')"'\0'd "s/
sql_text="$( cat $1 | sed '$s/;\s*//')"'\0'
fi
v1=$(echo -e -n "$sql_text" | md5sum | sed 's/ -//' | xxd -r -p | od -t x4 | sed -n -e 's/^0\+ //' -e 's/ //gp' | tr 'a-z' 'a-z')
v2=${v1:(-16):16}
v3=${v2:(-8):8}
# v2=$(echo "obase=16;ibase=16; $v1 % 10000000000000000" | bc| tr -d '\\\r\n')
# v3=$(echo "obase=10;ibase=16; $v1 % 100000000" | bc| tr -d '\\\r\n')
if [ $odebug -eq 1 ] ; then
echo v1=$v1 v2=$v2 v3=$v3
fi
echo "sql_text = $sql_text"
echo "full_hash_value(16) = $v1 "
echo "hash_value(10) = $(( 16#$v3 )) "
base32=($(echo {0..9} {a..z} | tr -d 'eilo'))
res=''
for i in $(echo "obase=32;ibase=16; $v2" | bc| tr -d '\\\r\n')
do
res=${res}${base32[$(( 10#$i ))]}
done
echo "sql_id(32) = $(printf "%13s" $res | tr ' ' '0')"
echo "sql_id(32) = $(printf "%013s" $res)"
res1=$(eval $(echo "obase=32;ibase=16; $v2" | bc| tr -d '\\\r\n' | awk 'begin{rs=" +"; printf "echo " }/./{printf "${base32[$(( 10#%02d))]}", $1}' ))
echo "sql_id(32) = $(printf "%013s" $res1)"
发表评论