Procedure parameter determines nested procedure parameter - oracle

I am wanting to write a procedure that takes an invoice number and then outputs the details of the customer that bought that particular product.
I have a procedure that displays the details of the customer using a id number as a parameter and that works fine!
I don't know how to use the invoice number to determine the ID value for the customer procedure.
Am using Oracle, sql plus
Code:
Customer details procedure:
SET SERVEROUTPUT ON;
CREATE OR REPLACE PROCEDURE custdets (in_number IN customers.c_ID%TYPE)
IS
CURSOR customer_cursor
IS
SELECT c_ID,
C_PH,
c_FName,
c_LName,
c_address1,
c_address2,
c_address3
FROM customers
WHERE in_number = c_id;
crow customer_cursor%ROWTYPE;
BEGIN
FOR crow IN customer_cursor
LOOP
DBMS_OUTPUT.PUT_LINE ('Customer Details');
DBMS_OUTPUT.PUT_LINE (
'CUST ID#: ' || crow.c_id || CHR (9) || 'PH: ' || crow.c_ph);
DBMS_OUTPUT.PUT_LINE (
'Customer Name: ' || crow.c_Fname || ',' || crow.c_Lname);
DBMS_OUTPUT.PUT_LINE (
'Address: '
|| crow.c_address1
|| ','
|| crow.c_address2
|| ','
|| crow.c_address3);
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE (SQLERRM);
END;
/
Current Query:
CREATE OR REPLACE PROCEDURE SalePurchase(in_invoice IN sales_purchases.sp_invoice%TYPE)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE('###########################################');
DBMS_OUTPUT.PUT_LINE('# P U R H A S E - S A L E - S U M M A R Y #');
DBMS_OUTPUT.PUT_LINE('# INVOICE NO: ' || in_invoice || ' #');
DBMS_OUTPUT.PUT_LINE(chr(13));
DBMS_OUTPUT.PUT_LINE('###########################################');
DBMS_OUTPUT.PUT_LINE(CHR(13));
DBMS_OUTPUT.PUT_LINE('############################################');
custdets();
end;
/
Thanks in advance!
Edit:
Have worked out that when i place an ID number in the procedure i want to call, eg custdets(4), it prints out the customer with the ID of that number. This is regardless of if they made the purchase or not.

Invoice number should be the primary key of, I don't know, a table called INVOICES maybe? It's your schema, you know it better than we can. Anyhow, that table should have a column called C_ID or perhaps customer_id which is the ID of the customer being invoiced; that column is a foreign key to the CUSTOMERS table.
So, your new procedure must take Invoice ID as a parameter. Use that value to look up the INVOICES record. That will give you the C_ID to pass into your existing custdets() procedure and display the Customer details.
"They are both in the same table how do i pass it it to custdets()"
Something like this:
CREATE OR REPLACE PROCEDURE SalePurchase(
in_invoice IN sales_purchases.sp_invoice%TYPE)
IS
l_customer_id sales_purchases.c_id%type;
BEGIN
select c_id into l_customer_id
from sales_purchases
where sp_invoice = in_invoice;
custdets(l_customer_id);
END;

Related

Getting the number of rows with same date in a procedure (PL/SQL)

