Oracle SQL. Data range in procedure - sql

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.

Related

How do you call a Procedure in Block 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.

Oracle PL/SQL How to store and fetch a dynamic multi column query

I am trying hard dynamic PL/SQL thing here.
I don't manage to fetch a column dynamic Query.
I am iterating on the name of the column to concatenate a full query in order to be executed on another table.
sql_req := 'select ';
for c in (SELECT name_col from TAB_LISTCOL)
loop
sql_req := sql_req || 'sum(' || c.name_col || '),';
end loop;
sql_req := sql_req || ' from ANOTHER_TAB ';
And when i try to execute it with EXECUTE IMMEDIATE or cursors or INTO/BULK COLLECT thing or just to fetch, i don't manage to iterate on the result.
I tried a lot.
Can you help me plz ? Or maybe it is not possible ?
ps : i know the coma is wrong but my code is more complexe than this : i didn't want to put more things
If you only want to get string columns, you can use listagg
select listagg(name_col, ',') WITHIN GROUP (ORDER BY null) from TAB_LISTCOL
Please see if this helps
In the absence of actual table structure and requirement, I'm creating dummy tables and query to illustrate an example:
SQL> create table another_tab
as
select 10 dummy_value1, 100 dummy_value2, 1000 dummy_value3 from dual union all
select 11 dummy_value1, 101 dummy_value2, 1001 dummy_value3 from dual union all
select 12 dummy_value1, 102 dummy_value2, 1003 dummy_value3 from dual
;
Table created.
SQL> create table tab_listcol
as select column_name from dba_tab_cols where table_name = 'ANOTHER_TAB'
;
Table created.
To reduce complexity in the final block, I'm defining a function to generate the dynamic sql query. This is based on your example and will need changes according to your actual requirement.
SQL> create or replace function gen_col_based_query
return varchar2
as
l_query varchar2(4000);
begin
l_query := 'select ';
for cols in ( select column_name cname from tab_listcol )
loop
l_query := l_query || 'sum(' || cols.cname || '), ' ;
end loop;
l_query := rtrim(l_query,', ') || ' from another_tab';
return l_query;
end;
/
Function created.
Sample output from the function will be as follows
SQL> select gen_col_based_query as query from dual;
QUERY
--------------------------------------------------------------------------------
select sum(DUMMY_VALUE1), sum(DUMMY_VALUE2), sum(DUMMY_VALUE3) from another_tab
Below is a sample block for executing a dynamic cursor using DBMS_SQL. For your ease of understanding, I've added comments wherever possible. More info here.
SQL> set serveroutput on size unlimited
SQL> declare
sql_stmt clob;
src_cur sys_refcursor;
curid number;
desctab dbms_sql.desc_tab; -- collection type
colcnt number;
namevar varchar2 (50);
numvar number;
datevar date;
l_header varchar2 (4000);
l_out_rows varchar2 (4000);
begin
/* Generate dynamic sql from the function defined earlier */
select gen_col_based_query into sql_stmt from dual;
/* Open cursor variable for this dynamic sql */
open src_cur for sql_stmt;
/* To fetch the data, however, you cannot use the cursor variable, since the number of elements fetched is unknown at complile time.
Therefore you use DBMS_SQL.TO_CURSOR_NUMBER to convert a REF CURSOR variable to a SQL cursor number which you can then pass to DBMS_SQL subprograms
*/
curid := dbms_sql.to_cursor_number (src_cur);
/* Use DBMS_SQL.DESCRIBE_COLUMNS to describe columns of your dynamic cursor, returning information about each column in an associative array of records viz., desctab. The no. of columns is returned in colcnt variable.
*/
dbms_sql.describe_columns (curid, colcnt, desctab);
/* Define columns at runtime based on the data type (number, date or varchar2 - you may add to the list)
*/
for indx in 1 .. colcnt
loop
if desctab (indx).col_type = 2 -- number data type
then
dbms_sql.define_column (curid, indx, numvar);
elsif desctab (indx).col_type = 12 -- date data type
then
dbms_sql.define_column (curid, indx, datevar);
else -- assuming string
dbms_sql.define_column (curid, indx, namevar, 100);
end if;
end loop;
/* Print header row */
for i in 1 .. desctab.count loop
l_header := l_header || ' | ' || rpad(desctab(i).col_name,20);
end loop;
l_header := l_header || ' | ' ;
dbms_output.put_line(l_header);
/* Loop to retrieve each row of data identified by the dynamic cursor and print output rows
*/
while dbms_sql.fetch_rows (curid) > 0
loop
for indx in 1 .. colcnt
loop
if (desctab (indx).col_type = 2) -- number data type
then
dbms_sql.column_value (curid, indx, numvar);
l_out_rows := l_out_rows || ' | ' || rpad(numvar,20);
elsif (desctab (indx).col_type = 12) -- date data type
then
dbms_sql.column_value (curid, indx, datevar);
l_out_rows := l_out_rows || ' | ' || rpad(datevar,20);
elsif (desctab (indx).col_type = 1) -- varchar2 data type
then
dbms_sql.column_value (curid, indx, namevar);
l_out_rows := l_out_rows || ' | ' || rpad(namevar,20);
end if;
end loop;
l_out_rows := l_out_rows || ' | ' ;
dbms_output.put_line(l_out_rows);
end loop;
dbms_sql.close_cursor (curid);
end;
/
PL/SQL procedure successfully completed.
Output
| SUM(DUMMY_VALUE1) | SUM(DUMMY_VALUE2) | SUM(DUMMY_VALUE3) |
| 33 | 303 | 3004 |
You have to use EXECUTE IMMEDIATE with BULK COLLECT
Below is an example of the same. For more information refer this link
DECLARE
TYPE name_salary_rt IS RECORD (
name VARCHAR2 (1000),
salary NUMBER
);
TYPE name_salary_aat IS TABLE OF name_salary_rt
INDEX BY PLS_INTEGER;
l_employees name_salary_aat;
BEGIN
EXECUTE IMMEDIATE
q'[select first_name || ' ' || last_name, salary
from hr.employees
order by salary desc]'
BULK COLLECT INTO l_employees;
FOR indx IN 1 .. l_employees.COUNT
LOOP
DBMS_OUTPUT.put_line (l_employees (indx).name);
END LOOP;
END;
If I understand correctly, you want to create a query and execute it and return the result to another function or some calling app. As the resulting query's columns are note before-known, I'd return a ref cursor in this case:
create function get_sums return sys_refcur as
declare
my_cursor sys_refcursor;
v_query varchar2(32757);
begin
select
'select ' ||
listagg('sum(' || name_col || ')', ', ') within group (order by name_col) ||
' from another_tab'
into v_query
from tab_listcol;
open my_cursor for v_query;
return v_query;
end get_sums;

