Run long Oracle stored procedure from PHP - php

I have a stored procedure which I run from PHP using:
//Request does not change
$sql = 'BEGIN SP_GET_MY_DATA(:POP, :SEG, :DUR, :VIEW, :PAGE, :OUTPUT_CUR); END;';
//Statement does not change
$stmt = oci_parse($conn,$sql);
oci_bind_by_name($stmt,':POP',$pop);
oci_bind_by_name($stmt,':SEG',$seg);
oci_bind_by_name($stmt,':DUR',$dur);
oci_bind_by_name($stmt,':VIEW',$view);
oci_bind_by_name($stmt,':PAGE',$page);
//But BEFORE statement, Create your cursor
$cursor = oci_new_cursor($conn)
// On your code add the latest parameter to bind the cursor resource to the Oracle argument
oci_bind_by_name($stmt,":OUTPUT_CUR", $cursor,-1,OCI_B_CURSOR);
// Execute the statement as in your first try
oci_execute($stmt);
// and now, execute the cursor
oci_execute($cursor);
// Use OCIFetchinto in the same way as you would with SELECT
while ($data = oci_fetch_assoc($cursor, OCI_RETURN_LOBS )) {
print_r($data}
}
The problem is I have millions of rows and complex logic in stored procedure. When I execute SP_GET_MY_DATA through SQL developer, it takes around 2 hours to complete it.
PHP is timing out when I do it. I cannot increase the max_execution_time in PHP as well.
How can I run this on Oracle or using PHP without timing out? Please help.

I answered how to use Oracle Scheduler to run a long running procedure asynchronously pretty comprehensively in this answer on the DBA stack exchange. See https://dba.stackexchange.com/a/67913/38772
TL;DR is
-- submit this as a background job
BEGIN
dbms_scheduler.create_job (
job_name => 'MY_BACKGROUND_JOB'
, job_type => 'STORED_PROCEDURE'
, job_action => 'SP_GET_MY_DATA'
, enabled => TRUE
, auto_drop => TRUE
);
END;
You'll have to do a little more work if you want to pass in parameters to the procedure. You may find this answer helpful https://dba.stackexchange.com/q/42119/38772/
For additional reference with all the gory details, the relevant chapter from Oracle's documentation is at https://docs.oracle.com/database/121/ADMIN/scheduse.htm

Don't increase max_execution_time, set it to 0, allow it to run indefinitely. If you are going to return loads of rows, make sure to either increase memory (ini_set) or allow immediate buffer flushing so that it can output directly to client.
The latter will also prevent clients from prematurely disconnecting because they didn't get any data. (ob_implicit_flush(true);)

Related

ORACLE + PHP oci_execute() got freeze and not return anything