I have been working this for a while now, and I am confused as to why I am getting "exact fetch returns more than requested number of rows" error. I even followed the answer for this Udemy course but that was giving the same error as well.
This is the code:
CREATE OR REPLACE PROCEDURE GET_SALE_COUNT
(
c_order_id IN NUMBER,
c_count_sales OUT NUMBER
)
AS
c_sales_date sales.sales_date%TYPE;
c_product_id sales.product_id%TYPE;
c_customer_id sales.customer_id%TYPE;
c_salesperson_id sales.salesperson_id%TYPE;
c_quantity sales.quantity%TYPE;
c_unit_price sales.unit_price%TYPE;
c_sales_amount sales.sales_amount%TYPE;
c_tax_amount sales.tax_amount%TYPE;
c_total_amount sales.total_amount%TYPE;
BEGIN
SELECT sales_date,product_id,customer_id,salesperson_id,quantity,unit_price,sales_amount,tax_amount,total_amount
INTO c_sales_date,c_product_id,c_customer_id,c_salesperson_id,c_quantity,c_unit_price,c_sales_amount,c_tax_amount,c_total_amount
FROM sales WHERE c_order_id = sales.order_id;
DBMS_OUTPUT.put_line('Sales Date: ' || c_sales_date);
DBMS_OUTPUT.put_line('Product ID: ' || c_product_id);
DBMS_OUTPUT.put_line('Customer ID: ' || c_customer_id);
DBMS_OUTPUT.put_line('Salesperson ID: ' || c_salesperson_id);
DBMS_OUTPUT.put_line('Quantity: ' || c_quantity);
DBMS_OUTPUT.put_line('Unit Price: ' || c_unit_price);
DBMS_OUTPUT.put_line('Sales Amount: ' || c_sales_amount);
DBMS_OUTPUT.put_line('Tax Amount: ' || c_tax_amount);
DBMS_OUTPUT.put_line('Total Amount: ' || c_total_amount);
SELECT COUNT(1) INTO c_count_sales FROM sales WHERE sales_date = c_sales_date;
END GET_SALE_COUNT;
DECLARE
count_sales NUMBER;
BEGIN
GET_SALE_COUNT(1267, count_sales);
DBMS_OUTPUT.put_line('Sale Count: ' || count_sales);
END
;
If your task is to count number of rows, why are you doing other unnecessary stuff? Because, it is the first select you wrote that returns more than exactly one row (and returns TOO_MANY_ROWS error). Why did that happen? Because not only one row exists for c_order_id parameter you pass. Run that statement out of the procedure and you'll see.
Therefore, as your task isn't to do what you did, skip it and use something like this:
create or replace procedure get_sale_count
(c_order_id in number,
c_count_sales out number
)
is
begin
select count(*)
into c_count_sales
from sales a
where a.sales_date in (select b.sales_date
from sales b
where b.order_id = c_order_id);
end;
/
If you do need to display various data, then do it in a loop.

CURSOR IN PROCEDURE PL/SQL

i have successfully created procedure for checking my stock item, here's the syntax :
create or replace procedure check_stock
AS
CURSOR check_stock IS
select category.category_name, item.item_name, item.stock from category join item on category.category_id = item.category_id;
begin
for stock in check_stock
LOOP
DBMS_OUTPUT.PUT_LINE(stock.category_name||' '||stock.item_name||' '||stock.stock);
END LOOP;
End;
now i want to create the same procedure but i need to input the item name so the output is the stock of that item name i have inputted, can someone show me the way/syntax using the syntax i've given above ? thanks
create or replace procedure check_stock ( v_item_name in Integer )
AS
CURSOR check_stock IS
select category.category_name, item.item_name, item.stock from category join item on category.category_id = item.category_id where item.item_name = v_item_name ;
begin
for stock in check_stock
LOOP
DBMS_OUTPUT.PUT_LINE(stock.category_name||' '||stock.item_name||' '||stock.stock);
END LOOP;
End;
You need to use one IN and one OUT parameter :
SQL> SET SERVEROUTPUT ON
SQL> CREATE OR REPLACE PROCEDURE check_stock(
i_item_name in item.item_name%type,
o_stock out category.stock%type
) AS
CURSOR check_stock IS
SELECT c.category_name, i.item_name, i.stock
FROM category c
JOIN item i
ON c.category_id = i.category_id
WHERE i.item_name = i_item_name;
BEGIN
FOR stock IN check_stock
LOOP
DBMS_OUTPUT.PUT_LINE(stock.category_name || ' ' || stock.item_name || ' ' || stock.stock);
o_stock := nvl(o_stock,0) + stock.stock;
END LOOP;
END;
/
but this way, you get the last value of stock from the cursor for multiple returning rows. It's unclear which value for stock value. So, I considered the summing up the returning stock values.

How do I fix my procedure country_demographics?

