How do you call a Procedure in Block SQL - sql

My procedure is below;
I have to create an anonymous block that CALLS the procedure and DISPLAYS all the customers total price and number of cars purchased from that PARTICULAR city
Can someone show me how to do this BLOCK
My procedure is as FOLLOW:
CREATE OR REPLACE PROCEDURE getCars
( v_custname OUT car.custname%TYPE,
v_purchcost OUT car.purchcost%TYPE,
v_city IN customer.custcity%TYPE,
v_count OUT NUMBER,
v_total OUT car.purchcost%TYPE
)
AS
BEGIN
Select c.custname, Count(c.custname), SUM(purchcost)
INTO v_custname, v_count, v_total
From car c
JOIN customer cs
ON c.custname = cs.custname
WHERE cs.custcity = v_city
Group By c.custname;
END;
/
This is what I have for the BLOCK to call procedure BUT ITS NOT WORKING
SET SERVEROUTPUT ON
SET VER OFF
ACCEPT p_city PROMPT 'Enter City Name: ';
DECLARE
CURSOR cname IS
Select customer.custname
INTO custname
From customer
Where UPPER(custcity) = UPPER('&p_city');
BEGIN
FOR
v_car in cname
LOOP
getCars(v_car.custname, v_count, v_total);
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_car.custname||v_count||v_total);
END;
/

If you only want to display them the following can be done:
CREATE OR REPLACE PROCEDURE getcars (
v_city IN customer.custcity%TYPE
) AS
BEGIN
FOR c IN (
SELECT
car.custname,
COUNT(car.custname) count_custname,
SUM(purchcost) purchcost
FROM
car
JOIN customer cs ON car.custname = cs.custname
WHERE
cs.custcity = v_city
GROUP BY
car.custname
) LOOP
dbms_output.put_line('Customer '
|| c.custname
|| ' has bought '
|| c.count_custname
|| ' totaling '
|| purchcost);
END LOOP;
END;
Call the procedure:
DECLARE
v_city customer.custcity%TYPE := 'some city';
BEGIN
getcars(v_city);
END;
Otherwise if you need to return it for each customer then you should use cursors or more complicated data structures.

Related

Oracle SQL. Data range in procedure

I have SELECT which exports sale info(date, number) between two dates.
SELECT SALE_DATE, SALE_NUM
from SALES
where sale_date between '&fromdate' AND '&todate';
But I wanna make this with procedure. How to do it? What I have tried:
create or replace procedure SALE_DATES(var_saledate in SALES.SALE_DATE%TYPE)
AS
BEGIN
DECLARE Cursor c4 IS
SELECT SALE_DATE, SALE_NUM
from SALES
where sale_date = var_saledate AND var_saledate between '&fromdate' AND '&todate';
BEGIN FOR item IN c4
LOOP
DBMS_OUTPUT.PUT_LINE
('Date= ' || item.sale_date || ', Sale number: ' || item.sale_num);
END LOOP;
END;
END;
start of the procedure:
begin SALE_DATES(.....);
end;
What parameter should I use in the braces?
Two parameters so you can pass in your date range... I've used more standard naming conventions and laid it out a little clearer.
CREATE OR REPLACE PROCEDURE get_sales
(P_from_date IN DATE
,P_to_date IN DATE
)
AS
CURSOR C_get_sales
IS
SELECT
s.sale_date
,s.sale_num
FROM
sales s
WHERE s.sale_date BETWEEN P_from_date AND P_to_date
;
BEGIN
FOR R_this_sale IN C_get_sales LOOP
DBMS_OUTPUT.PUT_LINE
('Date= ' || R_this_sale.sale_date || ', Sale number: ' || R_this_sale.sale_num)
;
END LOOP;
END;
SQL> set serveroutput on; -- for dbms_output.put_line to take effect
SQL> CREATE OR REPLACE PROCEDURE SALE_DATES( v_sale_date_from
sales.sale_date%type, v_sale_date_to sales.sale_date%type )
AS
Cursor c4 IS
select SALE_DATE, SALE_NUM
from SALES
where sale_date between v_sale_date_from AND v_sale_date_to ;
BEGIN
FOR item IN c4
LOOP
DBMS_OUTPUT.PUT_LINE ('Date= ' || item.sale_date || ', Sale number: ' || item.sale_num);
END LOOP;
END;/
SQL> exec SALE_DATES(to_date('&dt1','dd.mm.yyyy'),to_date('&dt2','dd.mm.yyyy'));/
-- assuming your nls_date_format is dd.mm.yyyy, when prompted enter 19.12.2017 as an example.

