A neat way to parse a query only once per script, if the query is done inside a function:
<?php
function querySomething($conn, $id)
{
static $stmt;
if (is_null($stmt)) {
$stmt = oci_parse($conn, 'select * from t where pk = :id');
}
oci_bind_by_name($stmt, ':id', $id, -1);
oci_execute($stmt, OCI_DEFAULT);
return oci_fetch_array($stmt, OCI_ASSOC);
}
?>
With the static variable, the statment handle isn't closed after the function has terminated. Very nice for functions that are called e.g. in loops. Unfortunately this only works for static sql. If you have dynamic sql, you can do the following:
<?php
function querySomething($conn, $data)
{
static $stmt = array();
$first = true;
$query = 'select * from t';
foreach ($data as $key => $value) {
if ($first) {
$first = false;
$query .= ' where ';
} else {
$query .= ' and ';
}
$query .= "$key = :b$key";
}
$queryhash = md5($query);
if (is_null($stmt[$queryhash])) {
$stmt[$queryhash] = oci_parse($conn, $query);
}
foreach ($data as $key => $value) {
// don't use $value, because we bind memory addresses here.
// this would result in every bind pointing at the same value after foreach
oci_bind_by_name($stmt[$queryhash], ":b$key", $data[$key], -1);
}
oci_execute($stmt[$queryhash], OCI_DEFAULT);
return oci_fetch_array($stmt[$queryhash], OCI_ASSOC);
}
?>
oci_parse
(PHP 5, PECL OCI8 >= 1.1.0)
oci_parse — Prepara una sentencia de Oracle para su ejecución
Descripción
$connection
, string $sql_text
)
Prepara la sentencia dada por sql_text usando la conexión
connection y devuelve el identificador de sentencia,
el cual puede usarse con oci_bind_by_name(),
oci_execute() y demás funciones.
Los identificadores de sentencia pueden liberarse
con oci_free_statement() o estableciendo la
variable a NULL.
Parámetros
-
connection -
Un identificador de conexión de Oracle devuelto por oci_connect(), oci_pconnect(), o oci_new_connect().
-
sql_text -
La sentencia SQL o PL/SQL.
Las sentencias SQL no deberían finalizar con un punto y coma (";"). Las sentencias PL/SQL sí deberían finalizar con un punto y coma (";").
Valores devueltos
Devuelve el gestor de sentencia en caso de éxito, o FALSE en caso de error.
Ejemplos
Ejemplo #1 Ejemplo de oci_parse() para sentencias SQL
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
// Analizar la sentencia. Observe que no hay punto y coma final en la sentencia SQL
$stid = oci_parse($conn, 'SELECT * FROM employees');
oci_execute($stid);
echo "<table border='1'>\n";
while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
echo "<tr>\n";
foreach ($row as $item) {
echo " <td>" . ($item !== null ? htmlentities($item, ENT_QUOTES) : "") . "</td>\n";
}
echo "</tr>\n";
}
echo "</table>\n";
?>
Ejemplo #2 Ejemplo de oci_parse() para sentencias PL/SQL
<?php
/*
Antes de ejecutar el programa de PHP, cree un procedimiento almacenado en
SQL*Plus o SQL Developer:
CREATE OR REPLACE PROCEDURE myproc(p1 IN NUMBER, p2 OUT NUMBER) AS
BEGIN
p2 := p1 * 2;
END;
*/
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
$p1 = 8;
// Cuando se analizan programas PL/SQL, debería existir un punto y coma final de la cadena
$stid = oci_parse($conn, 'begin myproc(:p1, :p2); end;');
oci_bind_by_name($stid, ':p1', $p1);
oci_bind_by_name($stid, ':p2', $p2, 40);
oci_execute($stid);
print "$p2\n"; // imprime 16
oci_free_statement($stid);
oci_close($conn);
?>
Notas
Nota:
Esta función no valida
sql_text. La única manera de averiguar sisql_textes una sentencia SQL o PL/SQL válida es ejecutarla.
Nota:
En versiones de PHP anteriores a la 5.0.0 use ociparse() en su luagar. El nombre antiguo de la función todavía puede ser utilizado en versiones actuales, sin embargo es obsoleto y no se recomienda.
Ver también
- oci_execute() - Ejecuta una sentencia
- oci_free_statement() - Libera todos los recursos asociados con una sentencia o cursor
For those that are having trouble with error checking, i have noticed on a lot of sites that people are trying to check the statement handle for error messages with OCIParse. Since the statement handle ($sth) is not created yet, you need to check the database handle ($dbh) for any errors with OCIParse. For example:
instead of:
<?php
$stmt = OCIParse($conn, $query);
if (!$stmt) {
$oerr = OCIError($stmt);
echo "Fetch Code 1:".$oerr["message"];
exit;
}
?>
use:
<?php
$stmt = OCIParse($conn, $query);
if (!$stmt) {
$oerr = OCIError($conn);
echo "Fetch Code 1:".$oerr["message"];
exit;
}
?>
Hope this helps someone.
Whereas MySQL doesn't care what kind of quotes are around a LIKE clause, ociexecute gives the error:
ociexecute(): OCIStmtExecute: ORA-00904: "NM": invalid identifier
for the following.
<?php
$sql = "SELECT * FROM addresses "
. "WHERE state LIKE \"NM\""; // error!
$stmt = ociparse($conn, $sql);
ociexecute($stmt);
?>
it's fine if you just use single quotes:
. "WHERE state LIKE 'NM'";
but i think it's interesting that ociparse doesn't say anything
When you want to call stored function (and want to read its result) which executes DML queries (insert, update, delete) inside its body you can't use "select your_stored_function(:param1, :param2) from dual" because you will receive "ORA-14551: cannot perform a DML operation inside a query" error.
In order to call such function and get its result you need to wrap it into nested procedure with OUT parameter like this:
DECLARE
PROCEDURE caller(return_value OUT NUMBER) AS
BEGIN
return_value := your_stored_function(:param1, :param2);
END;
BEGIN
caller(:return_value);
END;
and bind to :return_value variable to get the result of function.
one of the most things that is done wrong with oracle is the following.
Cosider:
<?php
$dbh = ocilogon('user', 'pass', 'db');
for ($i = 0; $i<=10; $i++) {
$sth = ociparse($dbh, 'SELECT * FROM T WHERE x = :x');
ocibindbyname($sth, ':x', $i, -1);
ociexecute($sth, OCI_DEFAULT);
if (ocifetchrow($sth, $row, OCI_ASSOC+OCI_RETURN_NULLS)) {
var_dump($row);
}
}
ocilogoff($dbh);
?>
Problem here is, that you parse the same statement over and over and that'll cost ressources and will introduce many wait events. This problem will increase exponentially with the number of users using your system. That's one of the things besides not using bind variables that will prevent your application from scaling well.
The right approach:
<?php
$dbh = ocilogon('user', 'pass', 'db');
$sth = ociparse($dbh, 'SELECT * FROM T WHERE x = :x');
for ($i = 0; $i<=10; $i++) {
ocibindbyname($sth, ':x', $i, -1);
ociexecute($sth, OCI_DEFAULT);
if (ocifetchrow($sth, $row, OCI_ASSOC+OCI_RETURN_NULLS)) {
var_dump($row);
}
}
ocilogoff($dbh);
?>
Now we are parsing the statement once and using it as often as possible.
When your using Oracle, create proper indexes, use bind variables and parse once and execute often. Not doing so will get you into trouble when more than a few users are working with your application simultaneously.
regarding egypt note on double quotes, the reason for that behaviour is that Oracle treats things with double quotes as identifiers on a given statement... using single quotes won't provoque mistakes...
if you're using "complex" statements e.g such having calls to build in oracle functions in the select list (as in example below), I did not find any other way as using the "AS <Name>" clause to being able to output the functions outcome using ociresult
example:
<?php
ociparse($conn,"select EMPNO, LPAD(' ', 2*(LEVEL-1)) || ENAME AS COMPLETE_FANTASY_NAME, JOB, HIREDATE from scott.emp start with job='MANAGER' connect by PRIOR EMPNO = MGR");
echo ociresult $stmt,"COMPLETE_FANTASY_NAME")." ";
?>
BTW: I also found out by TAE that "COMPLETE_FANATASY_NAME" might not be "complete fantasy" as it has to be all capital letters.