CREATE OR REPLACE PROCEDURE country_demographics
(p_country_name IN countries.country_name%TYPE,
p_country_demo_rec OUT ed_type)
IS
TYPE ed_type IS RECORD (
c_name countries.country_name%TYPE,
c_location countries.location%TYPE,
c_capitol countries.capitol%TYPE,
c_population countries.population%TYPE,
c_airports countries.airports%TYPE,
c_climate countries.climate%TYPE);
BEGIN
SELECT country_name, location, capitol, population, airports, climate
INTO ed_type.c_name, ed_type.c_location, ed_type.c_capitol, ed_type.population, ed_type.airports, ed_type.climate
FROM countries;
DBMS_OUTPUT.PUT_LINE('Country Name:' || v_country_demo_rec.country_name ||
'Location:' || v_country_demo_rec.location ||
'Capitol:' || v_country_demo_rec.capitol ||
'Population:' || v_country_demo_rec.population ||
'Airports:' || v_country_demo_rec.airports ||
'Climate:' || v_country_demo_rec.climate );
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20201, 'This country does not exist.');
END IF;
END;
The problem is asking me to create a procedure called country_demograhics. Pass the country_name as an IN parameter. Display CONTRY_NAME, LOCATION, CAPITOL, POPULATION, AIRPORTS, CLIMATE. Use a user-defined record structure for the INTO clause of your select statement. Raise an exception if the country does not exist.
Now here is a copy of my code, that keeps coming back with an error of:
Error at line 0: PL/SQL: Compilation unit analysis terminated.
That error should be the second error which tells you, it will not look any further. There should be another error too. I guess that ed_type doesn't exist outside of the procedure so it can not have an ed_type as OUT parameter. ed_type isn't known outside.
First thing - Look you used the different variable in declaring(p_country_demo_rec ) and begin(v_country_demo_rec) part. I think that might be one mistake.
Try following script:- it may help you.
CREATE OR REPLACE PROCEDURE COUNTRY_DEMOGRAPHICS
IS
TYPE ED_TYPE IS TABLE OF countries%ROWTYPE;
p_country_demo_rec ED_TYPE;
BEGIN
SELECT * BULK COLLECT INTO p_country_demo_rec FROM countries;
FOR i IN p_country_demo_rec.FIRST..p_country_demo_rec.LAST
LOOP
DBMS_OUTPUT.PUT_LINE('Country Name:'||p_country_demo_rec(i).country_name ||
'Location:' || p_country_demo_rec(i).location ||
'Capitol:' || p_country_demo_rec(i).capitol ||
'Population:' || p_country_demo_rec(i).population ||
'Airports:' || p_country_demo_rec(i).airports ||
'Climate:' || p_country_demo_rec(i).climate );
END LOOP;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20201, 'This country does not exist.');
END IF;
END;
/
EXECUTE COUNTRY_DEMOGRAPHICS;
Note:- You can use the one parameter(IN parameter) in a procedure to get the specific country demographics data and that parameter use in select statement for filter out the specific country.
Example:
CREATE OR REPLACE PROCEDURE COUNTRY_DEMOGRAPHICS(p_country_name IN varchar2)
Select statement looks like:
SELECT * BULK COLLECT INTO p_country_demo_rec FROM countries where
country_name = ||p_country_name;
Execute part:
EXECUTE COUNTRY_DEMOGRAPHICS(p_country_name);

Output results from Oracle Ref cursor

How can i rewrite below pl/sql block in order to avoid hard code column names one by one ? Below data are from OE schema. For orders table there are 8 columns on this table. Is it possible to output the results without hard code column names ? Any help is appreciated.
create or replace PACKAGE show_details AS
TYPE rt_order IS REF CURSOR RETURN orders%ROWTYPE;
TYPE typ_cust_rec IS RECORD
(cust_id NUMBER(6), cust_name VARCHAR2(20),
custphone customers.phone_numbers%TYPE,
credit NUMBER(9,2), cust_email VARCHAR2(30));
TYPE rt_cust IS REF CURSOR RETURN typ_cust_rec;
--Get order detail
PROCEDURE get_order(p_orderid IN NUMBER, p_cv_order IN OUT rt_order);
--Get customer detail
PROCEDURE get_cust(p_custid IN NUMBER, p_cv_cust IN OUT rt_cust);
END show_details;
create or replace PACKAGE BODY show_details
AS
PROCEDURE get_order (p_orderid IN NUMBER, p_cv_order IN OUT rt_order)
IS
BEGIN
OPEN p_cv_order FOR
SELECT * FROM orders
WHERE order_id = p_orderid;
-- CLOSE p_cv_order
END get_order;
PROCEDURE get_cust (p_custid IN NUMBER, p_cv_cust IN OUT rt_cust)
IS
BEGIN
OPEN p_cv_cust FOR
SELECT customer_id, cust_first_name,phone_numbers,
credit_limit,cust_email FROM customers WHERE customer_id = p_custid;
-- CLOSE p_cv_cust
END get_cust;
END;
SET SERVEROUTPUT ON SIZE UNLIMITED;
declare
cur_orders show_details.rt_order;
v_ordertab cur_orders%ROWTYPE;
begin
show_details.get_order(p_orderid =>2397, p_cv_order =>cur_orders);
LOOP
FETCH cur_orders INTO v_ordertab;
EXIT WHEN cur_orders%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('ORDER_ID: ' || v_ordertab.ORDER_ID || ' ORDER_DATE: ' || v_ordertab.ORDER_DATE || ' ORDER_MODE: ' || v_ordertab.ORDER_MODE || ' CUSTOMER_ID: ' || v_ordertab.CUSTOMER_ID);
END LOOP;
exception
when others then
DBMS_OUTPUT.put_line ('Error Code : ' || SQLCODE);
end;
/
Since you're executing this from SQL Developer, you can use the variable and print commands, which are carried over from SQL*Plus:
variable cur_orders refcursor;
exec show_details.get_order(p_orderid => 2397, p_cv_order => :cur_orders);
print cur_orders
CUR_ORDERS
-------------------------------------------------------------------------------------------------------------------------------------------------
ORDER_ID CUSTOMER_ID ORDER_MODE ORDER_DATE
--------------------------------------- --------------------------------------- --------------------------------------- -------------------------
2397 42 0 03-JAN-15
Notice that you're passing the ref cursor variable as a bind variable, so there is a colon before the name in the call (:cur_order). And you need to run script rather than run statement.
exec is just a shorthand for an anonymous block, so you could do it explicitly if you prefer, but the effect is the same:
var cur_orders refcursor;
begin
show_details.get_order(p_orderid => 2397, p_cv_order => :cur_orders);
end;
/
print cur_orders
You can also get the output in the result grid if you prefer, as shown here, but print is a bit closer to your dbms_output version. Or you can have a wrapper function so you could call query the procedure from a plain SQL call; depends what your end goal is, and if you're just manually executing the procedure to check the output then print may also be good enough.

