How to exceute a query stored in dictionary table into a CURSOR? - sql

I’ve a table named QUERIES_DICTIONNARY with 2 columns (ID, SQL_QUERY) the first column is NUMBER and the second column is a Varchar represent an SQL query.
CREATE TABLE "QUERIES_DICTIONNARY"
(
"ID" NUMBER(10,0) NOT NULL ENABLE,
"SQL_QUERY" NVARCHAR2(2000) NOT NULL ENABLE
)
I want to create a stored procedure with 2 parameters (P_KEY, P_RESULTS_CURSOR) where the P_KEY is the ID of my table, and the P_RESULT_CURSOR where i execute my SQL query of the record having ID = P_KEY.
CREATE OR REPLACE PROCEDURE GET_STATISTICS_RESULTS
(
P_KEY IN NUMBER,
P_RESULTS_CURSOR OUT SYS_REFCURSOR
)
In my stored procedure I get the record where the ID = P_KEY
Once I’ve the record I want to execute the the associated query using EXECUTE IMMEDIATE into the P_RESULTS_CURSOR.
CREATE OR REPLACE PROCEDURE GET_STATISTICS_RESULTS
(
P_KEY IN NUMBER
P_RESULTS_CURSOR OUT SYS_REFCURSOR
) AS
BEGIN
---- Get the record
-- SELECT SQL_QUERY FROM QUERIES_DICTIONNARY WHERE ID = P_KEY;
---- Then Execute the query
-- OPEN P_RESULTS_CURSOR FOR ?
END GET_STATISTICS_RESULTS;
How can I do this approach in one Stored procedure please?

There is two ways: simple and not so simple. Simple way is:
CREATE OR REPLACE PROCEDURE GET_STATISTICS_RESULTS
(
P_KEY IN NUMBER;
P_RESULTS_CURSOR OUT SYS_REFCURSOR;
) AS
cursor_text QUERIES_DICTIONNARY.SQL_QUERY%TYPE;
BEGIN
---- Get the record
SELECT SQL_QUERY
INTO cursor_text
FROM QUERIES_DICTIONNARY WHERE ID = P_KEY;
---- Then Execute the query
OPEN P_RESULTS_CURSOR FOR cursor_text;
END GET_STATISTICS_RESULTS;
Not so simple way - using DBMS_SQL package. You can see example in documentation. It will be quite complicated decision.

CREATE OR REPLACE PROCEDURE GET_STATISTICS_RESULTS
(
P_KEY IN NUMBER
P_RESULTS_CURSOR OUT SYS_REFCURSOR
) AS
l_s "QUERIES_DICTIONNARY"."SQL_QUERY"%TYPE;
BEGIN
---- Get the record
SELECT SQL_QUERY into l_s FROM QUERIES_DICTIONNARY WHERE ID = P_KEY;
---- Then Execute the query
OPEN P_RESULTS_CURSOR FOR l_s;
END GET_STATISTICS_RESULTS;
PLS-00382 appears because of NVARCHAR2 column. You may try to cast nvarchar2 to varchar2:
l_s VARCHAR2(4000);
...
SELECT CAST(SQL_QUERY AS VARCHAR2(4000)) into l_s FROM QUERIES_DICTIONNARY WHERE ID = P_KEY;
...
But you may lose some data.

Related

Oracle Procedure- Select all tables and then loop though the records of those tables

I got all the tables from my database that starts with "FULLTEXTLOOKTABLE_%"
Now i want to loop though all those tables.
Select each record from those tables and store the data in 4 variables.
Then delete that particular row in table
Insert into table using the data in those 4 variable.
I want to do this because there was a mismatch in the insertion of data in columns, i cannot alter the column name so i have to truncate the table re-insert correctly.
The code i wrote till now is:
create or replace PROCEDURE "Update"(name_in IN varchar2 )
AS
iID NUMBER(10,0);
FullTextDetails VARCHAR2(4000 BYTE);
Regex VARCHAR2(4000 BYTE);
MinMatch NUMBER(10,0);
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'FULLTEXTLOOKTABLE_%' )
LOOP
// Loop Throgh all records of c.table_name
// insert the data into iID, FullTextDetails,Regex,MinMatch
//Delete that row
//Insert it again using the data in variables
END LOOP;
END;
Instead of processing record by record, you can do it at a table level.
Create a new table tab_dest having the same structure as the source table tab_src including data as follows -
CREATE TABLE TAB_DEST AS
SELECT * FROM TAB_SRC;
Truncate your source table as -
TRUNCATE TABLE TAB_SRC;
Insert the records from TAB_DEST back to TAB_SRC specifying the column order -
INSERT INTO TAB_SRC(COL1, COL2, COL3)
SELECT COL1, COL3 AS COL2, COL2 AS COL3
FROM TAB_DEST;
Hope this works for you.
you can try something like this but for this way, you need to create a procedure that returns reference cursor
create or replace PROCEDURE "Update"(name_in IN varchar2 )
AS
iID NUMBER(10,0);
FullTextDetails VARCHAR2(4000 BYTE);
Regex VARCHAR2(4000 BYTE);
MinMatch NUMBER(10,0);
TYPE cur_type IS REF CURSOR;
tmp_cursor cur_type;
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'FULLTEXTLOOKTABLE_%' )
LOOP
tmp_cursor := prc_that_returns_ref_cursor(table_name);
//Delete the data from table
FOR tmp_cursor_rec IN tmp_cursor LOOP
//Insert it again using the data in tmp_cursor_rec.column name
END LOOP;
END;

