PDO output parameter error - pdo

I am using this SP, am getting both the result when using mysql workbench.
CREATE PROCEDURE SP(IN _start INT,IN _end INT,INOUT _count INT)
BEGIN
SET _count = (SELECT COUNT(*) FROM tbl);
SET #qry = CONCAT('select * from tbl limit ', _start, ',', _end);
PREPARE stmt FROM #qry;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
But when using with PDO am returning this error
$c=0;
$stmt = $this->_dbc->getConnection()->prepare("CALL SP(0,10,:count)");
$stmt->bindParam(":count",$c,PDO::PARAM_INT|PDO::PARAM_INPUT_OUTPUT,0);
$stmt->execute();
return $c;
PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1414 OUT or INOUT argument 3 for routine db22.SP is not a variable or NEW pseudo-variable in BEFORE trigger
But on changing
$this->_dbc->getConnection()->prepare("CALL SP(0,10,**:count**)");
to
$this->_dbc->getConnection()->prepare("CALL SP(0,10,#count)");
am not returning any error, but always getting the count as 0.
Whats the difference between :count and #count ?
How to get exact count via pdo ?

You can do the following workaround:
$dbh = $this->_dbc->getConnection()
$stmt = $dbh->prepare("CALL SP(0,10,#count)");
$stmt->execute();
$sql = "SELECT #count AS count";
$stmt = $dbh->prepare($sql);
$stmt->execute();
You can find more here: http://www.php.net/manual/en/pdo.prepared-statements.php#101993

Related

SQL Server 2008 stored procedure return value