oracle Stored Procedure to get the sum(balance) given a parameter

I have a supplier_product table ( supp_id, prod_id, invoice_id,price) and an invoice table (invoice_id, balance). I tried a stored proc. given (supp_id) it should all the existing invoice_id and display the balance. here's my code:
set serverouput on;
create or replace
Procedure SUP_loop
(v_SUPPLIER_ID int )
AS
CURSOR c_SUP IS
select SUPPLIER_ID , SUPP_INVOICE_ID, balance
from SUPPLIER_PRODUCT, supplier_invoice
where SUPPLIER_ID=v_SUPPLIER_ID
and supp_invoice_id.supplier_product=supp_invoice_id.supplier_invoice;
BEGIN
--LOOP WITH IMPLICIT VARIABLE DECLARED
--AUTOMATIC, OPEN FETCH, CLOSE
FOR v_SUP_data IN c_SUP LOOP
DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_SUP_data.SUPPLIER_ID) || ' ' ||
TO_CHAR(v_SUP_data.SUPP_INVOICE_ID) || ' ' ||
TO_CHAR(v_SUP_data.balance) );
END LOOP;
END;
/
the error i am getting is v_sup_data Error(20,31): PLS-00364: loop index variable 'V_SUP_DATA' use is invalid
Error(9,74): PL/SQL: ORA-00904: "SUPP_INVOICE_ID"."SUPPLIER_INVOICE": invalid identifier
You have the field and the table names swapped round the wrong way.
You have...
supp_invoice_id.supplier_invoice
...where you should have...
supplier_invoice.supp_invoice_id
:D
The syntax for referring to a column is <>.<>. So your cursor query needs the join condition to be supplier_produce.supp_invoice_id = supplier_invoice.supp_invoice_id, i.e.
create or replace
Procedure SUP_loop
(v_SUPPLIER_ID int )
AS
CURSOR c_SUP IS
select SUPPLIER_ID , SUPP_INVOICE_ID, balance
from SUPPLIER_PRODUCT, supplier_invoice
where SUPPLIER_ID=v_SUPPLIER_ID
and supplier_product.supp_invoice_id = supplier_invoice.supp_invoice_id;
BEGIN
--LOOP WITH IMPLICIT VARIABLE DECLARED
--AUTOMATIC, OPEN FETCH, CLOSE
FOR v_SUP_data IN c_SUP LOOP
DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_SUP_data.SUPPLIER_ID) || ' ' ||
TO_CHAR(v_SUP_data.SUPP_INVOICE_ID) || ' ' ||
TO_CHAR(v_SUP_data.balance) );
END LOOP;
END;
/

Resources