How stored procedure returns from multiple cursor

I have Oracle Stored Procedure below and I understood most of the logic inside except for the part where the caller of the procedure will get all the values from different cursors.
Did some reading that SP returns the OUT part in a procedure parameters shown in the sample below. But i fail to get any reference as to how p_returnCode can store resultsets from queries inside the stored procedure.
Procedure retrieveX(p_date date, p_loc varchar2, p_returnCode out integer)
The stored procedure below have 3 cursors cur1, cur2 and cur3. How or where does it store the values? Cur1 contains 2 columns with multiple rows and Cur2 and Cur3 contains one column with multiple rows.
Can anyone clarify this part?
Caller from cgi script
report.retrieveX(p_date,p_loc,p_return)
Full Stored Procedure
PROCEDURE retrieveX(
p_date DATE,
p_loc VARCHAR2,
p_returnCode OUT INTEGER
)
AS
TYPE cur_typ IS REF CURSOR;
cur1 cur_typ;
cur2 cur_typ;
cur3 cur_typ;
query_str VARCHAR2(2000) := '';
query_str2 VARCHAR2(2000) := '';
query_str3 VARCHAR2(2000) := '';
v_an VARCHAR2(20);
v_tn VARCHAR2(20);
v_sOID varchar2(20);
BEGIN
sqlRouteDT := 'AND sp.ROUTE_DT = TO_DATE(''' || TO_CHAR(p_date, 'YYYY/MM/DD') || ''',''YYYY/MM/DD'')';
IF p_loc IS NOT NULL THEN
sqlLocation := 'AND act.location_cd = ''' || p_loc || '''';
END IF;
p_returnCode := 0;
query_str := '
SELECT distinct
sp.ab,
y.track,
FROM ship sp
inner join activ act on sp.soid=act.on
inner join peace y on act.on=y.soid
where
sp.man is not null
' || sqlLocation || '
' || sqlRouteDT || '
ORDER BY sp.ab asc
';
OPEN cur1 FOR query_str1;
LOOP
FETCH cur1
INTO
v_AN,
v_FN
EXIT WHEN cur1%NOTFOUND;
query_str2 := '
SELECT DISTINCT INTER_CD
FROM TBL_INTR
WHERE AF = ''Y''
AND sOID = ''' || v_sOID || '''
ORDER BY INTER_CD
';
OPEN cur2 FOR query_str2;
LOOP
FETCH cur2
INTO v_intr_cd;
EXIT WHEN cur2%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('INTER_CD|' || v_intr_cd);
END LOOP;
CLOSE cur2;
query_str3 := '
SELECT DISTINCT hi_cd
FROM tbl_hi
WHERE AF = ''Y''
AND sOID = ''' || v_sOID || '''
ORDER BY hi_cd
';
OPEN cur3 FOR query_str3;
LOOP
FETCH cur3
INTO v_hi_c;
EXIT WHEN cur3%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('hi_cd|' || v_hi_c);
END LOOP;
CLOSE cur3;
END LOOP;
CLOSE cur1;
EXCEPTION WHEN OTHERS THEN
p_returnCode := 1;
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END retrieveX;
I don't think possible to do that you want.
Maybe you should to considerate the use of pipelined function, please take a look at https://oracle-base.com/articles/misc/pipelined-table-functions
With this technique, you can write a pl/sql code to access to complex data and relationship table and get the result as a simple
select * from StoredProcedure(Parameter_1...);

Cursor inside procedure body

I am trying to declare a cursor inside procedure body.
I know it is supposed to be done in the declare block but the table the cursor refers is created inside the procedure body.
--TABLE MAY OR MAY NOT BE PRESENT PRIOR TO PROCEDURE EXECUTION
SELECT COUNT(*)
INTO ln_cnt
FROM User_Tables
WHERE table_name = 'TMP$UOM_COMBO_GEN';
IF ln_cnt > 0 THEN
EXECUTE IMMEDIATE ' CREATE TABLE TMP$UOM_COMBO_GEN (UOM_ID VARCHAR2(20 BYTE), HIER_CODE VARCHAR2(20 BYTE),NODE_CODE VARCHAR2(200 BYTE))';
END IF;
CURSOR C_HIER
IS
SELECT DISTINCT HIER_CODE FROM TMP$UOM_COMBO_GEN WHERE UOM_ID=P_UOM_ID;
FOR HIER IN C_HIER
LOOP
IF C_HIER%ROWCOUNT = 1 THEN
LV_SQL2 := '(SELECT UOM_ID, NODE_CODE '||HIER.HIER_CODE||' FROM TMP$UOM_COMBO_GEN WHERE UOM_ID='''||P_UOM_ID||''' AND HIER_CODE='''||HIER.HIER_CODE||''')'||HIER.HIER_CODE;
LV_SORT := ' ORDER BY '||HIER.HIER_CODE||'';
LV_SQL := 'SELECT * FROM ' || LV_SQL2;
ELSE
LV_SQL3 := ' LEFT OUTER JOIN(SELECT NODE_CODE '||HIER.HIER_CODE||' FROM TMP$UOM_COMBO_GEN WHERE UOM_ID='''||P_UOM_ID||''' AND HIER_CODE='''||HIER.HIER_CODE||''')'||HIER.HIER_CODE ||' ON 1=1';
LV_SORT := LV_SORT||','||HIER.HIER_CODE||'';
LV_SQL := LV_SQL || LV_SQL3;
END IF;
END LOOP;
I am getting the following error.
Error(17,10): PLS-00103: Encountered the symbol "C_HIER" when expecting one of the following: := . ( # % ;
Well a table once created is stored in database and u can refer it from wherever u want to refer in the schema .
Also there a change may be required in your code in the following part
SELECT COUNT(*)
INTO ln_cnt
FROM User_Tables
WHERE table_name = 'TMP$UOM_COMBO_GEN';
IF ln_cnt > 0 THEN -- means only if table exists u want to create the table which will
--throw an exception if table is already there ,
--so better equate it to 0
EXECUTE IMMEDIATE ' CREATE TABLE TMP$UOM_COMBO_GEN (UOM_ID VARCHAR2(20 BYTE), HIER_CODE VARCHAR2(20 BYTE),NODE_CODE VARCHAR2(200 BYTE))';
END IF;
Now , if u really have a requirement to create a new table every time some condition is true/false and u then want to select the table in a cursor do something like following by using reference cursors
create or replace procedure abc(Table_name varchar2 , param_list varchar2 , where_clause varchar2) is
c_hier sys_refcursor ;
LV_SQL2 varchar2(2000) ;
LV_SORT varchar2(2000) ;
LV_SQL varchar2(2000) ;
LV_SQL3 varchar2(2000) ;
begin
SELECT COUNT(*)
INTO ln_cnt
FROM User_Tables
WHERE table_name = Table_name; -- Use any table here
IF ln_cnt = 0 THEN
EXECUTE IMMEDIATE ' CREATE TABLE '||Table_name||' '||param_list;
END IF;
open c_hier for 'SELECT DISTINCT '||param_list||' FROM '||table_name||' '||where_clause;
FOR HIER IN C_HIER
LOOP
IF C_HIER%ROWCOUNT = 1 THEN
LV_SQL2 := '(SELECT UOM_ID, NODE_CODE '||HIER.HIER_CODE||' FROM TMP$UOM_COMBO_GEN WHERE UOM_ID='''||P_UOM_ID||''' AND HIER_CODE='''||HIER.HIER_CODE||''')'||HIER.HIER_CODE;
LV_SORT := ' ORDER BY '||HIER.HIER_CODE||'';
LV_SQL := 'SELECT * FROM ' || LV_SQL2;
ELSE
LV_SQL3 := ' LEFT OUTER JOIN(SELECT NODE_CODE '||HIER.HIER_CODE||' FROM TMP$UOM_COMBO_GEN WHERE UOM_ID='''||P_UOM_ID||''' AND HIER_CODE='''||HIER.HIER_CODE||''')'||HIER.HIER_CODE ||' ON 1=1';
LV_SORT := LV_SORT||','||HIER.HIER_CODE||'';
LV_SQL := LV_SQL || LV_SQL3;
END IF;
END LOOP;

select statement not working in procedure

create table Employee(Id number,
Name varchar(20),
Age number,
DId number,
Salary number,
primary key(Id),
foreign key(DId) references Department on delete cascade);
declare
total number;
procedure myFunction(x in number) is
begin
insert into Employee values(17,'Jaskaran Singh',31,1,200000);
dbms_output.put_line('successfully executed');
select * from Employee;
end;
begin
myFunction(3);
end;
To return data from stored procedure, you should create a cursor and return that select like the following:
CREATE OR REPLACE PACKAGE TYPES
AS
TYPE DATA_CURSOR IS REF CURSOR;
END;
then in your code is going to be like:
CREATE OR REPLACE PROCEDURE MYPROC(RESULTSET OUT TYPES.DATA_CURSOR) AS
BEGIN
INSERT INTO EMPLOYEE VALUES(17,'Jaskaran Singh',31,1,200000);
DBMS_OUTPUT.PUT_LINE('successfully executed');
OPEN RESULTSET FOR
SELECT * FROM EMPLOYEE;
END;
The Execution part like
DECLARE THE_RESULT_SET OUT TYPES.DATA_CURSOR;
BEGIN
MYPROC(3, THE_RESULT_SET);
-- You can now get the THE_RESULT_SET and take the result from it...
END;
Important: if you want to print as I understand the other case, you can get that result (same code), and loop whatever you want and print the result from the THE_RESULT_SET
If you want to print what's in the EMPLOYEES table you have loop a cursor over the EMPLOYEES table, printing each row appropriately. Here's an example:
DECLARE
TOTAL NUMBER;
PROCEDURE MYFUNCTION(X IN NUMBER) IS
BEGIN
INSERT INTO EMPLOYEE VALUES(17,'Jaskaran Singh',31,1,200000);
DBMS_OUTPUT.PUT_LINE('successfully executed');
FOR aRow IN (SELECT * FROM EMPLOYEE) LOOP
DBMS_OUTPUT.PUT_LINE('ID=' || aRow.ID ||
' NAME=''' || aRow.NAME || '''' ||
' AGE=' || aRow.AGE ||
' DID=' || aRow.DID ||
' SALARY=' || aRow.SALARY);
END LOOP;
END;
BEGIN
MYFUNCTION(3);
END;
Share and enjoy.

Any alternatives to using cursor in SQL procedure in Oracle 10g?

I give the SQL few inputs and I need to get all the ID's and their count that doesn't satisfy the required criteria.
I would like to know if there are there any alternatives to using cursor.
DECLARE
v_count INTEGER;
v_output VARCHAR2 (1000);
pc table1%ROWTYPE;
unmarked_ids EXCEPTION;
dynamic_sql VARCHAR (5000);
cur SYS_REFCURSOR;
id pp.id%TYPE;
pos INTEGER;
BEGIN
v_count := 0;
SELECT *
INTO pc
FROM table1
WHERE id = '&ID';
DBMS_OUTPUT.ENABLE;
dynamic_sql :=
'SELECT ID from pp
WHERE ( TO_CHAR(cdate, ''yyyy/mm/dd'') =
TO_CHAR (:a, ''yyyy/mm/dd''))
AND aid IN (SELECT aid FROM ppd WHERE TO_CHAR(cdate, ''yyyy/mm/dd'') =
TO_CHAR (:b, ''yyyy/mm/dd'')
AND cid = :c )
AND cid <> :d';
OPEN cur FOR dynamic_sql USING pc.cdate, pc.cdate, pc.id, pc.id;
LOOP
FETCH cur INTO id;
EXIT WHEN cur%NOTFOUND;
v_count := v_count + 1;
DBMS_OUTPUT.PUT_LINE (' Id:' || id);
END LOOP;
CLOSE cur;
IF (v_count > 0)
THEN
DBMS_OUTPUT.PUT_LINE ( 'Count: ' || v_count || ' SQL: ' || dynamic_sql);
RAISE unmarked_ids;
END IF;
DBMS_OUTPUT.PUT_LINE('SQL ended successfully');
EXCEPTION
WHEN unmarked_ids
THEN
DBMS_OUTPUT.put_line (
'Found ID's that not marked with the current id.');
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line (
'No data found in table1 with the current id ' || '&ID');
END;
There are bind variables in the query. One of them is date, there are three more.
The count and ID's are required to be shown which will later be reported.
You could store the rowid in a temporary table along with an index value (0...n) and then use a while loop to go through the index values and join to the real table using the rowid.