I am trying to execute oracle SQL statement using below code.
$ofe_id = '123';
$stmt = oci_parse($this->db->conn_id, "DECLARE
l_retval VARCHAR2(255);
l_message VARCHAR2(2000);
BEGIN
l_retval := offerte.fill_temp_fields(:ofe_id,l_message);
END;") or die("Cannot parse query");
oci_bind_by_name($stmt, ':ofe_id', $ofe_id);
// $err = oci_error($stmt); //It return 'false';
$respose = oci_execute($stmt, OCI_DEFAULT);
I am using PHP 5.6 with Ubuntu 14.0x.
When I tried to execute above statement. It freeze and return nothing.
When I tried to debug the code I found that It executes till oci_execute() , After that it will not execute for the next code. I was waiting till 30 minutes but seems that it not returned any response.
You might have another Oracle session open and have some locks around your table.
Sorry, I'm not an expert but I used to have similar problems when testing Oracle capabilities at the same time from PHP-Apache and from PHP-CLI with several different SQL+ prompts open (e.g. SYSDBA, schema owner and non-privileged user). After committing the "freezing oci_execute" error disappeared.
You should specify the corrent type and size for your variable, however:
oci_bind_by_name($stmt, ':ofe_id', $ofe_id, -1, SQLT_INT);
^^^^^^^^^^^^
Both PHP and Oracle do silent type conversion between numbers and numeric strings, but sometimes the tailing characters can be lost.
Just use OCI_DEFAULT prior to PHP 5.3.2, use OCI_NO_AUTO_COMMIT instead... Can't say if this will solve all your problems because I can't reproduce this error right now, but I'm almost sure that this can help.

Laravel never ending EXEC

I'm rewriting a program from pure PHP to Laravel and I have a problem with executing a stored procedure (I didn't write it).
When I try
$sheetLines = DB::select("exec XXXXXXX".dbo.PRICELIST '?'", [$id]);
It keeps on going while PHP hasn't reached max memory. (increasing memory only makes it run longer)
Meanwhile in the old program it takes about 3 seconds and sends the response.
$tsql = "exec XXXXXXX.".dbo.PRICELIST '".$id."'";
Also when I'm calling other stored procedures from other modules everything works fine.
I have noticed that something like this also happens if I try for example:
DB::select(count(price) from orders group by price);
// would work with: count(price) as price_count
I have searched this problem a lot but have found no solution.
I'll be thankful for any help
I would access the underlying PDO driver to execute the stored procedure.
Try
$db = DB::getPdo();
$stmt = $db->prepare("EXEC XXXXXXX.dbo.PRICELIST :id");
$stmt->bindValue(':id', $id);
$result = $stmt->execute();
If that fails to work you could try the query method;
$query = DB::getPdo()->query("EXEC XXXXXXX.dbo.PRICELIST $id");
Regarding your count issue, in Laravel's Eloquent you can do the following;
$count = DB::table('orders')->groupBy('price')->count('price');
I've little experience with PHP, but seen:
set nocount on
Cause oddball behavior in some MSSQL drivers. Especially a stored procedure returns a single recordset, but doesn't output a "(x row(s) affected)" type messages. (Messages can be seen when manually executing a query in SQL Server Management Studio.)
My rule of thumb for complex stored procedure is to add...
set nocount on
...at the start of a stored procedure and conclude with...
set nocount off
....just before the final output.
Example:
create proc spTester
as
begin
set nocount off
-- {... do lots of crazy processing ...}
-- ok, ready to return the final output
set nocount off
select 1 as colA, 2 as colB
end

Why can't I bind a value to this parameter when using PDO? [duplicate]

This question already has answers here:
Getting raw SQL query string from PDO prepared statements
(16 answers)
Closed 6 years ago.
In PHP, when accessing MySQL database with PDO with parametrized query, how can you check the final query (after having replaced all tokens)?
Is there a way to check what gets really executed by the database?
So I think I'll finally answer my own question in order to have a full solution for the record. But have to thank Ben James and Kailash Badu which provided the clues for this.
Short Answer
As mentioned by Ben James: NO.
The full SQL query does not exist on the PHP side, because the query-with-tokens and the parameters are sent separately to the database.
Only on the database side the full query exists.
Even trying to create a function to replace tokens on the PHP side would not guarantee the replacement process is the same as the SQL one (tricky stuff like token-type, bindValue vs bindParam, ...)
Workaround
This is where I elaborate on Kailash Badu's answer.
By logging all SQL queries, we can see what is really run on the server.
With mySQL, this can be done by updating the my.cnf (or my.ini in my case with Wamp server), and adding a line like:
log=[REPLACE_BY_PATH]/[REPLACE_BY_FILE_NAME]
Just do not run this in production!!!
You might be able to use PDOStatement->debugDumpParams. See the PHP documentation .
Using prepared statements with parametrised values is not simply another way to dynamically create a string of SQL. You create a prepared statement at the database, and then send the parameter values alone.
So what is probably sent to the database will be a PREPARE ..., then SET ... and finally EXECUTE ....
You won't be able to get some SQL string like SELECT * FROM ..., even if it would produce equivalent results, because no such query was ever actually sent to the database.
I check Query Log to see the exact query that was executed as prepared statement.
I initially avoided turning on logging to monitor PDO because I thought that it would be a hassle but it is not hard at all. You don't need to reboot MySQL (after 5.1.9):
Execute this SQL in phpMyAdmin or any other environment where you may have high db privileges:
SET GLOBAL general_log = 'ON';
In a terminal, tail your log file. Mine was here:
>sudo tail -f /usr/local/mysql/data/myMacComputerName.log
You can search for your mysql files with this terminal command:
>ps auxww|grep [m]ysqld
I found that PDO escapes everything, so you can't write
$dynamicField = 'userName';
$sql = "SELECT * FROM `example` WHERE `:field` = :value";
$this->statement = $this->db->prepare($sql);
$this->statement->bindValue(':field', $dynamicField);
$this->statement->bindValue(':value', 'mick');
$this->statement->execute();
Because it creates:
SELECT * FROM `example` WHERE `'userName'` = 'mick' ;
Which did not create an error, just an empty result. Instead I needed to use
$sql = "SELECT * FROM `example` WHERE `$dynamicField` = :value";
to get
SELECT * FROM `example` WHERE `userName` = 'mick' ;
When you are done execute:
SET GLOBAL general_log = 'OFF';
or else your logs will get huge.
What I did to print that actual query is a bit complicated but it works :)
In method that assigns variables to my statement I have another variable that looks a bit like this:
$this->fullStmt = str_replace($column, '\'' . str_replace('\'', '\\\'', $param) . '\'', $this->fullStmt);
Where:
$column is my token
$param is the actual value being assigned to token
$this->fullStmt is my print only statement with replaced tokens
What it does is a simply replace tokens with values when the real PDO assignment happens.
I hope I did not confuse you and at least pointed you in right direction.
The easiest way it can be done is by reading mysql execution log file and you can do that in runtime.
There is a nice explanation here:
How to show the last queries executed on MySQL?
I don't believe you can, though I hope that someone will prove me wrong.
I know you can print the query and its toString method will show you the sql without the replacements. That can be handy if you're building complex query strings, but it doesn't give you the full query with values.
I think easiest way to see final query text when you use pdo is to make special error and look error message. I don't know how to do that, but when i make sql error in yii framework that use pdo i could see query text

