I can't for the life of me find out to get the behavior I want:
old:insert into build_pc
values (&&buildA, 35,40,32,29,26,22,22,null,13,11,7,1,999)
new:insert into build_pc
values (build_id_seq.nextval, 35,40,32,29,26,22,22,null,13,11,7,1,999)
1 rows inserted.
As part of an SQL transaction I'm trying to save a variable, moreover an integer ID number, so that it can be entered in a few places across the transaction. The problem is SQLDev is being too clever and converting back to he nextval function each time instead of just storing an int.
Google says use set, but that throws this:
SQLPLUS Command Skipped: set buildA = build_id_seq.nextval
As I understand, you want to use a bind variable in SQL Plus. Please, try this:
variable buildA NUMBER;
exec select build_id_seq.nextval into :buildA from dual;
insert into build_pc values (:buildA, 35,40,32,29,26,22,22,null,13,11,7,1,999);
If you use 11g you do not need to SELECT the value of sequence, you can use:
variable buildA NUMBER;
exec :buildA := build_id_seq.nextval;
insert into build_pc values (:buildA, 35,40,32,29,26,22,22,null,13,11,7,1,999);
Hope this helps!
You should be using define rather than set for a substitution variable:
define buildA = build_id_seq.nextval
... but it will be substituted verbatim wherever you reference &buildA, incrementing the sequence each time.
You could use a bind variable or the new_value syntax, but you probably just want to refer to build_id_seq.nextval the first time you use it, and build_id_seq.currval on all the subsequent references, rather than trying to store it yourself.
As the documentation says:
CURRVAL: Returns the current value of a sequence
NEXTVAL: Increments the sequence and returns the next value
So when you call nextval the sequence is incremented, and subsequent calls to currval in the same session get the same value, without incrementing again.
This is useful for creating parent/child records, for example, where you use nextval to create the primary key value and then currval to use the same value as a foreign key in child records.
Related
I want to know if I can create a PL/SQL procedure that the number of parameters and their types changes.
For example procedure p1.
I can use it like this
p1 (param1, param2,......., param n);
i want to pass table name and data in procedure, but the attributes change for every table,
create or replace PROCEDURE INSERTDATA(NOMT in varchar2) is
num int;
BEGIN
EXECUTE IMMEDIATE 'SELECT count(*) FROM user_tables WHERE table_name = :1'
into num using NOMT ;
IF( num < 1 )
THEN
dbms_output.put_line('table not exist !!! ');
ELSE
dbms_output.put_line('');
-- here i want to insert parameters in the table,
-- but the table attributes are not the same !!
END IF;
NULL;
END INSERTDATA;
As far as I can tell, no, you can not. Number and datatypes of all parameters must be fixed.
You could pass a collection as a parameter (and have different number of values within it), but - that's still a single parameter.
Where would you want to use such a procedure?
If you need to store, update and query a variable amount of information, might I recommend switching to JSON queries and objects in Oracle. Oracle has deep support for both fixed and dynamic querying of json data, both in SQL and PLSQL.
i want to pass table name and data in procedure, but the attributes change for every table,
The problem with such a universal procedure is that something needs to know the structure of the target table. Your approach demands that the caller has to discover the projection of the table and arrange the parameters in a correct fashion.
In no particular order:
This is bad practice because it requires the calling program to do the hard work regarding the data dictionary.
Furthermore it breaks the Law Of Demeter because the calling program needs to understand things like primary keys (sequences, identity columns, etc), foreign key lookups, etc
This approach mandates that all columns must be populated; it makes no allowance for virtual columns, optional columns, etc
To work the procedure would have to use dynamic SQL, which is always hard work because it turns compilation errors into runtime errors, and should be avoided if at all possible.
It is trivially simple to generate a dedicated insert procedure for each table in a schema, using dynamic SQL against the data dictionary. This is the concept of the Table API. It's not without its own issues but it is much safer than what your question proposes.
In DB2 I'm currently running a stored procedure, inside of which I'm calling another stored procedure with a parameter.
P1 : BEGIN ATOMIC
DECLARE V_ID INTEGER ;
CALL PROGRAMS. GET_ID_BY_NAME(P_NAME) ;
-- need output of above procedure to be set to V_ID for another call here
END P1
How can I set the result of that call to the variable V_ID so that I can use it down the line?
If the relationship between ID and NAME is 1:1 , or if you simply don't care which matching name gets returned, then it is easier to modify the stored procedure (or create a renamed clone of the stored-procedure and modify the cloned copy) so that the stored procedure has an OUTPUT parameter, which the stored procedure can simply SET to the matching name, and on return the caller stored-procedure simply uses that variable name to access its value.
You may want to defensively code for a no match by arranging that the output parameter is set to NULL for that case, and test for that in the calling code. There are plenty of examples of using stored-procedure output parameters if you search, either the official Db2 documentation or elsewhere.
Using an output-parameter from the stored procedure (instead of a result-set) is the simplest approach, fewer lines of code.
Otherwise, if the relationship between ID and NAME is 1:n, and the caller wants to choose which match, then the called-stored procedure should return a result set (0 or more rows ) via an open WITH RETURN cursor. Your caller code would need to handle both the 0 rows returned case, and choose which row to use (if more than one row is returned) if you want to choose between them (usually on the basis of another column in the result-set).
To access the result-set of a called stored-procedure from inside the caller procedure, your caller procedure needs to use statements like these [ASSOCIATE RESULT SET LOCATOR][1] (v1) WITH PROCEDURE PROGRAMS.GET_ID_BY_NAME, after the CALL PROGRAMS.GET_ID_BY_NAME(), then [ALLOCATE c1 CURSOR FOR RESULT SET][1] v1.
You now have a cursor which you can iterate over to FETCH each row into variables, and at end you must CLOSE the cursor. If you know there is only ever be a single row in the result set then you don't need to iterate.
If you search, there are plenty of examples of these statements, including in the IBM documentation for your Db2 version and platform. I have shown links for Db2-LUW.
I'm trying to advance a sequence by a negative number but I keep getting the following error:
ORA-02286: no options specified for ALTER SEQUENCE
So far no amount of Googling has helped. The statement that I use is
ALTER SEQUENCE %s.nextval INCREMENT BY ?
The sequence name is generally schema.table_name_seq. Including the schema name is unavoidable because the connection is not necessarily to the same one.
Appending an inconsequential option like minvalue 1 did nothing to satisfy it either.
My second attempt was to drop .nextval from the query and run ALTER directly on the sequence (just throwing stuff at the wall to see what sticks at this point), ie:
ALTER SEQUENCE %s INCREMENT BY ?
which results in
ORA-01722: invalid number
Next I supplied a positive integer and negated it in the query (INCREMENT BY -?) which produced the same error. The query is prepared as such:
num = -1 * Math.abs(num);
stmt = conn.prepareStatement(sql);
stmt.setLong(1, num); //also tried setInt()
stmt.execute();
The idea is to set the .nextval sequence for a given table back to an arbitrary number (but always more than minval).
JustinCave's comment was most illuminating:
alter sequence is DDL. You cannot use bind variables in DDL. You would need to dynamically build the DDL statement you want and execute that and it wouldn't make sense to use a PreparedStatement to do so.
Recognizing that there are better ways to do it, but acknowledging time constraints, I constructed the following SQL statement:
ALTER SEQUENCE %s INCREMENT BY %d
which works like a charm. Yes, the number to advance by is hardcoded in each request. Not the best solution but I cannot create stored procedures at this point, so it'll have to do.
Thank you Justin.
In SQL Developer, is it possible to define a variable as a construction based on another variable(s) to be used with a SPOOL?
For example:
define startdate='01-JAN-14'
define sdt=SUBSTR(&&startdate,4,3)
SPOOL &&sdt._File.csv
Giving me the output "JAN_File.csv"
I know that won't work because I've tested it and I've tried to research it, but is there a workaround to get to that same output?
Not pretty but if you really wanted to you could use a bind variable as an intermediate step:
define startdate='01-JAN-14'
var bind_sdt varchar2(3);
exec :bind_sdt := SUBSTR('&&startdate',4,3);
column sub_sdt new_value sdt;
select :bind_sdt as sub_sdt from dual;
SPOOL &&sdt._File.csv
The column ... new_value command is creating a substitution variable from the select-list item, which is the bind variable, which is the sub-string.
You could set termout off and back on around the exec and select, and set verify off if you aren't already doing so. You haven't said where the start date string is coming from so I've left that as a define.
On an earlier stage, our system was provided with tables that hold last used autonumber (instead of using sequences). We are now redoing the client solution for the system, and need to 'reinvent' how to fetch next record number - by SQL.
The client application is made in FileMaker, the database still resides in Oracle. The challenge is to update last used autonumber AND supply it to the new record initiated in the client - in one operation.
A SELECT statement can retrieve the last used number.
An UPDATE statement can increment the last used number.
A function selecting and returning the number is not allowed to contain update statements.
A procedure may do the update, and may retain the new value returning it into an OUT parameter, but does not return the new value to the client - unless the client in some way can read the OUT parameter from the procedure (I do not think it reads DBMS_OUTPUT).
If the procedure proceeds to do an INSERT on the table where the client is preparing an INSERT, the inserts will not be identical, as far as I can see.
So - is there a syntax that will make the OUT value accessible to the client as result of an SQL statement including a procedure call (perhaps making the OUT parameter in some way refer to the client's new record recnr field), or is this altogether a blind alley?
regarding syntax - You need to wrap your PL/SQL procedure with out param into function (you can use overloaded function with the same name in the same package) and return out value as function result.
Regarding design - I do not recommend to use "home-made" mechanism to replace sequences. Sequences are much better optimised and more reliable solution.