Unable to retrieve multiple records in an Oracle extract - oracle

I need to get the year from a date in all of the records in an Oracle database. The following is my attempt:
CREATE or replace TYPE BODY student_t AS MEMBER FUNCTION getYear RETURN NUMBER IS
yearDOB NUMBER;
BEGIN
SELECT EXTRACT(YEAR FROM s.dob) INTO yearDOB
from student s;
return yearDOB;
END;END;/
But that will not work when the table has more than 1 record.
How I can fix the issue?

When you do select into rdbms thinks that only one value will be returned, or that your variable will be an array. You cannot set a NUMBER variable to an array of objects.

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.

Function results column names to be used in select statement

I have function which returns column names and i am trying to use the column name as part of my select statement, but my results are coming as column name instead of values
FUNCTION returning column name:
get_col_name(input1, input2)
Can И use this query to the results of the column from table -
SELECT GET_COL_NAME(input1,input2) FROM TABLE;
There are a few ways to run dynamic SQL directly inside a SQL statement. These techniques should be avoided since they are usually complicated, slow, and buggy. Before you do this try to find another way to solve the problem.
The below solution uses DBMS_XMLGEN.GETXML to produce XML from a dynamically created SQL statement, and then uses XML table processing to extract the value.
This is the simplest way to run dynamic SQL in SQL, and it only requires built-in packages. The main limitation is that the number and type of columns is still fixed. If you need a function that returns an unknown number of columns you'll need something more powerful, like the open source program Method4. But that level of dynamic code gets even more difficult and should only be used after careful consideration.
Sample schema
--drop table table1;
create table table1(a number, b number);
insert into table1 values(1, 2);
commit;
Function that returns column name
create or replace function get_col_name(input1 number, input2 number) return varchar2 is
begin
if input1 = 0 then
return 'a';
else
return 'b';
end if;
end;
/
Sample query and result
select dynamic_column
from
(
select xmltype(dbms_xmlgen.getxml('
select '||get_col_name(0,0)||' dynamic_column from table1'
)) xml_results
from dual
)
cross join
xmltable
(
'/ROWSET/ROW'
passing xml_results
columns dynamic_column varchar2(4000) path 'DYNAMIC_COLUMN'
);
DYNAMIC_COLUMN
--------------
1
If you change the inputs to the function the new value is 2 from column B. Use this SQL Fiddle to test the code.

Apex Interactive Report ORA-1843

I'm trying to display at oracle apex IR query, a biggest date from 3 different tables, using greatest function to choice the biggest. For that, created two varchar2 type functions.
One of them return the date converted to char, of a selected table like this:
create or replace FUNCTION RETURN_DATES (key in number, parameter in NUMBER)
RETURN VARCHAR2 IS
...
BEGIN
case parameter
when 1 then
select distinct MAX(mydate) into seal from table1 v WHERE v.num_contract = number_contract;
return NVL(TO_CHAR(TO_DATE (seal),'mm/dd/yyyy'),'01/01/1980')
when 2 then
select... from table2 x WHERE...
...
END;
The second function calls that one using a greatest function between the parameter calls, which refers to case statement:
create or replace FUNCTION RETURN_BIGGEST_DATE(key in number) return VARCHAR2 IS
...
BEGIN
select greatest (RETURN_DATES(key,1),RETURN_DATES(key,2),RETURN_DATES(key,3)) into greatest_date from dual;
return greatest_date;
...
END;
When i call it on sql commands it's works fine, but at an interactive report its fails, returning the ORA-1843, using the quite same query.
Could anyone help?
Simply modifying the RETURN_DATES returning DATE
and the seal variable, of date type, without conversions, works fine at apex IR!
I noticed when a date column is relayed on ir query, there are an extra parameter on Column filter, named Date Ranges. It was the two way traffic to start simulations and change the returning type of the that function.
No more changes was required:
create or replace FUNCTION RETURN_DATES (key in number, parameter in NUMBER)
RETURN DATE IS
...
select distinct MAX(mydate) into seal from table1 v WHERE v.num_contract = number_contract;
return NVL(seal),'01/01/1980')

Reading IN parameters in a query in PL/SQL

I have written a PL/SQL procedure as:
CREATE OR REPLACE PROCEDURE checkProdQuantity (productid IN number, orderqty IN number)
IS
qty number;
qty_diff number;
BEGIN
SELECT quantity INTO qty from Products where ProductID=productid;
IF orderqty>qty THEN
dbms_output.put_line('Ordered quatity is greater than available quantity');
ELSE
qty_diff:=qty-orderqty;
UPDATE Products set quantity=qty_diff where ProductID=productid;
END IF;
END;
/
But when I try to execute this procedure with valid parameters, it shows an error: exact fetch returns more than the requested number of rows.
I have checked my table, and for the parameters I am supplying it should return only one row. I think for some reason, the value of productid IN parameter is not being read in the select query. Even if I provide some random values for productid parameter, it still gives the same error. I am unable to figure out where the problem is.
I don't think the compiler is distinguishing between the parameter productid and the column name ProductID.
Try renaming your parameter to a_productId, or something different from the column name.
Its happning because, when you write
SELECT quantity INTO qty from Products where ProductID=productid;
Oracle scope resolution interprets productid as the column_name and not as your input variable.
Change the name of the input variable to something other than the column name and it should work.
Hope it helps
Vishad

Resources