PDO->bindParam, PDO->bindValue and PDO->closeCursor

So far I have been using PDO->bindParam however while reading the manual I found PDO->bindValue from what I can tell PDO->bindValue passes by value where as PDO->bindParam passes by reference, is this the only difference?
$modThread = db()->prepare("UPDATE `threads` SET `modtime` = UNIX_TIMESTAMP( ) WHERE `threadid` =:id LIMIT 1");
while(something)
{
$modThread->bindParam(':id', $thread);
$modThread->execute();
//*******************HERE********************//
}
Again while reading the manual I found: PDO->closeCursor should I place it where marked? Is it optional/automatically called? Seems only certain drivers need it. Will calling it on a driver that doesn't need/support it cause errors? How about MySQL?
This isn't true. If you find yourself needing to use closeCursor, one of the most optimal times is for insert/update/delete commands, and rarely for SELECT statements for which you have already fetched results.
For example, if you select all records from a table, then issue $stmt->fetch(), this actually accomplishes the goal for closeCursor immediately as the rows are now no longer in an unfetched status.
From the manual:
This method is useful for database drivers that do not support executing a PDOStatement object when a previously executed PDOStatement object still has unfetched rows. If your database driver suffers from this limitation, the problem may manifest itself in an out-of-sequence error.
When you will really need closeCursor is during any of the following instances:
If your DB driver doesn't allow for a new stmt to be executed while unfetched rows are available from the previous execute
You have multiple prepared statements and would like to execute them one-after-another ($stmt1->execute(); $stmt->closeCursor(); $stmt2->execute(); $stmt2->closeCursor(); $stmt3...etc)
You have multiple stmts that must execute insert/update/delete inside the same block. This is true because, while you dont get mysql row results back, you DO get number of affected rows result set back (which is still a result).
When using transactions
When you want to issue select-style prepared statements and execute them, but not retrieve the data until later
When you don't need the closeCursor statement:
If you have already fetched the rows (as with $stmt->fetch()) before your next statement is to be executed. At this point the rows are in a "fetched" state and frees up the driver to execute new statements.
Just as useful for closing a cursor is unset() (ie: unset($stmt)) and setting the statement to null ($stmt = null), opening the doors for the built-in Garbage Collector to clear everything up.
See the manual for more information: http://php.net/manual/en/pdostatement.closecursor.php
The 'recurring' bindParam() here is not really necessary:
$thread = 0;
$modThread->bindParam(':id', $thread);
while($thread < 20)
{
$thread++;
$modThread->execute(); //executing with the new value, which you couldn't do with bindValue
}
You don't need a closeCursor() when there is no resultset (i.e, only with SELECT s or procedures giving results back) , but usually I've already done a fetchAll somewhere in a previous statement / row.