Oracle procedure, placement of multiple records in variables

I'm trying to create my first oracle procedure. The select will return multiple records; I need to be able to place each record in the variables and use the record in later actions in the procedure. Any help please?
key number;
keyCount number;
rub varchar2(50);
srub varchar2(100);
type varchar2(200);
date varchar2(14);
note varchar2(500);
BEGIN
SELECT KEY,COUNT(KEY),RUB,
SRUB,TYPE ,DATE,NOTE FROM Student
WHERE S_KEY = {key};
END;
In PL/SQL we need to select results into matching variables. One way is separate variables for each column (as shown). The alternative is to use a row variable which matches the project of the query; find out more.
You've got an aggregating function - COUNT() so you need a GROUP BY clause which defines the non-aggregating columns. You say you have more than one record so you need to populate a collection not scalar variables. Find out more.
Your procedure should look something like this
create or replace procedure my_first_proc
( p_key in student.s_key%type )
as
type my_rec is record (
key number ,
keyCount number ,
rub varchar2(50); ,
srub varchar2(100) ,
type varchar2(200) ,
date varchar2(14),
note varchar2(500)
);
type my_rec_coll is table of my_rec;
l_student_recs my_rec_coll;
BEGIN
SELECT KEY,COUNT(KEY),RUB,SRUB,TYPE ,DATE,NOTE
bulk collect into l_student_recs
FROM Student
WHERE S_KEY = p_key
group by KEY,RUB,SRUB,TYPE ,DATE,NOTE
;
for idx in l_student_recs.first() .. l_student_recs.last()
loop
-- do some processing here
dbms_output.put_line('RUB = '||l_student_recs(idx).rub);
end loop;
EXCEPTION
when no_data_found then
raise_application_error(-01403, 'no student records for key='||p_key);
END;
Get into good habits:
use sensible variable names
distinguish parameter names from local variables
handle predictable exceptions

Select IDs that match all items on table

