how to reference function parameter in query? - oracle

In the declared function definition I want to use value of function parameter item_account_id in the select query's WHERE clause .
CREATE OR REPLACE
FUNCTION UPDATE_CURRENT_BALANCE( item_account_id IN item_account.item_account_id%TYPE)
RETURN boolean
AS
BEGIN
if item_data_table_id = 10 then
select current_balance_curr_id
from BANK_ACCOUNT
where item_account_id = item_account_id;
end if;
RETURN true;
END UPDATE_CURRENT_BALANCE;

You have a scoping issue because the parameter name is the same as the column. The way Oracle's name resolution works, both these item_account_id = item_account_id will identify the table column. Even it we added a table alias and used it in the one side of the equality operation Oracle will still evaluate it as 1 = 1.
The solution is simple: rename the parameter. Using a prefix is popular:
CREATE OR REPLACE FUNCTION UPDATE_CURRENT_BALANCE
( p_item_account_id IN item_account.item_account_id%TYPE)
RETURN boolean
AS
BEGIN
if item_data_table_id = 10 then -- the posted code has no source for this? perhaps it's another parameter?
select current_balance_curr_id
from BANK_ACCOUNT
where item_account_id = p_item_account_id;
end if;
RETURN true;
END UPDATE_CURRENT_BALANCE;
I presume item_data_table_id is another parameter which got lost in transcription. You should prefix that too: consistency is a good thing in naming conventions.

Related

Oracle: Update from within procedure not working

In my Oracle PL/SQL procedure I am trying to update a row like this:
UPDATE personal p
SET p.surname = surname, p.name = name, p."alter" = alter, p.sex = sex, p.jobcode = jobcode, p.year_wage = month_wage * 12
WHERE p.personalnr = personalnr;
COMMIT;
I have added these two statements right after the commit to confirm the code is reached and executed with the right arguments (e.g. here I want to change the name):
DBMS_OUTPUT.put_line('updated ' || name);
DBMS_OUTPUT.put_line('personalnr ' || personalnr);
Now this update-statement is part of a procedure that is called from within another procedure.
However, the changes are not applied and the name will remain the same even tho the update was executed. I have tried to use an exception-handler as well and there doesn't seem to be any exception happening. I can confirm that the WHERE-clause is as intendet. There is one record that matches the predicate.
Now the strange thing:
When I change the code to the example below, an update happens. However it updates every record and not only the one with the right personalnr. Again: the routine is called only once with one personalnr that matches only one entry in the table.
UPDATE personal p
SET p.name = 'test'
WHERE p.personalnr = personalnr;
COMMIT;
It is working, but it's updating all rows in the table (or at least, those where personalnr is not null), not just the one you expect.
From the documentation:
If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence.
You have a PL/SQL variable that has the same name as a column. When you do
where p.personalnr = personalnr
you are really doing:
where p.personalnr = p.personalnr
and the same thing happens in the set part; SET p.surname = surname updates the column value to whatever value it had before, not the PL/SQL variable's value. So it looks like the update didn't happen- it actually did, but because everything was set to the same as it's original value it doesn't look like anything happened. (Except - all rows will now have the same year_wage value...)
You can either prefix your variables with the procedure name:
where p.personalnr = my_proc.personalnr
or change the variable names so they don't conflict; it's common to use a short prefix, e.g. l_ for a local variable, or p_ for a passed-in parameter, etc.
where p.personalnr = l_personalnr
Remember to do that for the set part too, or your update still won't appear to do anything.
UPDATE personal p
SET p.surname = l_surname, p.name = l_name, p."alter" = l_alter,
p.sex = l_sex, p.jobcode = l_jobcode, p.year_wage = l_month_wage * 12
WHERE p.personalnr = l_personalnr;
You need to change the parameter name something other than the table's column name.
UPDATE personal p
SET p.name = 'test'
WHERE p.personalnr = personally;
-- here condition is column_name = column_name
-- This will be true for all the records of the table
Change personalnr --> p_personalnr and it will work for you

How to find a last inserted value from procedure in codeigniter