In PHP with PDO, how to check the final SQL parametrized query? [duplicate]

This question already has answers here:
Getting raw SQL query string from PDO prepared statements
(16 answers)
Closed 6 years ago.
In PHP, when accessing MySQL database with PDO with parametrized query, how can you check the final query (after having replaced all tokens)?
Is there a way to check what gets really executed by the database?
So I think I'll finally answer my own question in order to have a full solution for the record. But have to thank Ben James and Kailash Badu which provided the clues for this.
Short Answer
As mentioned by Ben James: NO.
The full SQL query does not exist on the PHP side, because the query-with-tokens and the parameters are sent separately to the database.
Only on the database side the full query exists.
Even trying to create a function to replace tokens on the PHP side would not guarantee the replacement process is the same as the SQL one (tricky stuff like token-type, bindValue vs bindParam, ...)
Workaround
This is where I elaborate on Kailash Badu's answer.
By logging all SQL queries, we can see what is really run on the server.
With mySQL, this can be done by updating the my.cnf (or my.ini in my case with Wamp server), and adding a line like:
log=[REPLACE_BY_PATH]/[REPLACE_BY_FILE_NAME]
Just do not run this in production!!!
You might be able to use PDOStatement->debugDumpParams. See the PHP documentation .
Using prepared statements with parametrised values is not simply another way to dynamically create a string of SQL. You create a prepared statement at the database, and then send the parameter values alone.
So what is probably sent to the database will be a PREPARE ..., then SET ... and finally EXECUTE ....
You won't be able to get some SQL string like SELECT * FROM ..., even if it would produce equivalent results, because no such query was ever actually sent to the database.
I check Query Log to see the exact query that was executed as prepared statement.
I initially avoided turning on logging to monitor PDO because I thought that it would be a hassle but it is not hard at all. You don't need to reboot MySQL (after 5.1.9):
Execute this SQL in phpMyAdmin or any other environment where you may have high db privileges:
SET GLOBAL general_log = 'ON';
In a terminal, tail your log file. Mine was here:
>sudo tail -f /usr/local/mysql/data/myMacComputerName.log
You can search for your mysql files with this terminal command:
>ps auxww|grep [m]ysqld
I found that PDO escapes everything, so you can't write
$dynamicField = 'userName';
$sql = "SELECT * FROM `example` WHERE `:field` = :value";
$this->statement = $this->db->prepare($sql);
$this->statement->bindValue(':field', $dynamicField);
$this->statement->bindValue(':value', 'mick');
$this->statement->execute();
Because it creates:
SELECT * FROM `example` WHERE `'userName'` = 'mick' ;
Which did not create an error, just an empty result. Instead I needed to use
$sql = "SELECT * FROM `example` WHERE `$dynamicField` = :value";
to get
SELECT * FROM `example` WHERE `userName` = 'mick' ;
When you are done execute:
SET GLOBAL general_log = 'OFF';
or else your logs will get huge.
What I did to print that actual query is a bit complicated but it works :)
In method that assigns variables to my statement I have another variable that looks a bit like this:
$this->fullStmt = str_replace($column, '\'' . str_replace('\'', '\\\'', $param) . '\'', $this->fullStmt);
Where:
$column is my token
$param is the actual value being assigned to token
$this->fullStmt is my print only statement with replaced tokens
What it does is a simply replace tokens with values when the real PDO assignment happens.
I hope I did not confuse you and at least pointed you in right direction.
The easiest way it can be done is by reading mysql execution log file and you can do that in runtime.
There is a nice explanation here:
How to show the last queries executed on MySQL?
I don't believe you can, though I hope that someone will prove me wrong.
I know you can print the query and its toString method will show you the sql without the replacements. That can be handy if you're building complex query strings, but it doesn't give you the full query with values.
I think easiest way to see final query text when you use pdo is to make special error and look error message. I don't know how to do that, but when i make sql error in yii framework that use pdo i could see query text

Categories