How do you assign a sequence value to a variable? - sql

I need to assign a sequence value to a variable for use later after the sequence value has been incremented. I've tried this but it gives an error:
variable imageID number;
select SEQ_IMAGE_ID.CURRVAL into :imageID from dual;
select * from IMAGES where IMAGE_ID = :imageID;
Error starting at line 2 in command:
select SEQ_IMAGE_ID.CURRVAL into :imageID from dual
Error report:
SQL Error: ORA-01006: bind variable does not exist
01006. 00000 - "bind variable does not exist"
I have triple checked that the sequence name is correct, any ideas?

You seem to be doing this in SQL*Plus or SQL Developer, from the variable declaration. You need to do the assignment in a PL/SQL block, either with an explicit begin/end or with the exec call that hides that:
variable imageID number;
exec select SEQ_IMAGE_ID.CURRVAL into :imageID from dual;
select * from IMAGES where IMAGE_ID = :imageID;
If you're using 11g you don't need to select, you can just assign:
variable imageID number;
exec :image_id := SEQ_IMAGE_ID.CURRVAL;
select * from IMAGES where IMAGE_ID = :imageID;
You could also use a substitution variable:
column tmp_imageid new_value image_id;
select SEQ_IMAGE_ID.CURRVAL as tmp_imageID from dual;
select * from IMAGES where IMAGE_ID = &imageID;
Note the change from : to indicate a bind variable, to & to indicate a substitution variable.

In PL/SQL, the variable needs to be declared, something like this:
declare
V_IMAGEID;
begin
select SEQ_IMAGE_ID.CURRVAL into V_IMAGEID from dual;
select * /*into ... */ from IMAGES where IMAGE_ID = V_IMAGEID;
end;
If you're using bind variables, the variable must be bound. The error message indicates this isn't the case. How exactly to bind variables depends on the language/situation. Make sure you use the right direction when binding variables. In the first (dual) query, you will need an out parameter. You may need to specify this.

just remove ':' before :imageId. If you are in a trigger use :new.imageid
the word variable should be removed as well
p.s. I mean anonymous block surely.

Related

ORACLE PL/SQL Variable Function Scope - Need Explanation