I am trying get a last inserted value but I'm getting the error:
"Incorrect number of arguments for PROCEDURE hssl.insert_brouchermaster; expected 2, got 3"
Procedure
BEGIN
INSERT INTO `brouchermaster`(`broucher_name`,`order_number`) VALUES (brouchername,orderno,NOW());
SET #inserted_instance_id = LAST_INSERT_ID();
END
Model:
$insert_result = $this->db->query("CALL insert_brouchermaster('".$broucher_name."', '".$order_number."', #inserted_instance_id)");
$last_insert_query = $this->db->query("SELECT #inserted_instance_id");
The CI function db->query('your query here') returns either true or false so it isn't going to return #inserted_instance_id for you. I think you'll need to change
$insert_result = $this->db->query("CALL insert_brouchermaster('".$broucher_name."', '".$order_number."', #inserted_instance_id)");
to this
$this->db->query("CALL insert_brouchermaster('".$broucher_name."', '".$order_number."'");
//and add this line
$new_id = $this->db->insert_id();
That said, I've not had not used stored procedures very often, and not for awhile, so my syntax may be faulty. Never the less, use insert_id() to get what your after.
Documentation on the function here
Also see this SO question

If there a way to compile (not execute) a query syntaxicly and semanticly with JDBC?

I have numerous queries which contains syntax error (and without unit test, but that's another problem) and I'd like to massively check if there are no errors.
For that, I've done the following at first:
String q = ...; // some query
try (PreparedStatement stmt = connection.prepareStatement(q)) {
final ParameterMetaData pmd = stmt.getParameterMetaData();
for (int i = 1; i <= pmd.getParameterCount(); ++i) {
stmt.setNull(i, java.sql.Types.NULL);
}
stmt.execute();
} catch (SQLException e) {
...
} finally {
connection.rollback();
}
It works, but then I came into such errors: http://www.oracle-error.com/11g/ORA-30081.html
Basically, somewhere in my query, I have that:
select *
from table T
where id = ? or ( ? - INTERVAL '1' DAY ) between date_start and date_end
If I execute the same query, replacing ? by NULL, in TOAD, I've got the same error.
The ParameterMetaData does not help either, because it don't store the information I want (eg: what Oracle expect as parameter).
Is there some solution to compile the query syntactically and semantically (to check for missing columns, etc) ignoring parameters along the way?
As of now, I am replacing the ? by NULL, except if after the "?" I found some date stuff, where I use sysdate.
eg:
select *
from table T
where id = NULL or ( sysdate - INTERVAL '1' DAY ) between date_start and date_end
Not directly through JDBC, but you can do it indirectly; heavily inspired by this, you can do:
String q = ...; // some query
try (PreparedStatement stmt = connection.prepareStatement("declare c integer;
begin
c := dbms_sql.open_cursor;
dbms_sql.parse(c,?,dbms_sql.native);
dbms_sql.close_cursor(c);
end;")) {
stmt.setString(1, q.replace("?", ":b0"));
stmt.execute();
} catch (SQLException e) {
...
}
The statement you prepare is now an anonymous block, and the single bind variable is now your original query to validate. You don't need to know anything about the query's parameters. The replace converts the JDBC ? placeholders to generic :b0 bind variables so the parser doesn't object to them.
You could be more advanced and replace each placeholder with a different bind variable (:b0, :b1) etc. but I don't think it will generally be necessary. This crude replace would also potentially modify string literals though, of course, which may be something you need to consider; a regular expression approach would be more robust.
One other option to try might be to use the EXPLAIN PLAN statement available in Oracle and in some other DBMSes (possibly in a slightly different form). Prepend 'EXPLAIN PLAN FOR ' to your statement and execute() (no need to prepare). The original statement won't actually run, but it will be parsed and compiled, and you don't need to bind any parameters.
Proof.
It may still choke on untyped parameter markers in some cases though.

how to call oracle stored procedure in jpa2.0 which returns integer as out variable

I have written one stored procedure which will return integer value. but am not able to get the count in jpa. can any one suggest me the way to call stored procedure and get the return out value into some variable in java using jpa.
int count = 0;
String id = "m123"
count = getEm().createNativeQuery("call sample_procedure(?,?)")
.setParameter(1, id)
.setParameter(2, count)
.executeUpdate();
stored procedure: I have some logic in procedure and inside loop I was incrementing the count.
create or replace
PROCEDURE sample_procedure(
id IN VARCHAR,
count OUT NUMBER)
IS
........
BEGIN
.....
LOOP
---------
count := count + 1;
Looks similar to this answer
Options which I tried:
Tried with curly braces before the call statement.
Query q = entitymanager.createNativeQuery("{call sample_procedure(?,?)}").setParameter(1, inparam).setParameter(2, outparam);
q.executeUpdate();
It did not work. Did not return the output to java class.
Tried with this:
Query q = entitymanager.createNativeQuery("{call sample_procedure(?,?)}", Integer.class).setParameter(1, inparam).setParameter(2, outparam);
q.executeUpdate();
It did not work either. Did not return the output to java class.
Tried with this as well:
Query q = entitymanager.createNativeQuery("{call sample_procedure(?,?)}").setParameter(1, inparam).setParameter(2, outparam);
q.getSingleResult();
This also threw an exception: Cannot perform an update or delete operation on select query: "{call sample_procedure(?,?)}".
I believe it is not possible to do it like this as mentioned by Sean Patrick Floyd in the answer link i have provided above.(Note: I do not have the book :)) If you need to process your out parameter and if that is the reason you need it, you need to handle it logically. Seems its not possible in jpa2.0
I have some logic in procedure and inside loop I was incrementing the count.
First, in you given use case, count has to be declared INOUT (both input and output).
PROCEDURE sample_procedure(
id IN VARCHAR,
count INOUT NUMBER)
Then:
For JPA < 2.1 don't have support for getting back values from OUT or INOUT parameters. So, you are probably stuck if you need to stay with that version.
JPA >= 2.1 has explicit support for calling stored procedure using the EntityManager.html.createStoredProcedureQuery method:
count = getEm().createStoredProcedureQuery("sample_procedure")
.registerStoredProcedureParameter(1, String.class, ParameterMode.IN)
.registerStoredProcedureParameter(2, Integer.class, ParameterMode.INOUT)
.setParameter(1, id)
.setParameter(2, count);
storedProcedure.execute();
int count = (Integer)storedProcedure.getOutputParameterValue(2);
Untested. Beware of typos !

what are the OleDbTypes associated with Oracle Number and varchar2 when calling a function

I'm trying to map OleDb parameters to an Oracle Function. I was able to do this using the System.Data.Oracle namespace but then found that this is depricated, so I thought i would re-write it as OldDb to avoid installing the Oracle Provider.
I have defined the following oracle function as an example:
create function GetImagePath (AIRSNumber in number)
return varchar2
is
begin
return '\\aiimg524\images\Ofndrtrk\2010\01\0kvrv1p000lcs74j';
end;
and I'm calling it using the following code:
using (var command = new OleDbCommand())
{
command.Connection = con;
command.CommandText = ConfigurationManager.AppSettings[OTRAK_PHOTO_FUNC];
command.CommandType = CommandType.StoredProcedure;
string parm = ConfigurationManager.AppSettings[OTRAK_PHOTO_PARM];
command.Parameters.Add(parm, OleDbType.Decimal); // maps to oracle Number
command.Parameters[parm].Direction = ParameterDirection.Input;
command.Parameters[parm].Value = airsNumber;
command.Parameters.Add(RETURN_VALUE, OleDbType.Variant); // maps to Oracle varchar2
command.Parameters[RETURN_VALUE].Direction = ParameterDirection.ReturnValue;
try
{
con.Open();
command.ExecuteNonQuery();
path = command.Parameters[RETURN_VALUE].Value.ToString();
}
I tried a bunch of different OleDB types for the parameter and the return value. the current attempt is from a mapping table i found on the web that said number = decimal and varchar2 = variant. I'm about to try every permutation of types in the enum and wanted to ask for help. the not so useful error message i get is:
[System.Data.OleDb.OleDbException] = {"ORA-06550: line 1, column 7:\nPLS-00306: wrong number or types of arguments in call to 'GETIMAGEPATH'\nORA-06550: line 1, column 7:\nPL/SQL: Statement ignored"}
This actually had nothing to do with the type of the parameters but the order. Using the OleDb provider for Oracle does not respect the names of the parameters in the parameter collection but rather the order that the parameters are added. Wwhen calling an oracle function, the return value is a free parameter that must be declared first. by adding my return value parameter and then the actual function parameter things started working.
using the command.Parameters.AddWithValue(parm, value) also simplifies things a bit.

Resources