configuring the oracle procedure so that it should accept the input value

I have created an procedure which delete the 3 months old partition from the table now i want to make the below procedure configurable enough such that it should accept the count from the user so please advise how can i modify the below procedure
create or replace
PROCEDURE Delete_partitions
/*
This procedure will delete partitions for the following tables:
TEMPTABLE
*/
BEGIN
FOR cc IN
(
SELECT partition_name, high_value
FROM user_tab_partitions
WHERE table_name = 'TEMPTABLE'
)
dbms_output.put_line('starting to drop partition ');
LOOP
EXECUTE IMMEDIATE 'BEGIN
IF sysdate >= ADD_MONTHS(' || cc.high_value || ', 2) THEN
EXECUTE IMMEDIATE
''ALTER TABLE TEMPTABLE DROP PARTITION ' || cc.partition_name || '
'';
END IF;
dbms_output.put_line('drop partition completed');
END;';
END LOOP;
exception
when others then
dbms_output.put_line(SQLERRM);
END;
/
Add some parameters to it:
create or replace
PROCEDURE Delete_partitions(cnt in number)
/*
This procedure will delete partitions for the following tables:
TEMPTABLE
*/
BEGIN
FOR cc IN
(
SELECT partition_name, high_value
FROM user_tab_partitions
WHERE table_name = 'TEMPTABLE'
)
dbms_output.put_line('starting to drop partition ');
LOOP
EXECUTE IMMEDIATE 'BEGIN
IF sysdate >= ADD_MONTHS(' || cc.high_value || ', ' || cnt-1 || ') THEN
EXECUTE IMMEDIATE
''ALTER TABLE TEMPTABLE DROP PARTITION ' || cc.partition_name || '
'';
END IF;
dbms_output.put_line('drop partition completed');
END;';
END LOOP;
exception
when others then
dbms_output.put_line(SQLERRM);
END;
/

How I can resolve this Procedure in Oracle PLSQL (It's not homework or something like that)

I'm learning Oracle Database and PL/SQL. I'm trying to create a PROCEDURE to query annual salary for an specific employee. What is wrong with my code? Thanks.
CREATE OR REPLACE PROCEDURE annual_salary (
p_lname IN employees23.last_name%TYPE,
p_empid IN employees23.employee_id%TYPE)
IS
BEGIN
DECLARE
v_annualsal employees23.salary%TYPE;
BEGIN
SELECT salary*12
INTO v_annualsal
FROM employees23
WHERE last_name = p_lname;
DBMS_OUTPUT.PUT_LINE(v_annualsal);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No existe empleado');
END;
END annual_salary;
/
BEGIN
annual_salary('Russell');
END;
create table employees23 as
select 10 emp_id, cast('Russell' as varchar2(30)) last_name, 6000.00 salary
from dual;
create or replace procedure annual_salary (lname employees23.last_name%type) is
annualsal employees23.salary%type;
begin
select salary*12 into annualsal
from employees23
where last_name = lname
;
dbms_output.put_line (lname || ' has ' || annualsal || ' p.a.');
exception when no_data_found then
raise_application_error (-20000, 'emploee ' || lname || ' does not exists');
end annual_salary;
/
exec annual_salary('Russell');

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.