So I have a table called Mroom which looks something like this:
CREATE Mroom(ID_ROOM NUMBER(3), CHAR_NAME NVARCHAR2(30), CHAR_VALUE NUMBER(20));
The contents of the table are something like:
1; air-conditioner; 1
1; pool; 2
2; pool; 1
Which would mean that the room with the ID 1 has 1 air-conditioner and 2 pools. And the room with the ID 2 has one pool.
I've also created a table of a datatype, that I've called c_chars:
CREATE OR REPLACE TYPE c_chars FORCE IS OBJECT (CHAR VARCHAR2(20), VALUE NUMBER(30));
/
CREATE OR REPLACE TYPE tab_chars FORCE IS TABLE OF c_chars;
/
And I need to know if for example if there are any rooms that have at least 1 air-conditioner and a pool. My current code:
CREATE OR REPLACE PROCEDURE thisRoomOK(v_carateristicas IN tab_chars, lista OUT SYS_REFCURSOR)
IS BEGIN
OPEN lista FOR SELECT MRoom.ID_ROOM
FROM Mroom, TABLE(v_carateristicas) v_carateristicas
WHERE Mroom.CHAR_NAME = v_carateristicas.CHAR
AND Mroom.VALUE >= v_carateristicas.VALUE
GROUP BY Mroom.ID_ROOM
ORDER BY getSalas.Mroom.ID_ROOM;
END;
/
The problem is if I ask for the rooms that have at least one air-conditioner and a pool. It will return me both rooms, because the rrom 2 has a pool (it works like an OR, but I'd like it to work like and AND).
Edit 1:
If possible, I'd like to receive answers that changes the procedure, but not the tables, altough all answers are welcome!
Edit 2:
The code above is a simplification, this is the full code:
Object definition:
CREATE OR REPLACE TYPE c_valor FORCE IS OBJECT (CARATERISTICA VARCHAR2(20), VALOR NUMBER(30));
/
CREATE OR REPLACE TYPE tabc_valor FORCE IS TABLE OF c_valor;
/
Procedure:
CREATE OR REPLACE PROCEDURE getSalas_Carateristicas(v_carateristicas IN tabc_valor, lista OUT SYS_REFCURSOR)
IS BEGIN
OPEN lista FOR SELECT getSalas.ID_SALA
FROM getSalas, TABLE(v_carateristicas) v_carateristicas
WHERE getSalas.NOME_CARATERISTICA = v_carateristicas.CARATERISTICA
AND getSalas.VALOR >= v_carateristicas.VALOR
GROUP BY getSalas.ID_SALA
ORDER BY getSalas.ID_SALA;
END;
/
Procedure altered according to #XING's sugestion:
CREATE OR REPLACE PROCEDURE getSalas_Carateristicas(v_carateristicas IN tabc_valor, lista OUT SYS_REFCURSOR)
IS BEGIN
OPEN lista FOR SELECT getSalas.ID_SALA
FROM getSalas
INNER JOIN TABLE(v_carateristicas) v_carateristicas ON getSalas.NOME_CARATERISTICA = v_carateristicas.CARATERISTICA
AND getSalas.VALOR >= v_carateristicas.VALOR
GROUP BY getSalas.ID_SALA
ORDER BY getSalas.ID_SALA;
END;
/
I guess the issue is with the way you ask for a room. See below demo and explaination inline.
Proc:
CREATE OR REPLACE TYPE T541682.c_chars FORCE IS OBJECT (v_CHAR VARCHAR2(20), v_VALUE NUMBER(30));
/
CREATE OR REPLACE TYPE T541682.tab_chars FORCE IS TABLE OF c_chars;
/
CREATE OR REPLACE PROCEDURE T541682.thisRoomOK (
v_carateristicas IN tab_chars,
lista OUT SYS_REFCURSOR)
IS
BEGIN
OPEN lista FOR
SELECT MRoom.ID_ROOM
FROM Mroom
inner join TABLE (v_carateristicas) v_carateristicas
ON Mroom.CHAR_NAME = v_carateristicas.v_CHAR
AND Mroom.CHAR_VALUE = v_carateristicas.v_VALUE
GROUP BY Mroom.ID_ROOM
ORDER BY Mroom.ID_ROOM;
END;
/
declare
var tab_chars :=tab_chars();
type var1 is table of MRoom.ID_ROOM%type index by pls_integer;
var_out var1;
x sys_refcursor;
begin
var.extend(3);
-- This is the way i populate my object for which i want to match with the table. Am sure you are passing all rows same as your table hence its getting the all the rows.
var(1) := c_chars('air-conditioner',1);
var(2) := c_chars('pool',2);
-- Must be populating the third row as well. And as per your query both rows wil be picked up if you populate the object .
--var(3) := c_chars('pool',1);
-- Calling your procedure
thisRoomOK(v_carateristicas => var,lista => x);
fetch x bulk collect into var_out ;
--- displaying number of room
for i in 1..var_out.count
loop
dbms_output.put_line('Room Number - '||var_out(i));
end loop;
---passing the sys_refcursor result to another procedure
proc1(var_out);
end;
Output:
SQL> /
Room Number - 1
PL/SQL procedure successfully completed.

How to use in statement with nested table

Hey there I have a function, and part of the function is to make sure that the selected value is within the passed in table of varchar2s. To start I declare a varchar2 table type like so.
create or replace type Varchar2Table is table of varchar2(200)
Then I have the function which accepts the nested table parameter and has a select statement on them.
function SelectPeople(inputNames Varchar2Table) return People
begin
--stuff
select * from person_table where name in inputNames; --line of interest
--more stuff
end;
This doesn't seem to work though, I get the following error:
ORA-00932: inconsistent datatypes: expected NUMBER got
ENGSPL5.VARCHAR2TABLE
Any suggestions?
The TABLE operator allows nested tables to be used in SQL statements. The function was also missing an IS and an INTO.
create or replace type Varchar2Table is table of varchar2(200);
create table person_table(id number, name varchar2(100));
create or replace function SelectPeople(inputNames Varchar2Table) return number
is --Missing "IS".
type numberTable is table of number; --Need a collection to store results.
numbers numberTable;
begin
select id
bulk collect into numbers --Missing "INTO".
from person_table
where name in (select column_value from table(inputNames)); --Missing "TABLE".
--Alternatively a multiset condition can be used.
--where name member of inputNames;
--Dummy return value to make the function compile.
return 1;
end;
/

Return multiple rows from a stored procedure

How do I create a stored procedure which can return multiple rows using SQL Developer BTW.?
Right now stored procedure returns the value for 1 row in 4 diff variables (there are 4 cols)
How I would go about making it so that it could return more than 1 row, for example if i were to query in my date it could return all the relevant data for that date instead of only 1.
create or replace PROCEDURE P2
(
ts IN TIMESTAMP,
u_id OUT VARCHAR2,
u_email OUT VARCHAR2,
cmnt OUT VARCHAR2
)
AS
BEGIN
SELECT U_ID , U_EML, C_TX INTO u_id, u_email, cmnt
FROM U_CM
WHERE U_CM_TS = ts;
END;
ts is the input timestamp
if i put in more a timestamp that has multiple rows associated with it i get an error?
How do i change the design so I can be successful in doing what i want? I am new to this so I dont know where to start
Use ref cursor:
create or replace PROCEDURE P2
(
ts IN TIMESTAMP,
p_result OUT sys_refcursor
)
AS
BEGIN
open p_result for
SELECT U_ID , U_EML, C_TX
FROM U_CM
WHERE U_CM_TS = ts;
END;
Also, try to give a more detailed names to columns and tables for more maintainable code. For example, user_id, user_email instead of u_id, u_eml. What is c_tx? I have no idea. Read about table and column naming conventions.