我正在使用PHP脚本(下面复制)我编写了使用oci函数从给定Oracle数据库的不同模式中检索数据。昨天,我注意到一个问题,到目前为止100%可重复,我很难理解。
问题在于:我有一个简单的查询("从表中选择*,其中id =' 42'")和两个模式S1和S2。首先我连接到DB,然后对于每个模式,我更改current_schema并执行查询。
如果我在S1上执行此操作,则返回0结果,这很好。
如果我在S2上执行此操作,则返回2个结果,这很好。
如果我在S1上执行此操作,那么S2,它在S1上返回0结果(这很好),我得到了一个
尝试在S2上执行查询时出现1858 - ORA-01858: a non-numeric character was found where a numeric was expected
错误。相应的非数字字符似乎是' *'来自"选择*"。
由于我怀疑Oracle缓存问题,我决定在执行之前在查询末尾附加一个随机注释。这确实解决了这个问题。 这很糟糕,出于表演原因可能会出错。
由于我无法在SQL开发人员中重现此问题,问题可能也来自我的代码本身,但随机后缀修复问题的事实让我认为Oracle缓存正在做错事。
所以这里是我的代码:第一部分包含用于包装oci函数的有用函数,第二部分是几个不同测试的实际运行:BUG是错误情形(ORA-01858),RAND是具有随机后缀(精细)的场景,SCHEMA_1是仅具有第一个架构的场景(0结果:罚款),SCHEMA_2是仅具有第二个架构的场景(2个结果:罚款),SAME_SCHEMA是具有第二个架构两次的场景( 2结果两次:罚款)。
<?php
function db_trigger_error($e) // http://php.net/manual/en/function.oci-error.php
{
$message =
"\n<pre>\n" .
htmlentities($e['code']) . " - " .
htmlentities($e['message']) . " : \n" .
htmlentities(str_replace(array("\r\n","\t"),array(" "," "),$e['sqltext'])) .
sprintf("\n%" . ($e['offset']+1) . "s", "^") .
"\n</pre>\n";
throw new Exception($message);
}
function execute_query($conn,$query,$fetch_wanted,&$fetch,&$result)
{
echo($query . "; ...<br>\n");
// PARSE
$stid = oci_parse($conn, $query);
if (!$stid) { db_trigger_error(oci_error($conn)); }
$type = oci_statement_type($stid);
// EXECUTE
$exe = oci_execute($stid);
if (!$exe) { db_trigger_error(oci_error($stid)); }
// FETCH ARRAY RESULT
$update = oci_num_rows($stid);
$fetch = $fetch_wanted ?
oci_fetch_all($stid, $result,null,null,OCI_FETCHSTATEMENT_BY_COLUMN) :
0;
// FREE STUFF
oci_free_statement($stid);
echo(" ... returned $fetch row" . ($fetch>1?'s':'') . " and updated/inserted/deleted $update row" . ($update>1?'s':'') . " ($type)<br>\n");
}
function connect_db(&$conn,$username,$password,$connec_string)
{
echo("Connecting to username/password@connec_string<br>\n");
$conn = oci_connect($username,$password,$connec_string);
if (!$conn) { db_trigger_error(oci_error()); }
echo("Connected to Server " . oci_server_version($conn) . " with Client version " . oci_client_version($conn) . "<br>\n");
}
function close_connect_db($conn)
{
if ($conn)
{
echo("Connection closed<br>\n");
oci_close($conn);
} else
{
echo("No connection to close<br>\n");
}
}
$username="USERNAME";
$password="PASSWORD";
$connec_string="(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=HOST)(PORT=PORT)))(CONNECT_DATA=(SERVICE_NAME=SERVICE)))";
$query="select * from TABLE t where t.T_ID = '035DA31100000000'";
foreach(array('BUG','RAND','SCHEMA_1','SCHEMA_2','SAME_SCHEMA') as $testCase)
{
echo "Start $testCase<br>\n";
$schemas=($testCase=='SCHEMA_1') ? array('S1') :
($testCase=='SCHEMA_2') ? array('S2') :
($testCase=='SAME_SCHEMA') ? array('S2','S2') :
array('S1','S1');
connect_db($conn,$username, $password, $connec_string);
foreach($schemas as $schema)
{
$suffix=($testCase=='RAND') ? ' --' . rand() : '';
$query_schema="alter session set current_schema=$schema";
try
{
execute_query ($conn,$query_schema, false,$fetch,$result,false);
execute_query($conn, $query . $suffix,true, $fetch,$result,false);
} catch (Exception $e)
{
echo 'Caught exception: ', $e->getMessage(), "\n";
$fetch=0;
}
}
close_connect_db($conn);
echo "End $testCase<br>\n";
}
?>
相应的输出是:
Start BUG
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S1; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 0 row and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
Warning: oci_execute() [function.oci-execute]: ORA-01858: a non-numeric character was found where a numeric was expected in /users/myself/public_html/ProofOfConcept.php on line 25
Caught exception:
1858 - ORA-01858: a non-numeric character was found where a numeric was expected :
select * from TABLE t where t.T_ID = '035DA31100000000'
^
Connection closed
End BUG
Start RAND
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S1; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000' --1458191218; ...
... returned 0 row and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000' --849888503; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
Connection closed
End RAND
Start SCHEMA_1
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
Connection closed
End SCHEMA_1
Start SCHEMA_2
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
Connection closed
End SCHEMA_2
Start SAME_SCHEMA
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
Connection closed
End SAME_SCHEMA
有人知道这个问题吗?如果在DB端有适当的修复?使用随机评论作为后缀不好吗? 提前谢谢。
编辑以添加部分调查
这是在Justin Cave的良好建议下完成的。
我现在已经更新了PHP代码,以便我可以使用select * from v$sql where sql_text like '%random_part'
基于随机令牌检索sql_id,以便我可以使用select * from table(dbms_xplan.display_cursor('sql_id'))
获取查询计划。在&#39; RAND&#39;方案,这个随机部分是不同的,我们运行select查询的两次,在其他情况下,随机部分保留用于查询的不同运行。
然后我们得到:
select * from v$sql where sql_text like '%random_part'
返回1行。
相应的查询计划是
Plan hash value: 2841037776
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TABLE | 1 | 283 | 1 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | I_T_ID | 1 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("BFP"."T_ID"='035DA31100000000')
select * from v$sql where sql_text like '%random_part'
返回具有相同SQL_ID的2行。行的不同部分是:END_OF_FETCH_COUNT(resp 1和0),PARSE_CALLS(resp 1和0),BUFFER_GETS(resp 4和3),OPTIMIZER_COST(resp 2和1),PARSING_SCHEMA_ID(resp 193和203),PARSING_SCHEMA_NAME( resp S1和S2),CHILD_NUMBER(resp 0和1),CPU_TIME(resp 7598和4093),ELAPSED_TIME(相同),CHILD_ADDRESS(奇怪的事情)。
相应的查询计划是
Plan hash value: 2841037776
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TABLE | 5 | 1535 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | I_T_ID | 5 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("BFP"."T_ID"='035DA31100000000')
似乎使用了相同的查询计划。