I just tripped over an answer to a problem I was having with a PL/SQL variable not being recognized by a function and I was hoping someone could explain to me why my solution worked and what is happening "underneath the hood".
Background
As part of an optimization project, I am trying to collect metrics on individual SQL scripts within a Stored Procedure. The Stored Proc that I am dissecting has an In-type date parameter that I need to define in order to run each individual SQL Script:
CREATE OR REPLACE myStoredProc (DATE_IN DATE, ERROR_OUT OUT VARCHAR2)
IS
BEGIN
--Truncate Temp Tables
--6 Individual SQL Scripts
EXCEPTION
--Error Handling
END;
To run each script individually, I decided to just drop each SQL statement into a PL/SQL block and feed the DATE_IN parameter in as a variable:
DECLARE
DATE_IN DATE := TO_DATE('16-JUL-2014','DD-MON-RR');
BEGIN
--Place individual script here
END;
The Problem
This approach worked fine for a couple of the queries that referred to this DATE_IN variable but one query with a reference to an outside function which takes DATE_IN as a parameter started throwing an ORA-00904 error:
DECLARE
DATE_IN DATE := TO_DATE('16-JUL-2014','DD-MON-RR');
BEGIN
insert into temp_table
SELECT table1.field1,
table1.field2,
table2.fieldA,
MyFunction(table1.field1, DATE_IN) --This was the problem line
FROM
table1,
table2
WHERE EXISTS (inner query)
AND table1.keys = table2.keys
AND table2.date <= DATE_IN
END;
Solution
At the advice of another Developer, I was able to get around this error by adding a colon (:) in front of the DATE_IN variable that I was passing into the function so that the problem line read MyFunction(table1.field1, :DATE_IN). As soon as I did that, my error disappeared and I was able to run the query without issue.
I was happy for the result but the other Developer wasn't able to explain why it was needed, only that it was necessary to call any functions or other stored procs from a PL/SQL statement. I assume this has something to do with scope but I would like to get a better explanation as to why this colon was necessary for the function to see the variable.
Questions
I've tried to do a little research looking over Oracle documentation on parameters, variables, binding/declaring and constants but my research has only given me more questions:
After reading up on variables, I now question if that is the correct term for what I have been using (since I didn't actually use the VARIABLE command and I'm passing in a date - which is not an allowable data type). If my DATE_IN DATE := statement is not a variable, then what is it?
Why were the rest of my references to DATE_IN recognized by the compiler but passing the value to the function was out of scope?
What exactly is the colon (:) doing here? Is this turning that into a bind variable?
Thanks in advance. I appreciate any guidance you can provide!
----------------------------------EDIT--------------------------------------
I was asked to provide additional information. My Db version is 11G, 11.2.0.2.0. The query that I was able to reproduce this error is below.
DECLARE
EXTRACT_DT_IN DATE := TO_DATE('16-JUL-2014','DD-MON-RR');
BEGIN
--This begins the pre-optimized query that I'm testing
insert into AELI_COV_TMP_2_OPT
SELECT /*+ ordered use_nl(CM MAMT) INDEX (CM CSMB_CSMB2_UK) INDEX (MAMT (MBAM_CSMB_FK_I) */
CM.CASE_MBR_KEY
,CM.pyrl_no
,MAMT.AMT
,MAMT.FREQ_CD
,MAMT.HOURS
,aeli$cov_pdtodt(CM.CASE_MBR_KEY, EXTRACT_DT_IN)
FROM
CASE_MEMBERS CM
,MEMBER_AMOUNTS MAMT
WHERE EXISTS (select /*+ INDEX(SDEF SLRY_BCAT_FK_I) */
'x'
from SALARY_DEF SDEF
where SDEF.CASE_KEY = CM.CASE_KEY
AND SDEF.TYP_CD = '04'
AND SDEF.SLRY_KEY = MAMT.SLRY_KEY)
AND CM.CASE_MBR_KEY = MAMT.CASE_MBR_KEY
AND MAMT.STAT_CD = '00'
AND (MAMT.xpir_dt is null or MAMT.xpir_dt > EXTRACT_DT_IN)
AND MAMT.eff_dt <= EXTRACT_DT_IN;
--This ends the pre-optimized query that I'm testing
END;
Here is the error I'm encountering when trying to run an Explain Plan on this statement. I am able to get past this error if I remove reference to line 13 or I add a colon (:) to the EXTRACT_DT_IN on that line.
----------------------EDIT 2-------------------
Here is the function signature of aeli$.cov_pdtodt. (I've replaced the owner for security reasons).
CREATE OR REPLACE function __owner__.aeli$cov_pdtodt
(CASE_MBR_KEY_IN IN NUMBER, EXTRACT_EFF_DT_IN DATE)
RETURN DATE IS
PDTODT DATE;
Your anonymous block is fine as it is, as long as you execute the whole block. If you try to execute just the insert, or its select, as a standalone command then it will indeed fail with ORA-00904.
That isn't quite a scope problem, it's a context problem. You're trying to refer to a PL/SQL variable in an SQL context, and that is never going to work.
In a PL/SQL context this would work:
declare
some_var dual.dummy%type := 'X';
begin
insert into some_table
select dummy from dual where dummy = some_var;
end;
/
... because the insert has access to the PL/SQL some_var.
In an SQL context this will error:
select * from dual where dummy = some_var;
... because it's looking for a column called SOME_VAR, and there isn't one.
If you do this instead:
select * from dual where dummy = :some_var;
... the some_var is now a client-managed bind variable. If you execute that you'll either be prompted for the bind value, or given a not-all-variables-bound error, or bind-variable-not-declared, or similar, depending on your client.
If you only do an explain plan of that though, e.g. with
set auto trace traceonly explain
select * from dual where dummy = :some_var;
... then the bind variable doesn't necessarily have to be populated for the plan to be calculated. Some clients may still complain and want a bind value, but the parser would be OK with it - enough to produce a plan anyway. Though not able to take advantage of bind variable peeking or histograms etc.
For example, SQL Developer happily produces a plan for your original sample query if both references are turned into bind variables, just the insert ... part of the block is selected, and you press Explain Plan (F10).
I'm not sure what you read, but you're mixed up on a few things here.
Your DATE_IN is a variable. You don't need to type 'VARIABLE' anywhere to declare a variable, all you need is the name of the variable and the datatype.
All of the below are legitimate variables in PL/SQL (although poorly named).
variable_1 NUMBER;
variable_2 VARCHAR2(100);
variable_3 DATE;
It's hard to tell what you're doing in your code without seeing it all. Do you have two DATE_IN variables declared within the same block? Is DATE_IN the name of a column in your table?
If you have a column named DATE_IN in table1 or table2, that's likely your problem. Oracle doesn't know if you want to use your variable or your column, and it will always default to the column name. Your function would be expecting a DATE and receiving a column, hence the error.

Trouble using Oracle SQL variable in where clause

I'm trying to write a script to help assist with the numerous "What's happened to this actionlist" type calls, but i'm coming up against a brick wall doing some of the most simplest operations.
I'm trying to declare a variable and use it in the where clause of a query. I've trimmed everything irrelevant out of the query, in an attempt to get this core functionality to work.
The id is usually an 18 digit number, but does occasionally contain alpha-numerics, hence why it is a varchar. The column I'm trying to compare it to is an 18 byte varchar field.
declare
id_to_check VARCHAR(18);
begin
id_to_check := '549576015500000109';
select
txn_timestamp, exception_type
from cut.event
where irn_id = id_to_check;
end;
Every time it throws an error: 'an INTO clause is expected in this select statement'. I understand how INTO's work, i.e. if I wanted to assign the result of a select to the variable, but I don't understand how that would apply in this instance as I'm not assigning the result of the query to a variable?
The other frustrating thing is I'm actually following documentation on docs.oracle.com.
http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/overview.htm#LNPLS001.
I've also looked at various google results, but I can't figure out how to do a comparison with the variable without first selecting something into it, but as you can see, I can't select into because I only need it for comparison reasons?
Kind Regards,
Ian
Raphaƫl Althaus's answer applies if you are doing something in PL/SQL, but I'm not sure that's actually what you want. It sounds like you want a plain SQL query, but you want to be able to pass it a value? Assuming you're running this in SQL*Plus or SQL Developer you have a few ways to declare and use a fixed value. (Similar methods will be available in other clients; PL/SQL developer seems to like both of these, at least in the command window, but not sure about Toad or anything else).
1) Using a substitution variable:
define id_to_check = 549576015500000109
select txn_timestamp, exception_type
from cut.event
where irn_id = '&id_to_check';
2) Using a bind variable:
variable id_to_check varchar2(18)
exec :id_to_check := '549576015500000109';
select txn_timestamp, exception_type
from cut.event
where irn_id = :id_to_check;
The exec is shorthand for a small anonymous PL/SQL block, but apart from step of assigning the value to the variable, it's plain SQL, so the query doesn't need to be wrapped in a begin ... end, and you don't have to worry about selecting into variables.
In either case you can have the ID value passed as an argument, or ask for it as part of the script, when the script it run. For example, you could write a query.sql script:
define id_to_check = &1
select txn_timestamp, exception_type
from cut.event
where irn_id = '&id_to_check';
... and run it with:
sqlplus -l -s <user>/<password> #query 549576015500000109
... or as:
accept id_to_check prompt 'Enter the ID to check: '
select txn_timestamp, exception_type
from cut.event
where irn_id = '&id_to_check';
... and have the user prompted to enter the ID at run-time.
The question is not "Do you want to put the result into a variable".
In the "body" of an Oracle procedure, you just can't use a SELECT without an INTO clause.
(In fact you can have, but in a cursor).
so
declare
id_to_check VARCHAR(18);
event_timestamp cut.event.txn_timestamp%TYPE;
event_exception cut.event.exception_type%TYPE;
begin
id_to_check := '549576015500000109';
select
txn_timestamp, exception_type
INTO event_timestamp, event_exception
from cut.event
where irn_id = id_to_check;
end;
Caution : if no result is found, this will raise a NO_DATA_FOUND exception.
You can manage it with
begin
id_to_check := '549576015500000109';
BEGIN
select
txn_timestamp, exception_type
INTO event_timestamp, event_exception
from cut.event
where irn_id = id_to_check;
EXCEPTION WHEN NO_DATA_FOUND THEN
--...something
END
end
Your real problem is NOT about using variable (you got that right), but about returning cursors from PL/SQL blocks.
You seem to believe that a bare select at the end of a procedure should somehow make it return the results or at least display them. While some databases work like that - Oracle does not. If you want any data to leave a block of any kind (like function or procedure), you have to pass them explicitly. Usually this is done by using an in parameter of a cursor ref type.
As you can see, the select without into would not make sense in Oracle. Even if you managed to make it work and select the data, you would not be able to access the results.

