Call the retrieve procedure in Oracle with the primary key value - oracle

I have a table like this:
CST_ID CST_NAME CST_TYPE PYMENT_MODE TOTAL_PAYMENT
----------------------------------------------------
105 Jyothy Regular Full 15900
101 Alveena Regular half 15800
102 Nizam Regular Full 15500
100 Hari Regular Full 14500
103 Sharath Partime Full 17500
104 Jipsy Shifted Full 18000
I created a stored procedure for getting the row that I called from the procedure with the primary key valued field (here cst_id) as
CREATE or replace PROCEDURE sp_slct (cst_id NUMBER) AS
tot_id NUMBER;
--ls_name varchar2,
--ls_type varchar2,
--ls_mode varchar2,
--ls_pymnt int;
BEGIN
select
cst_id, cst_name, cst_type, payment_mode, total_payment into
ls_name, ls_type, ls_mode, ls_pymnt from tbl_cst
where
tbl_cst.cst_id = sp_slct.cst_id;
END sp_slct;
but I'm getting the errors in compilation
How do I get the result to select a row with the ID that I given as the input to the procedure?

You miss, at least, the IN keyword in the procedure parameter list, here how it should be.
Maybe you should declare also the vars in the INTO clause
CREATE or replace PROCEDURE sp_slct (cst_id IN NUMBER) AS

It would be great if you tell us your error Messages, that would improve the possibility to help. Until now your procedure doesnt do anything than to start a query.
You have two ways to use the result of a query. You could use cursors (if you want to search in some rows) or you can use "into" if you needs only one value. (as you did)
And if you want to get a result that matches to your Parameter list you must put your Parameters into the where- clause (as you did).
The only mistake I can see is the order: SELECT ... INTO ... FROM ...
Oracle documentation
And, of course, you must declare the variables (undo your comments)

Related

How to use sequence in a function in PLSQL?

In my schema, I have a reference number column in my settlement
table with a null value of varchar2 type, that I want to update it continuously.
Then I've created a sequence called ref_seq_num using a built-in function.
I want to use it (ref_seq_num) within my function get_ref_num to update the sequence ref. number to my settlement table,
which the return type also is varchar2 and I have a function like below
CREATE OR REPLACE FUNCTION get_ref_num RETURN settlement.ref_nr %TYPE IS
v_ref_seq settlement.ref_nr%TYPE;
BEGIN
v_ref_seq := to_char(sysdate, 'YYYYMMDD')||LPAD(ref_seq_num.nextval, 8,'0');
RETURN v_ref_seq;
END get_ref_num;
However, I bum into this error message 1/55 PLS-00302: component 'ref_nr' must be declared. I also tried changing the data type to varchar2 and error message is PLS-00215: String length constraints must be in range (1 .. 32767) How can I fix it?
According to your code, it seems that there's a table whose name is SETTLEMENT, but it doesn't contain the REF_NR column.
The following example shows how to do that:
SQL> create sequence ref_seq_num;
Sequence created.
A table that does contain the REF_NR column (which is then used in the function):
SQL> create table settlement (ref_nr varchar2(20));
Table created.
Your code, unmodified:
SQL> CREATE OR REPLACE FUNCTION get_ref_num RETURN settlement.ref_nr %TYPE IS
2 v_ref_seq settlement.ref_nr%TYPE;
3 BEGIN
4 v_ref_seq := to_char(sysdate, 'YYYYMMDD')||LPAD(ref_seq_num.nextval, 8,'0');
5 RETURN v_ref_seq;
6 END get_ref_num;
7 /
Function created.
Testing:
SQL> select get_ref_num from dual;
GET_REF_NUM
--------------------------------------------------------------------------------
2019050400000001
SQL>
If you have a column called ref_nr within settlement table, you code must work properly. I think the problem in the second case raises due to missing data precision part ( should be such as varchar2(16) ) for defining the variable as v_ref_seq varchar2. I would prefer using a numeric type such as number or int to hold the values for ref_nr, since they are all numeric, and this data type protects the data remain as numeric. Whenever you need to query you may use to_char function preventing exponential display( select to_char(ref_nr) from settlement ).
Moreover, if you use Oracle 12c version, you don't need to create such an extra function, just alter your table so that being sequence as your default for the column :
alter table settlement
modify ref_nr default to_char(sysdate, 'yyyymmdd')||lpad(ref_seq_num.nextval, 8,'0');