Here is my stored procedure
ALTER PROCEDURE Delete
#ID nvarchar(64),
#value int = 0 output
AS
BEGIN
IF(EXISTS(SELECT * FROM A where Ap = #ID))
BEGIN
set #value = 1
END
ELSE IF(EXISTS(SELECT * FROM B where Bp = #ID))
BEGIN
set #value = 2
END
ELSE
BEGIN
select *
from Table_D
END
END
RETURN #value
Problem is that when I execute it, this does not return any value
There are multiple ways of returning status information from a stored procedure to an application. Each has its pros and cons; no single technique can definitely be said to be the right one in all circumstances. Even so, I'll start off with:
TL;DR: recommendation
Use RAISERROR if your stored procedure runs into trouble and cannot return the data it normally returns. Use OUTPUT parameters for information the client isn't free to ignore, but which isn't logically part of your result. Use the return value if you have an informational status code that the client is free to ignore. Use additional result sets only if you know what you're doing.
RAISERROR
If your stored procedure encounters an error and can't return any data, you can use RAISERROR to terminate execution and cause an exception to be raised on the client side.
CREATE PROCEDURE [Delete]
#ID nvarchar(64)
AS BEGIN
IF(EXISTS(SELECT * FROM A where Ap = #ID))
BEGIN
RAISERROR('Wrong. Try again.', 11, 1);
RETURN;
END
ELSE IF(EXISTS(SELECT * FROM B where Bp = #ID))
BEGIN
RAISERROR('Wrong in a different way. Try again.', 11, 2);
RETURN;
END
ELSE
BEGIN
select *
from Table_D
END
END
The second parameter (severity) must be set to at least 11 to make the error propagate as an exception, otherwise it's just an informational message. Those can be captured too, but that's out of the scope of this answer. The third parameter (state) can be whatever you like and could be used to pass the code of the error, if you need to localize it, for example. User-generated message always have SQL error code 50000, so that can't be used to distinguish different errors, and parsing the message is brittle.
The C# code to process the result:
try {
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
...
}
}
} catch (SqlException e) {
Console.WriteLine(
"Database error executing [Delete] (code {0}): {1}", e.State, e.Message
);
}
This is a natural fit for errors because the code to actually process the data stays what it is, and you can handle the exception at the right location (rather than propagating a status code everywhere). But this method is not appropriate if the stored procedure is expected to return a status that is informational and not an error, as you would be catching exceptions all the time even though nothing's wrong.
Output parameter
A stored procedure can set parameter values as well as receive them, by declaring them OUTPUT:
CREATE PROCEDURE [Delete]
#ID nvarchar(64),
#StatusCode INT OUTPUT
AS BEGIN
IF(EXISTS(SELECT * FROM A where Ap = #ID))
BEGIN
SET #StatusCode = 1;
END
ELSE IF(EXISTS(SELECT * FROM B where Bp = #ID))
BEGIN
SET #StatusCode = 2;
END
ELSE
BEGIN
SET #StatusCode = 0;
select *
from Table_D
END
END
From C#, this is captured in a parameter marked as an output parameter:
SqlParameter statusCodeParameter = command.Parameters.Add(
new SqlParameter {
ParameterName = "#StatusCode",
SqlDbType = SqlDbType.Int,
Direction = ParameterDirection.Output
}
);
using (var reader = command.ExecuteReader()) {
int statusCode = (int) statusCodeParameter.Value;
if (statusCode != 0) {
// show alert
return;
}
while (reader.Read()) {
...
}
}
The benefits here are that the client cannot forget to declare the parameter (it must be supplied), you're not restricted to a single INT, and you can use the value of the parameter to decide what you want to do with the resul set. Returning structured data is cumbersome this way (lots of OUTPUT parameters), but you could capture this in a single XML parameter.
Return value
Every stored procedure has a return value, which is a single INT. If you don't explicitly set it using RETURN, it stays at 0.
CREATE PROCEDURE [Delete]
#ID nvarchar(64)
AS BEGIN
IF(EXISTS(SELECT * FROM A where Ap = #ID))
BEGIN
RETURN 1
END
ELSE IF(EXISTS(SELECT * FROM B where Bp = #ID))
BEGIN
RETURN 2
END
ELSE
BEGIN
select *
from Table_D
END
END
From C#, the return value has to be captured in a single special parameter marked as the return value:
SqlParameter returnValueParameter = command.Parameters.Add(
new SqlParameter { Direction = ParameterDirection.ReturnValue }
);
using (var reader = command.ExecuteReader()) {
// this could be empty
while (reader.Read()) {
...
}
}
int returnValue = (int) returnValueParameter.Value;
It's important to note that the return value will not be available until you've processed all other result sets that the stored procedure generates (if any), so if you're using it for a status code that indicates there are no rows, you must still process the empty result set first before you have the status code. You cannot return anything other than an INT. Frameworks/OR mappers often have no support for the return value. Finally, note that the client is not required to do anything with the return value, so you have to carefully document its intended use.
Result set
The stored procedure can simply return what it wants as the result set, just like it's returning the other data. A stored procedure is allowed to return multiple result sets, so even if your status is logically separate from the other data, you can return it as a row.
CREATE PROCEDURE [Delete]
#ID nvarchar(64)
AS BEGIN
DECLARE #StatusCode INT = 0;
IF(EXISTS(SELECT * FROM A where Ap = #ID))
BEGIN
SET #StatusCode = 1;
END
ELSE IF(EXISTS(SELECT * FROM B where Bp = #ID))
BEGIN
SET #StatusCode = 2;
END
SELECT #StatusCode AS StatusCode;
IF #StatusCode = 0
BEGIN
select *
from Table_D
END
END
To process this with C#, we need SqlDataReader.NextResult:
using (var reader = command.ExecuteReader()) {
if (!reader.Read()) throw new MyException("Expected result from stored procedure.");
statusCode = reader.GetInt32(reader.GetOrdinal("StatusCode"));
if (statusCode != 0) {
// show alert
return;
}
reader.NextResult();
while (reader.Read()) {
// use the actual result set
}
}
The main drawback here is that it's not intuitive for a stored procedure to return a variable number of result sets, and very few data frameworks/OR mappers support it, so you'll nearly always end up writing manual code like this. Returning multiple result sets is not really a good fit for returning a single piece of data like a status code, but it might be an alternative to returning structured data in an XML output parameter (especially if there's lots).
The return seems to be out of scope of the procedure. Try:
ALTER PROCEDURE Delete
#ID nvarchar(64),
#value int=0 output
AS
BEGIN
IF(EXISTS(SELECT * FROM A where Ap=#ID))
BEGIN
set #value=1
END
ELSE IF(EXISTS(SELECT * FROM B where Bp=#ID))
BEGIN
set #value=2
END
ELSE
BEGIN
set #value=5
end --end if
RETURN #value
end --end procedure
This is where using tabbing properly makes the code a lot more readable, and these problems more obvious
Don't use the output parameter. Rather, use this:
ALTER PROCEDURE Delete
#ID nvarchar(64)
AS
BEGIN
DECLARE #value int
SET #value = 0
IF(EXISTS(SELECT 1 FROM A where Ap=#ID))
BEGIN
set #value=1
END
ELSE IF(EXISTS(SELECT 1 FROM B where Bp=#ID))
BEGIN
set #value=2
END
ELSE
BEGIN
set #value=5
end
Select #value as Value, * from Table_D
end
Can you try running the SP as the script below?
declare #pID as nvarchar(64)
declare #pValue as int
set #pID = 1 -- Your ID filter
exec Delete #pID, #pValue OUTPUT
select #pValue

How to execute a Pl sql file from a perl script

Hi folks can you please help me in understanding how to call a pl sql file from perl script
I have a pl sql file like this
DECLARE
x NUMBER := 100;
BEGIN
FOR i IN 1..10 LOOP
IF MOD(i,2) = 0 THEN -- i is even
INSERT INTO temp VALUES (i, x, 'i is even');
ELSE
INSERT INTO temp VALUES (i, x, 'i is odd');
END IF;
x := x + 100;
END LOOP;
COMMIT;
END;
The file is named test.sql I want to call this file from a perl script. I know first we have to connect to db and then perform the process but I Don now know how to execute this file from a perl script
Basically you need to
use the DBI module with the appropriate driver (Oracle or whatever)
slurp in the script into a variable by using plain perl
open a DB connection
prepare the slurped in script
execute the statement handle
disconnect from the DB
Here is an example (I am not showing how to slurp in the script):
use DBI;
use DBD::Oracle;
my $service="xxx";
my $user = "yyy";
my $pass = "zzz";
my $DBH = DBI->connect
(
"dbi:Oracle:$service",
"$user", "$pass",
{
RaiseError => 0,
PrintError => 0,
AutoCommit => 0,
ShowErrorStatement => 0
}
) or die;
my $script = qq(
declare
x number := 1;
begin
insert into xxx values (x);
commit;
end;
);
my $sth = $DBH->prepare($script) or die;
$sth->execute() or die;
$DBH->disconnect();

How can I get a query result in perl?

I tried this script to obtain some info from DB:
use strict;
use warnings;
use DBI;
my $dbh;
my $query= "SELECT table_name FROM user_tables";
my $queryH;
my #tables_names;
my $i;
my $db_pass2 = "*****";
my $db_user2 = "*****";
print "DB connection...\n\n";
#DB connection
$dbh = DBI->connect('DBI:Oracle:host=***********;sid=*****;port=***', $db_user2, $db_pass2);
$queryH = $dbh->prepare($query);
print " ".$queryH->execute()."\n";
print "\n\n";
$queryH->finish();
$dbh->disconnect();
print "\n\n End!!!\n";
Why $queryH->execute() return me 0E0? How can I get a string as result?
I need to get the table name from the db... How can I get it with this script?
execute only executes the query. To retrieve the results you have to use one of the many retrieval methods documented under Statement Handle Methods. For example:
$queryH->execute;
my ( $table_name ) = $queryH->fetchrow_array;
print "result = $table_name\n";

SQL error in YII ::: SQLSTATE[42000]: Syntax error or access violation: 1064

I Wrote a sql code in yii and its giving this error:
CDbCommand failed to execute the SQL statement: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1. The SQL statement executed was: SELECT * FROM users where id in ()
I had no idea why this is happening.........
The code is:
$t = implode(",", $array12);
echo $t;
$sql2 = 'SELECT * FROM users where id in ('. $t. ')';
// echo $sql2; die;
$command = $connection->createCommand($sql2);
$row5 = $command->queryAll();
echo "<pre>";
print_r($row5);
echo "</pre>";
When I echo the sql using echo $sql2 and die() to see the sql, it gives me this:
SELECT * FROM users where id in (44,45)
Now, I used above sql directly in the the as
$sql2 = 'SELECT * FROM users where id in (44,45)';
$command = $connection->createCommand($sql2);
$row5 = $command->queryAll();
and its work perfectly, I do not know what to do with my sql.
You got error when $array12 is empty:
SELECT * FROM users where id in () (check whole sql at the end of a error's message)
You have to check to count elements in $array12:
if (count($array12)) {
$t = implode(",", $array12);
$sql2 = 'SELECT * FROM users where id in ('. $t. ')';
// echo $sql2; die;
$command = $connection->createCommand($sql2);
$row5 = $command->queryAll();
} else {
$row5 = array();
}

How can I avoid the program quitting when Perl's DBI encounters an error preparing a statement?

I'm making a script that goes through a table that contains all the other table names on the database. As it parses each row, it checks to see if the table is empty by
select count(*) cnt from $table_name
Some tables don't exist in the schema anymore and if I do that
select count(*)
directly into the command prompt, it returns the error:
206: The specified table (adm_rpt_rec) is not in the database.
When I run it from inside Perl, it appends this to the beginning:
DBD::Informix::db prepare failed: SQL: -
How can I avoid the program quitting when it tries to prepare this SQL statement?
One option is not to use RaiseError => 1 when constructing $dbh. The other is to wrap the prepare in an eval block.
Just put the calls that may fail in an eval block like this:
for my $table (#tables) {
my $count;
eval {
($count) = $dbi->selectrow_array("select count(*) from $table");
1; #this is here so the block returns true if it succeeds
} or do {
warn $#;
next;
}
print "$table has $count rows\n";
}
Although, in this case, since you are using Informix, you have a much better option: the system catalog tables. Informix keeps metadata like this in a set of system catalog tables. In this case you want systables:
my $sth = $dbh->prepare("select nrows from systables where tabname = ?");
for my $table (#tables) {
$sth->execute($table);
my ($count) = $sth->fetchrow_array;
$sth->finish;
unless (defined $count) {
print "$table does not exist\n";
next;
}
print "$table has $count rows\n";
}
This is faster and safer than count(*) against the table. Full documentation of the system catalog tables can be found in IBM Informix Guide to SQL (warning this is a PDF).
Working code - assuming you have a 'stores' database.
#!/bin/perl -w
use strict;
use DBI;
my $dbh = DBI->connect('dbi:Informix:stores','','',
{RaiseError=>0,PrintError=>1}) or die;
$dbh->do("create temp table tlist(tname varchar(128) not null) with no log");
$dbh->do("insert into tlist values('systables')");
$dbh->do("insert into tlist values('syzygy')");
my $sth = $dbh->prepare("select tname from tlist");
$sth->execute;
while (my($tabname) = $sth->fetchrow_array)
{
my $sql = "select count(*) cnt from $tabname";
my $st2 = $dbh->prepare($sql);
if ($st2)
{
$st2->execute;
if (my($num) = $st2->fetchrow_array)
{
print "$tabname: $num\n";
}
else
{
print "$tabname: error - missing?\n";
}
}
}
$sth->finish;
$dbh->disconnect;
print "Done - finished under control.\n";
Output from running the code above.
systables: 72
DBD::Informix::db prepare failed: SQL: -206: The specified table (syzygy) is not in the database.
ISAM: -111: ISAM error: no record found. at xx.pl line 14.
Done - finished under control.
This printed the error (PrintError=>1), but continued. Change the 1 to 0 and no error appears. The parentheses in the declarations of $tabname and $num are crucial - array context vs scalar context.