Can't declare variable in Firebird 2.5, why?

I have a one line query:
DECLARE VARIABLE var_SecondsOfTime INTEGER;
But after running the query I am getting this message:
Engine Error (code = 335544569):
Dynamic SQL Error. SQL error code =
-104. Token unknown - line 1, column 9. VARIABLE.
SQL Error (code = -104): Invalid
token.
I've looked everywhere on the Internet and all examples showing the same declaration style which I am using.
What is wrong?
Firebird 2.5 supports execution of code blocks surrounded by a execute block statement, try this:
set term ^ ;
EXECUTE BLOCK
AS
DECLARE VARIABLE var_SecondsOfTime INTEGER;
BEGIN
SELECT 1 from RDB$DATABASE into var_SecondsOfTime ;
END
^
set term ; ^
I issued the select because I'm pretty sure it is not possible to execute an empty block, try this by yourself removing the select.
Edit
My original select was invalid for a block, I added the into clause to collect the result. I never used firebird maestro, but it now works perfectly on isql, as shown.
Try this:
set term ^ ;
EXECUTE BLOCK
AS
DECLARE VARIABLE var_SecondsOfTime INTEGER;
BEGIN
SELECT 1 from RDB$DATABASE into :var_SecondsOfTime ;
END^
set term ;^
The second set term needs the semi colon before the carat.