PL/SQL reusable dynamic sql program for same type of task but different table and column

Thank you for reply guys. I kind of solved my problem.
I used to try to update data with ref cursor in dynamic SQL using "where current of" but I now know that won't work.
Then I tried to use %rowtype to store both 'id' and 'clob' in one variable for future updating but turns out weak ref cursor can't use that type binding either.
After that I tried to use record as return of an ref cursor and that doesn't work on weak cursor either.
On the end, I created another cursor to retrieve 'id' separately along with cursor to retrieve 'clob' on the same time then update table with that id.
I'm now working on a Oracle data cleaning task and have a requirement like below:
There are 38 tables(maybe more in the future) and every table has one or multiple column which type is Clob. I need to find different keyword in those columns and according to a logic return binary label of the column and store it in a new column.
For example, there is a table 'myTable1' which has 2 Clob columns 'clob1' and 'clob2'. I'd like to find keyword 'sky' from those columns and store '0'(if not found) or '1'(if found) in two new columns 'clob1Sky','clob2Sky'.
I know if I could write it on a static way which will provide higher efficiency but I have to modify it for those very similar tasks every time. I want save some time on this so I'm trying to write it in a reusable way and not binding to certain table.
But I met some problem when writing the program. My program is like below:
create or replace PACKAGE body LABELTARGETKEYWORD
as
/**
#param varcher tableName: the name of table I want to work on
#param varchar colName: the name of clob column
#param varchar targetWord: the word I want to find in the column
#param varchar newColName: the name of new column which store label of clob
*/
PROCEDURE mainProc(tableName varchar, colName varchar,targetWord varchar,newColName varchar2)
as
type c_RecordCur is ref cursor;
c_sRecordCur c_recordCur;
/*other variables*/
begin
/*(1) check whether column of newColName exist
(2) if not, alter add table of newColName
(3) open cursor for retrieving clob
(4) loop cursor
(5) update set the value in newColName accroding to func labelword return
(6) close cursor and commit*/
end mainProc;
function labelWord(sRecord VARCHAR2,targetWord varchar2) return boolean...
function ifColExist(tableName varchar2,newColName varchar2) return boolean...
END LABELTARGETKEYWORD;
Most DML and DDL are written in dynamic sql way.
The problem is when I write the (5) part, I notice 'Where current of' clause can not be used in a ref cursor or dynamic sql statement. So I have to change the plan.
I tried to use a record(rowid,label) to store result and alter the table later.(the table only be used by two people in my group, so there won't be problem of lock and data changes). But I find because I'm trying to use dynamic sql so actually I have to define ref cursor with return of certain %rowtype and basically all other variables, %type in dynamic sql statement. Which makes me feel my method has something wrong.
My question are:
If there a way to define %type in dynamic sql? Binding type to variable in dynamic SQL?
Could anybody give me a hint how to write that (5) part in dynamic SQL?
Should not I design my program like that?
Is it not the way how to use dynamic SQL or PLSQL?
I'm very new to PL/SQL. Thank you very much.
According to Tom Kyte's advice, to do it in one statement if it can be done in one statement, I'd try to use a single UPDATE statement first:
CREATE TABLE mytable1 (id NUMBER, clob1 CLOB,
clob2 CLOB, clob1sky NUMBER, clob2sky NUMBER )
LOB(clob1, clob2) STORE AS SECUREFILE (ENABLE STORAGE IN ROW);
INSERT INTO mytable1(id, clob1, clob2)
SELECT object_id, object_name, object_type FROM all_objects
WHERE rownum <= 10000;
CREATE OR REPLACE
PROCEDURE mainProc(tableName VARCHAR2, colName VARCHAR2, targetWord VARCHAR2, newColName VARCHAR2)
IS
stmt VARCHAR2(30000);
BEGIN
stmt := 'UPDATE '||tableName||' SET '||newColName||'=1 '||
'WHERE DBMS_LOB.INSTR('||colName||','''||targetWord||''')>1';
dbms_output.put_line(stmt);
EXECUTE IMMEDIATE stmt;
END mainProc;
/
So, calling it with mainProc('MYTABLE1', 'CLOB1', 'TAB', 'CLOB1SKY'); fires the statement
UPDATE MYTABLE1 SET CLOB1SKY=1 WHERE DBMS_LOB.INSTR(CLOB1,'TAB')>1
which seems to do the trick:
SELECT * FROM mytable1 WHERE clob1sky=1;
id clob1 clob2 clob1sky clob2skiy
33 I_TAB1 INDEX 1
88 NTAB$ TABLE 1
89 I_NTAB1 INDEX 1
90 I_NTAB2 INDEX 1
...
I am not sure with your question-
If this job is suppose to run on daily or hourly basis ,running query through it will be very costly. One thing you can do - put all your clob data in a file and save it in your server(i guess it must be linux). then you can create a shell script and schedule a job to run gerp command and fetch your required value and "if found then update your table".
I think you should approaches problem another way:
1. Find all columns that you need:
CURSOR k_clobs
select table_name, column_name from dba_tab_cols where data_type in ('CLOB','NCLOB');
Or 2 cursor(you can build you query if you have more than 1 CLOB per table:
CURSOR k_clobs_table
select DISTINCT table_name from dba_tab_cols where data_type in ('CLOB','NCLOB');
CURSOR k_clobs_columns(table_namee varchar(255)) is
select column_name from dba_tab_cols where data_type in ('CLOB','NCLOB') and table_name = table_namee;
Now you are 100% that column you are checking is clob, so you don't have to worry about data type ;)
I'm not sure what you want achieve, but i hope it may help you.

Binding Variables in PL/SQL

I read this script that assigns of a data column info into 2 binding variables.
something like this:
EXEC SQL SELECT
var1
into :v.v1:v2
from table
Shouldn't there be a comma in there? Or is this like assigning var1 into v.v1 and also into v2 with the same values?
The above script would give error only. if you want to assign value comma is required for the same.
The syntax would be :- Ex if you want to fetch Empno,Ename,Deptno,salary from
EMPLOYEES.The plsql block would be as given below.
DECLARE
L_EMPNO NUMBER;
L_ENAME VARCHAR2(1000);
L_DEPTNO NUMBER;
L_SALARY NUMBER;
BEGIN
SELECT EMPNO, ENAME, DEPTNO, SALARY
INTO L_EMPNO, L_ENAME, L_DEPTNO, L_SALARY
FROM EMPLOYEES
WHERE EMPNO=100;
END;
This code is a snippet from a PRO*C program, a C program with embedded SQL.
v2 is an indicator variable. See here for info: https://docs.oracle.com/cd/B28359_01/appdev.111/b28427/pc_04dat.htm#i12463
An indicator variable will contain a value that relates to it's associated variable which in this case is v.v1 and is set after the operation in which it is used. In this case, after the select, you can test v2 and based on it's value it will tell you info about v.v1:
From the link above, if v2 equals:
0 - The operation was successful
-1 - A NULL was returned, inserted, or updated.
-2 - Output to a character host variable from a "long" type was truncated, but the original column length cannot be determined.
>0 - The result of a SELECT or FETCH into a character host variable was truncated. In this case, if the host variable is a multibyte character variable, the indicator value is the original column length in characters. If the host variable is not a multibye character variable, then the indicator length is the original column length in bytes.
I would suggest using it's other form, which would make things clear for the person that will maintain this after you (at least do that person a favor and comment this when you get your head around it). Always code for the person that will maintain after you. Don't you wish the person before you did that?!:
EXEC SQL SELECT
var1
into :v.v1 INDICATOR :v2
from table

plsql table type with index of is complaining

Pl/SQL:
Intent: My intent was to access employee tuple object defied as cursor below by using key as the employee_id.
Problem: I created a cursor - *l_employees_cur* and want to create type table as below type *l_employees_t*, as below but the compiler is complaining saying that PLS-00315 implementation restriction unsupported table index type.
CURSOR l_employees_cur
IS
SELECT employee_id,manager_id,first_name,last_name FROM employees;
type l_employees_t
IS
TABLE OF l_employees_cur%rowtype INDEX BY employees.employee_id%TYPE;
The definition of employees.employee_id is:
EMPLOYEE_ID NUMBER(6) NOT NULL
why can't I do this ? or Am I doint something wrong.
From the Oracle Documenation:
Associative Arrays
An associative array (formerly called PL/SQL table or index-by table) is a set of key-value pairs. Each key is a unique index, used to locate the associated value with the syntax variable_name(index).
The data type of index can be either a string type or PLS_INTEGER. Indexes are stored in sort order, not creation order. For string types, sort order is determined by the initialization parameters NLS_SORT and NLS_COMP.
I think that your mistake is the declaration of the plsql table.
Why don't you try the next one:
type l_employees_t
IS
TABLE OF l_employees_cur%rowtype INDEX BY pls_integer;
I also have a question for you:
What is the meaning of EMPLOYEE_ID NOT NULL NUMBER(6) in your code above?
Greetings
Carlos
Storing and Retreiving SQL Query Output in a PL/SQL Collection
The example in the OP looks a lot like Oracle's new sample HR data schema. (For those old-timers who know, the successor to the SCOTT-TIGER data model). This solution was developed on an Oracle 11g R2 instance.
The Demo Table Design - EMP
Demonstration Objectives
This example will show how to create a PL/SQL collection from an object TYPE definition. The complex data type is derived from the following cursor definition:
CURSOR l_employees_cur IS
SELECT emp.empno as EMPLOYEE_ID, emp.mgr as MANAGER_ID, emp.ename as LAST_NAME
FROM EMP;
After loading the cursor contents into an index-by collection variable, the last half of the stored procedure contains an optional step which loops back through the collection and displays the data either through DBMS_OUTPUT or an INSERT DML operation on another table.
Stored Procedure Example Source Code
This is the stored procedure used to query the demonstration table, EMP.
create or replace procedure zz_proc_employee is
CURSOR l_employees_cur IS
SELECT emp.empno as EMPLOYEE_ID, emp.mgr as MANAGER_ID, emp.ename as LAST_NAME
FROM EMP;
TYPE employees_tbl_type IS TABLE OF l_employees_cur%ROWTYPE INDEX BY PLS_INTEGER;
employees_rec_var l_employees_cur%ROWTYPE;
employees_tbl_var employees_tbl_type;
v_output_string varchar2(80);
c_output_template constant varchar2(80):=
'Employee: <<EMP>>; Manager: <<MGR>>; Employee Name: <<ENAME>>';
idx integer;
outloop integer;
BEGIN
idx:= 1;
OPEN l_employees_cur;
FETCH l_employees_cur INTO employees_rec_var;
WHILE l_employees_cur%FOUND LOOP
employees_tbl_var(idx):= employees_rec_var;
FETCH l_employees_cur INTO employees_rec_var;
idx:= idx + 1;
END LOOP;
CLOSE l_employees_cur;
-- OPTIONAL (below) Output Loop for Displaying The Array Contents
-- At this point, employees_tbl_var can be handed off or returned
-- for additional processing.
FOR outloop IN 1 .. idx LOOP
-- Build the output string:
v_output_string:= replace(c_output_template, '<<EMP>>',
to_char(employees_tbl_var(outloop).employee_id));
v_output_string:= replace(v_output_string, '<<MGR>>',
to_char(employees_tbl_var(outloop).manager_id));
v_output_string:= replace(v_output_string, '<<ENAME>>',
employees_tbl_var(outloop).last_name);
-- dbms_output.put_line(v_output_string);
INSERT INTO zz_output(output_string, output_ts)
VALUES(v_output_string, sysdate);
COMMIT;
END LOOP;
END zz_proc_employee;
​
I commented out the dbms_output call due to problems with the configuration of my server beyond my control. The alternate insert command to a output table is a quick way of visually verifying that the data from the EMP table found its way successfully into the declared collection variable.
Results and Discussion of the Solution
Here is my output after calling the procedure and querying my output table:
While the actual purpose behind the access to this table isn't clear in the very terse detail of the OP, I assumed that the first approach was an attempt to understand the use of collections and custom data types for efficient data extraction and handling from structures such as PL/SQL cursors.
The portion of this example procedure is very reusable, and the initial steps represent a working way of making and loading PL/SQL collections. If you notice, even if your own version of this EMP table is different, the only place that requires redefinition is the cursor itself.
Working with types, arrays, nested tables and other collection types will actually simplify work in the long run because of their dynamic nature.

Generate automatic Id

I am making a web application which uses a database in which I have a field I_ID which i want to automatically increment like I0 then I1 then I2 and so on with each record insertion in the database.
To achieve it I made a trigger for this table.But its not working fine.What can be the reason.Please help
My Trigger T1:
CREATE OR REPLACE TRIGGER "T1"
before
insert on "TBINDIVIDUAL"
for each row
declare
x varchar2(10);
mx varchar2(13);
mx2 varchar2(13);
y number(3);
begin
x:=:new.I_ID;
mx:=substr(x,1,1);
select max(I_ID) into mx2 from tbindividual where I_ID like mx||'%';
y:=to_number(substr(mx2,2));
:new.I_ID:=mx||to_char(y+1);
end t1;
/
EDITED :
As i do by answer
CREATE OR REPLACE TRIGGER "TBINDIVIDUAL_T1"
BEFORE
insert on "TBINDIVIDUAL"
for each row
begin
:new.I_ID = SEQ1.nextval;
end;
/
But it give two errors
Encountered the symbol "=" when expecting one of the following: := . ( # % ; indicator
Encountered the symbol "END"
Please help
So Oracle is not SQL Server...
if you want to get unique ID's, you need to populate them from a sequence.
for creating a sequence use:
create sequence myseq;
and in your code use (depends on the version):
:new.I_ID := myseq.nextval;
or
select myseq.nextval into :new.I_ID from dual;
the problem your code doesn't work is what :new and :old means...
I would recommend you reading about their meaning...
Hope I've been helpful...
Because of the discussion in the comments - here is a full example:
for this table:
CREATE TABLE test (A number);
to add a unique, sequential ID you need to first create a sequence:
CREATE SEQUENCE myseq;
and a trigger:
CREATE OR REPLACE TRIGGER "T1"
before
insert on "test"
for each row
begin
:new.I_ID := myseq.nextval;
end t1;
/
by the way - I would recommend to check before substituting :new.I_ID, if it is null or not, cause sometimes in upgrades people add a unique ID from an external resource.. (such as them getting a unique number from the sequence themselves...)
you can read more about sequences here:
http://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_6015.htm#SQLRF01314
sorry for the way the code is displayed.. need to learn how to write code here...
One more thing - In Oracle - you cannot create such a PL/SQL to increase existing counter without locks.
Concurrent queries might run the first query in the PL/SQL simultaneously, which means multiple sessions will get the same I_ID.
Also notice that in your code you queried the max on varchar, which is not the same as max on number...
Adding concatenated text is unrelated to the unique ID. In your case it will look like:
:new.I_ID = substr(:new.I_ID,1,1)||to_char(myseq.nextval);
assuming x is being inputted with the char you want..

Resources