perl: executing stored procedure which uses select - oracle

I am running into some errors when trying to run a stored procedure through Perl.
The following statements work fine in sqlDeveloper and return expected results
EXEC alpha.beta.do_something()
This does not generate any output, as expected
Select alpha.beta.get_something(alpha.STRING_ARRAY('xyz')) from tableName
This generates output which is given to us from the DBA guys. It has this format in DB terms: Output Type:Ref Cursor. The cursor has two columns, a and b
When running through Perl, I get errors when running the second statement.
This is the Perl code that I am using:
sub execute_procedure {
my $dbh = shift;
my $procedure = shift;
my $statement = "BEGIN $procedure; END;"
my $sth;
eval {$sth = $dbh->prepare($statement);};
eval {$sth->execute();};
if ($#) {
print "failed";
} else {
print "passed";
}
}
# Call 1
execute_procedure($dbh, "alpha.beta.do_something()")
# Call 2
execute_procedure($dbh, "Select alpha.beta.get_something(alpha.STRING_ARRAY('xyz')) from tableName")
Call 1 works as expected, does not generate any errors
Call 2 results in this error
"Select alpha.beta.get_something(alpha.STRING_ARRAY('xyz')) from
tableName" results in :PLS-00428: an INTO clause is expected in this
SELECT statement (DBD ERROR: error possibly near <> indicator at char
6 in 'BEGIN <>Select alpha.beta.get_result(alpha.STRING_ARRAY('xyz'))
from tableName; END;')
if I remove BEGIN and END from the statement in the execute_procedure function like this:
# my $statement = "BEGIN $procedure; END;";
my $statement = "$procedure";
then it does not generate any error, but it returns a result which I don't know how to parse
my $result = $sth->fetchrow_hashref;
print Dumper($result)
results in:
$VAR1 = {
'alpha.beta.get_result(alpha.STRING_ARRAY(\'xyz\'))' => bless( {}, 'DBI::st' )
};
so, my questions are
What is the right way to execute a stored procedure which uses the SELECT statement? Should it be called with BEGIN $procedure; END; or without the BEGIN/END?
How can I get the actual data from bless( {}, 'DBI::st' )? I have tried to use different fetch options: fetchrow, fetchrow_hashref etc. but I am not able to get the actual data from the object

$VAR1 = { 'alpha.beta.get_result(alpha.STRING_ARRAY(\'xyz\'))' => bless( {}, 'DBI::st' ) };
The value here is a DBI statement handle. It's the same thing you get when you do my $sth = $dbh->prepare(...). It's probably been executed already, so you can just call any of the fetch methods on it.
my $result = $sth->fetchrow_hashref;
while (
my $res = $result->{'alpha.beta.get_result(alpha.STRING_ARRAY(\'xyz\'))'}
->fetchrow_hashref
) {
# ...
}
But that looks horrible, and you might not know what the keys are. So you might want to just iterate over all of them.
my $result = $sth->fetchrow_hashref;
foreach my $key ( keys %${ $result } ) {
print "Processing results of $key\n";
while ( my $res = $result->{$key}->fetchrow_hashref ) {
# do things with the $res
}
}
You just need to figure out a good way to deal with the results. The above loop also works if there is just one key in the hash ref, so you could just use it anyway and hope that there is always just one.

Related

C# Linq to entity (EF Core) create SQL statement "like '1 %'" : Startswith doesn't work when passed a variable

I am trying to recreate the SQL condition of: like '1 %'.
The column in the table contains two sets of values separated by a space. Using StartsWith(glassTag) returns any row where the column starts with '1'. It seems to do a trim on the string before testing the condition. But, if I use StartsWith("1 "), it returns the rows I expect.
What am I doing wrong?
This is a LINQ to Entity question. SqlMethods are not compatible.
the following returns where the Name is: "119 GL-01"
var glassTag = "1 ";
var gItem = await _context.Items.Where(x => x.Name.StartsWith(glassTag) && x.Material.MaterialType.Name.Equals("Glass") && x.JobItems.Any(j => j.Job.Code.Equals(pCode))).ToListAsync();
The code below returns the results I expect
var gItem = await _context.Items.Where(x => x.Name.StartsWith("1 ") && x.Material.MaterialType.Name.Equals("Glass") && x.JobItems.Any(j => j.Job.Code.Equals(pCode))).ToListAsync();
StartsWith simply doesn't work for a variable being passed when looking for spaces at the end.
I have captured the sql being generated by EF.
The code below generates the sql directly below it. When passing a variable to the StartsWith condition, it doesn't use "like". And it creates something that isn't right.
var gItem = _context.Items.Where(x => x.Name.StartsWith(gd.glasstag + " "));
DECLARE #__glassTag_0 nvarchar(4000) = N'1 ';
SELECT [i].[ItemId], [i].[Name]
FROM [Item] AS [i]
WHERE (#__glassTag_0 = N'') OR ([i].[Name] IS NOT NULL AND (LEFT([i].[Name], LEN(#__glassTag_0)) = #__glassTag_0))
The code below generates the sql below it. Note, it uses the "like" condition.
var gItem = _context.Items.Where(x => x.Name.StartsWith("1 "));
SELECT [i].[ItemId], [i].[Name]
FROM [Item] AS [i]
WHERE [i].[Name] IS NOT NULL AND ([i].[Name] LIKE N'1 %')
So, I will be creating a stored procedure to retrieve the data I need. The code will look like this:
var glassTag = gd.glasstag + " ";
var gItem = await _context.Items.FromSqlRaw($"spItemsByGlassTag '{pCode}', '{glassTag}'").ToListAsync();

Concate and produce null

I try to make an exception for my function where is address is null, it did not have to print out the null value.
Here is my function:
if (UserAddress::where('address_2', '=', 'null')) {
$a = UserAddress::select("*", DB::raw("CONCAT(user_addresses.address_1,' ',user_addresses.address_2,' ',user_addresses.city,' ' ,user_addresses.postcode,' ',user_addresses.state) as full_address"))->pluck('full_address');
}else{
$a = UserAddress::select("*", DB::raw("CONCAT(user_addresses.address_1,' ',user_addresses.address_2,' ',user_addresses.city,' ' ,user_addresses.postcode,' ',user_addresses.state) as full_address"))->pluck('full_address');
}
When I die dump $a, the first produce null query.
You cannot use arithmetic comparison operators such as =, <, or <> to test for NULL.
Instead of where('address_2', '=', 'null'), try using the whereNull() function like so:
if (UserAddress::whereNull('address_2')) {
$a = UserAddress::select("*", DB::raw("CONCAT(user_addresses.address_1,' ',user_addresses.address_2,' ',user_addresses.city,' ' ,user_addresses.postcode,' ',user_addresses.state) as full_address"))->pluck('full_address');
}else{
$a = UserAddress::select("*", DB::raw("CONCAT(user_addresses.address_1,' ',user_addresses.address_2,' ',user_addresses.city,' ' ,user_addresses.postcode,' ',user_addresses.state) as full_address"))->pluck('full_address');
}
It can help to enable the query log to see what exactly you are querying on the database, then you can run it directly via phpmyadmin (or whatever you are using). Place DB::enableQueryLog(); before you query, and //dd(DB::getQueryLog()) after the query is executed for troubleshooting.
I solve it. Use empty string with CONCAT_WS
DB::raw("CONCAT_WS('',user_addresses.address_1,...

Dashes Causing SQL Trouble in DBI

I have a SQL query with a WHERE clause that typically has values including a dash as stored in the database as a CHAR(10). When I explicitly call it like in the following:
$sth = $dbh->prepare("SELECT STATUS_CODE FROM MyTable WHERE ACC_TYPE = 'A-50C'");
It works and properly returns my 1 row; however if I do the following:
my $code = 'A-50C';
$sth = $dbh->prepare("SELECT STATUS_CODE FROM MyTable WHERE ACC_TYPE = ?");
$sth->execute($code);
or I do:
my $code = 'A-50C';
$sth = $dbh->prepare("SELECT STATUS_CODE FROM MyTable WHERE ACC_TYPE = ?");
$sth->bind_param(1, $code);
$sth->execute();
The query completes, but I get no results. I suspect it has to do with the dash being interpretted incorrectly, but I can't link it to a Perl issue as I have printed my $code variable using print "My Content: $code\n"; so I can confirm its not being strangely converted. I also tried including a third value for bind_param and if I specify something like ORA_VARCHAR2, SQL_VARCHAR (tried all possibilities) I still get no results. If I change it to the long form i.e. { TYPE => SQL_VARCHAR } it gives me an error of
DBI::st=HASH<0x232a210>->bind_param(...): attribute parameter
'SQL_VARCHAR' is not a hash ref
Lastly, I tried single and double quotes in different ways as well as back ticks to escape the values, but nothing got me the 1 row, only 0. Any ideas? Haven't found anything in documentation or searching. This is oracle for reference.
Code with error checking:
my $dbh = DBI->connect($dsn, $user, $pw, {PrintError => 0, RaiseError => 0})
or die "$DBI::errstr\n";
# my $dbh = DBI->connect(); # connect
my $code = 'A-50C';
print "My Content: $code\n";
$sth = $dbh->prepare( "SELECT COUNT(*) FROM MyTable WHERE CODE = ?" )
or die "Can't prepare SQL statement: $DBI::errstr\n";
$sth->bind_param(1, $code);
$sth->execute() or die "Can't execute SQL statement: $DBI::errstr\n";
my $outfile = 'output.txt';
open OUTFILE, '>', $outfile or die "Unable to open $outfile: $!";
while(my #re = $sth->fetchrow_array) {
print OUTFILE #re,"\n";
}
warn "Data fetching terminated early by error: $DBI::errstr\n"
if $DBI::err;
close OUTFILE;
$sth->finish();
$dbh->disconnect();
I ran a trace and got back:
-> bind_param for DBD::Oracle::st (DBI::st=HASH(0x22fbcc0)~0x3bcf48 2 'A-50C' HASH(0x22fbac8)) thr#3b66c8
dbd_bind_ph(1): bind :p2 <== 'A-50C' (type 0 (DEFAULT (varchar)), attribs: HASH(0x22fbac8))
dbd_rebind_ph_char() (1): bind :p2 <== 'A-50C' (size 5/16/0, ptype 4(VARCHAR), otype 1 )
dbd_rebind_ph_char() (2): bind :p2 <== ''A-50' (size 5/16, otype 1(VARCHAR), indp 0, at_exec 1)
bind :p2 as ftype 1 (VARCHAR)
dbd_rebind_ph(): bind :p2 <== 'A-50C' (in, not-utf8, csid 178->0->178, ftype 1 (VARCHAR), csform 0(0)->0(0), maxlen 16, maxdata_size 0)
Your problem is likely a result of comparing CHAR and VARCHAR data together.
The CHAR data type is notorious (and should be avoided), because it stores data in fixed-length format. It should never be used for holding varying-length data. In your case, data stored in the ACC_TYPE column will always take up 10 characters of storage. When you store a value whose length is less than the size of the column, like A-50C, the database will implicitly pad the string up to 10 characters, so the actual value stored becomes A-50C_____ (where _ represents a whitespace).
Your first query works because when you use a hard-code literal, Oracle will automatically right-pad the value for you (A-50C -> A-50C_____). However, in your second query where you use bind variables, you're comparing a VARCHAR against a CHAR and no auto-padding will happen.
As a quick fix to the problem, you could add right-padding to the query:
SELECT STATUS_CODE FROM MyTable WHERE ACC_TYPE = rpad(?, 10)
A long-term solution would be to avoid using the CHAR data type in your table definitions and switch to VARCHAR2 instead.
As your DBI_TRACE revealed, the ACC_TYPE column is CHAR(10) but the bound parameter is understood as VARCHAR.
When comparing CHAR, NCHAR, or literal strings to one another, trailing blanks are effectively ignored. (Remember, CHAR(10) means that ACC_TYPE values are padded to 10 characters long.) Thus, 'A ' and 'A', as CHARs, compare equal. When comparing with the VARCHAR family, however, trailing blanks become significant, and 'A ' no longer equals 'A' if one is a VARCHAR variant.
You can confirm in sqlplus or via quick DBI query:
SELECT COUNT(1) FROM DUAL WHERE CAST('A' AS CHAR(2)) = 'A'; -- or CAST AS CHAR(whatever)
SELECT COUNT(1) FROM DUAL WHERE CAST('A' AS CHAR(2)) = CAST('A' AS VARCHAR(1));
(Oracle terms these blank-padded and nonpadded comparison semantics, since the actual behavior per the ANSI-92 spec is to pad the shorter CHAR or literal to the length of the longer and then compare. The effective behavior, whatever the name, is that one ignores trailing blanks and the other does not.)
As #MickMnemonic suggested, RPAD()ing the bound value will work, or, better, altering the column type to VARCHAR. You could also CAST(? AS CHAR(10)). TRIM(TRAILING FROM ACC_TYPE) would work, too, at the cost of ignoring any index on that column.

Writing 100 rows form oracle database using powershell

I have an problem using powershell .
first i would like to explain what the sitiation is :
I have an Oracle database running with an table NAMES . In the table i got about 10000 rows with data . I would like to to count them til 100 rows and and then "echo " them on my powershell prompt here is where the problem comes in because i can count the rows using the following script:
$query = “SELECT * FROM NAMES"
$command = $con.CreateCommand()
$command.CommandText = $query
$result = $command.ExecuteReader()
$test= $result|Measure-Object
$i=$test.Count
The number that returns is the correct number but then it goes wrong because when i want to use an foreach loop i cant get the names from my table
Here is wat i got maybey it helps
foreach ($Row in $query.Tables[0].Rows)
{
write-host "value is : $($Row[0])"
}
hope someone finds an answer
You are missing the strict mode: Set-StrictMode -Version latest. By setting it, you'd get much more informative an error message:
$query = "SELECT * FROM NAMES"
foreach ($Row in $query.Tables[0].Rows) { ... }
Property 'Tables' cannot be found on this object. Make sure that it exists.
+ foreach ($Row in $query.Tables[0].Rows) { ... }
The $query variable doesn't contain member Tables, so attempting to read it is futile. It would seem likely that the $result variable contains the Tables member. That depends on the data provider you are using and what's missing on the code sample.

Codeigniter Active Record return type

I tried to get a umat_id with this SQL query :
SELECT umat_id FROM msumat WHERE nama = $nama
I converted this SQL query into CI's Active Record :
$this->db->select('umat_id');
$terdaftar = $this->db->get_where('msumat', array('nama' => $nama));
So this query should return a string (example : "John").
But I got an error when I tried to echo it :
Object of class CI_DB_mysql_result could not be converted to string
I have tried something like this : echo (string)$terdaftar;, but it's not working.
All I want is to echo "John"
EDIT
Just said I want to insert "John" into a variable. How to do that?
$john = ????
As some of the users already pointed the solution, I'm only explaining why you did get this error so you can understand better the querying results that codeigniter gives.
This error:
But I got an error when I tried to echo it : Object of class
CI_DB_mysql_result could not be converted to string
Happens because you were trying to echo an object.
This piece of code
$terdaftar = $this->db->get_where('msumat', array('nama' => $nama));
Will return an object, this object will have information about the query you've done.
With this object you can get the result(rows) as objects doing this:
$results = $terdaftar->result();
Or you if you feel more comfortable with arrays you can return the results(rows) as an array doing this:
$results = $terdaftar->result_array();
You can also get the number of results doing this:
$number_results = $terdaftar->num_rows()
And this is just an example you can read more about the results here
http://ellislab.com/codeigniter/user-guide/database/results.html
EDIT
A better explanation: imagine that we use the result_array() function to get the result in a pure array format:
$results = $terdaftar->result_array();
Now your variable $results is an array, to iterate through it and get the data you want you'll do something like this:
foreach ($results as $key => $row) {
//the row variable will have each row of your database returned by your query
//so if you want to access a field from that row,
//let's say for example the name field. You would do something like this
if($row['name']=='John')
echo $row['name'];
}
Try:
$this->db->select('umat_id');
$terdaftar = $this->db->get_where('msumat', array('nama' => $nama));
foreach ($terdaftar->result() as $row)
{
echo $row->umat_id;
}
Read the documentation for more information.
Try this:
$this->db->select('umat_id');
$terdaftar = $this->db->get_where('msumat', array('nama' => $nama));
$row = $terdaftar->row_array();
$your_variable = $row['umat_id']; /*Here comes your john*/

Resources