accessing a bind variable in sqlplus

In the following example,
variable recordId number;
BEGIN
SELECT MAX(recordvalue)
INTO recordId
FROM sometable;
END;
PRINT recordid;
SELECT *
FROM someothertable
WHERE recordkey = &recordId;
The select statement on the last line cannot access the value of recordId.
I know i can access recordId inside the pl/sql block using :recordId but is there a way to access recordId in a sql statement that is not in a pl/sql block? (like in the last line).
You can use bind variables in SQL*Plus too, still as :recordId. The & version will prompt for a value, and has no direct relationship to the variable version.
variable recordId number;
BEGIN
SELECT MAX(recordvalue)
INTO :recordId
FROM sometable;
END;
/
PRINT recordid;
SELECT *
FROM someothertable
WHERE recordkey = :recordId;
The slightly more common way to assign values to bind variables is with exec :recordId := value;, but exec is really just shorthand for an anonymous block anyway.
Not sure why you'd want to mix and match like this though. If the intention is to use the result of one query in a later one, you could try new_value instead:
column x_val new_value y_val; -- could also noprint if you do not need to see the intermediate value
SELECT MAX(recordvalue) x_val
FROM sometable;
SELECT *
FROM someothertable
WHERE recordkey = &y_val;

Oracle: select into variable being used in where clause

Can I change the value of a variable by using a select into with the variable's original value as part of the where clause in the select statement?
EI would the following code work as expected:
declare
v_id number;
v_table number; --set elsewhere in code to either 1 or 2
begin
select id into v_id from table_1 where name = 'John Smith';
if(v_table = 2) then
select id into v_id from table_2 where fk_id = v_id;
end if;
end;
Should work. Have you tried it? Any issues?
After parsing your select statements should have bind variables where your v_id is. The substitution is made when the statement is actually executed.
Edit:
Unless you're sticking constants into your queries, Oracle will always parse them into statements with bind variables - it enables the DBMS to reuse the same basic query with multiple values without reparsing the statement - a huge performance gain. The whole idea of a bind variable is runtime substitution of values into a parsed query. Think of it this way: in order to process a query, all of the values need to be known. You send them to the engine, Oracle does it's work, and returns a result. It's a serial process with no way for the output value to step on the input one.