How to search for a month that is input by the user - sql

I am working on some homework and have been stuck on this for a week. I have tried using TO_CHAR, MONTH(search), and EXTRACT(MONTH from...) and they all end up with either identifier 'JAN'(the month I am searching for) is not declared, or expression is of the wrong type. This assignment is to display all the rows for pledges made in a specified month. The column PLEDGEDATE is of type Date in the format 'dd-mmm-yy'. Any ideas how to make this work?
Declare
Pledges UNIT_2_ASSIGNMENT%ROWTYPE;
SEARCH DATE;
Begin
SEARCH := &M0NTH;
FOR PLEDGES IN
(SELECT IDPLEDGE, IDDONOR, PLEDGEAMT,
CASE
WHEN PAYMONTHS = 0 THEN 'LUMP SUM'
ELSE'MONHTLY - '||PAYMONTHS
END AS MONTHLY_PAYMENT
FROM UNIT_2_ASSIGNMENT
WHERE TO_CHAR(PLEDGEDATE,'MMM') = 'SEARCH'
ORDER BY PAYMONTHS)
LOOP
DBMS_OUTPUT.PUT_LINE('Pledge ID: '||UNIT_2_ASSIGNMENT.IDPLEDGE||
' Donor ID: '||UNIT_2_ASSIGNMENT.IDDONOR||
' Pledge Amount: '||TO_CHAR(UNIT_2_ASSIGNMENT.PLEDGEAMT)||
' Lump Sum: '||MONTHLY_PAYMENT);
END LOOP;
END;

You can use (comments on changes are inline):
DECLARE
Pledges UNIT_2_ASSIGNMENT%ROWTYPE;
SEARCH VARCHAR2(3); -- Use VARCHAR2 not DATE data type.
BEGIN
SEARCH := 'JAN'; -- Replace with your substitution variable.
FOR PLEDGES IN (
SELECT IDPLEDGE,
IDDONOR,
PLEDGEAMT,
CASE
WHEN PAYMONTHS = 0
THEN 'LUMP SUM'
ELSE 'MONHTLY - '||PAYMONTHS
END AS MONTHLY_PAYMENT
FROM UNIT_2_ASSIGNMENT
WHERE TO_CHAR(PLEDGEDATE,'MON') = SEARCH -- Unquote variable and use MON not MMM
ORDER BY PAYMONTHS
)
LOOP
DBMS_OUTPUT.PUT_LINE(
'Pledge ID: '||Pledges.IDPLEDGE|| -- Use rowtype variable name not table name.
' Donor ID: '||Pledges.IDDONOR||
' Pledge Amount: '||TO_CHAR(Pledges.PLEDGEAMT)||
' Lump Sum: '||Pledges.MONTHLY_PAYMENT
);
END LOOP;
END;
/
Which, for the sample data:
CREATE TABLE unit_2_assignment( idpledge, iddonor, pledgeamt, pledgedate, paymonths ) AS
SELECT LEVEL,
'Donor' || LEVEL,
LEVEL * 1000,
ADD_MONTHS( DATE '2020-01-01', LEVEL - 1 ),
LEVEL
FROM DUAL
CONNECT BY LEVEL <= 12;
Outputs:
Pledge ID: 1 Donor ID: Donor1 Pledge Amount: 1000 Lump Sum: MONHTLY - 1

You should enclose your substitution variable into single quotes ('&MONTH') because SQLPlus treats it as simple word and can substitute anything, according to examples in so old 8i reference. And it can be figured out by the error message: he tries to use JAN as identifier, so it is not properly enclosed.
Declare
Pledges UNIT_2_ASSIGNMENT%ROWTYPE;
SEARCH DATE;
Begin
SEARCH := '&M0NTH';
What it says:
For example, if the variable SORTCOL has the value JOB and the variable
MYTABLE has the value EMP, SQL*Plus executes the commands
SQL> BREAK ON &SORTCOL
SQL> SELECT &SORTCOL, SAL
2 FROM &MYTABLE
3 ORDER BY &SORTCOL;
But for your task there's no need to use PL/SQL, just format an output of SQLPlus script in appropriate way (have no SQLPlus console to put direct formatting options, but better to read the doc on SQLPlus by yourself).

Related

check if two values are present in a table with plsql in oracle sql

I'm trying to create a procedure, that checks if two values are present in a table.
The logic is as follows: Create a function called get_authority. This function takes two parameters (found in the account_owner table): cust_id and acc_id, and returns 1 (one), if the customer has the right to make withdrawals from the account, or 0 (zero), if the customer doesn't have any authority to the account. I'm writing plsql and using oracle live sql. I can't figure out how to handle the scenario where a customer has two accounts!
account_owner is seen here:
create or replace function get_authority(
p_cust_id in account_owner.cust_id%type,
p_acc_id in account_owner.acc_id%type
)
return varchar2
as
v_return number(1);
v_acc_id account_owner.acc_id%type;
v_cust_id account_owner.cust_id%type;
begin
for v_ in (select account_owner.cust_id,
account_owner.acc_id
from account_owner
where p_cust_id = cust_id)
LOOP
if p_cust_id = v_cust_id and p_acc_id = v_acc_id then
v_return := v_return + 1;
else
v_return := v_return + 0;
end if;
return v_return;
END LOOP;
end;
/
When I check for the cust_id I get the return 0 - but it should be 1??
select get_authority('650707-1111',123) from dual;
return:
GET_AUTHORITY('650707-1111',123)
0
What do I do wrong?
You got 0? How come; should be NULL.
v_return number(1);
so it is initially NULL. Later on, you're adding "something" to it, but - adding anything to NULL will be NULL:
SQL> select 25 + null as result from dual;
RESULT
----------
SQL>
Therefore, set its default value to 0 (zero):
v_return number(1) := 0;
Also, you declared two additional variables:
v_acc_id account_owner.acc_id%type;
v_cust_id account_owner.cust_id%type;
Then you compare them to values passed as parameters; as they are NULL, ELSE is executed.
Furthermore, there's a loop, but you don't do anything with it. If you meant that this:
for v_ in (select account_owner.cust_id,
(rewritten as for v_ in (select cust_id) evaluates to v_cust_id - it does not. Cursor variables are referred to as v_.cust_id (note the dot in between).
Also, if there's only one row per p_cust_id and p_acc_id, why do you use cursor FOR loop at all? To avoid no_data_found or too_many_rows? I wouldn't do that; yes, it fixes such "errors", but is confusing. You'd rather properly handle exceptions.
Here's what you might have done:
Sample data:
SQL> select * From account_owner;
ACCOW_ID CUST_ID ACC_ID
---------- ----------- ----------
1 650707-1111 123
2 560126-1148 123
3 650707-1111 5899
Function; if there are more rows per parameters' combination, max function will make sure that too_many_rows is avoided (as it bothers you). You don't really care what it returns - important is that select returns anything to prove that authority exists for that account.
SQL> create or replace function get_authority
2 (p_cust_id in account_owner.cust_id%type,
3 p_acc_id in account_owner.acc_id%type
4 )
5 return number
6 is
7 l_accow_id account_owner.accow_id%type;
8 begin
9 select max(o.accow_id)
10 into l_accow_id
11 from account_owner o
12 where o.cust_id = p_cust_id
13 and o.acc_id = p_acc_id;
14
15 return case when l_accow_id is not null then 1
16 else 0
17 end;
18 end;
19 /
Function created.
Testing:
SQL> select get_authority('650707-1111', 123) res_1,
2 get_authority('650707-1111', 5899) res_2
3 from dual;
RES_1 RES_2
---------- ----------
1 1
SQL>

How to receive only the name of a month as input parameter and not entire date in pl/sql

create or replace
PROCEDURE DISPLAY_PRESC(
P_PATIENT_ID IN NUMBER,
P_MONTH IN DATE)
IS
V_patient_name PATIENT.PATIENT_NAME%TYPE;
CURSOR PRES_CURSOR IS
SELECT PRESCRIPTION_ID, PRESCRIPTION_DATE
FROM PATIENT PAT, PRESCRIPTION PRES
WHERE PAT.PATIENT_ID = P_PATIENT_ID
AND PAT.PATIENT_ID = PRES.PATIENT_ID
AND PRES.PRESCRIPTION_DATE = P_MONTH;
BEGIN
SELECT PATIENT_NAME INTO V_PATIENT_NAME
FROM PATIENT
WHERE PATIENT_ID = P_PATIENT_ID;
DBMS_OUTPUT.PUT_LINE ('List of prescriptions for: ' || V_PATIENT_NAME|| ' during: ' ||P_MONTH);
FOR PRES_REC IN PRES_CURSOR LOOP
DBMS_OUTPUT.PUT_LINE (PRES_REC.PRESCRIPTION_ID|| ' ' || PRES_REC.PRESCRIPTION_DATE);
END LOOP;
DBMS_OUTPUT.PUT_LINE ('*** END OF REPORT ***');
END DISPLAY_PRESC;
This procedure is supposed to display all the prescriptions of a particular patient for a certain month.
input parameters
eg: Patient_ID = 10;
P_Month = 'June' & not '01-Jun-07' like I currently have to enter it.
thanks in advance for the help.
try this code to display prescriptions in certain month in all years:
create or replace PROCEDURE DISPLAY_PRESC(P_PATIENT_ID IN NUMBER,
P_MONTH IN varchar2)
IS
V_patient_name PATIENT.PATIENT_NAME%TYPE;
CURSOR PRES_CURSOR IS
SELECT PRESCRIPTION_ID, PRESCRIPTION_DATE
FROM PATIENT PAT, PRESCRIPTION PRES
WHERE PAT.PATIENT_ID = P_PATIENT_ID
AND PAT.PATIENT_ID = PRES.PATIENT_ID
------ modified
AND trim(Initcap(to_char(PRES.PRESCRIPTION_DATE, 'month')) ) = trim(Initcap(P_MONTH))
------ added
order by PRES.PRESCRIPTION_DATE ;
BEGIN
SELECT PATIENT_NAME
INTO V_PATIENT_NAME
FROM PATIENT
WHERE PATIENT_ID = P_PATIENT_ID;
DBMS_OUTPUT.PUT_LINE('List of prescriptions for: ' || V_PATIENT_NAME ||
' during: ' || Initcap(P_MONTH) );
FOR PRES_REC IN PRES_CURSOR LOOP
DBMS_OUTPUT.PUT_LINE(PRES_REC.PRESCRIPTION_ID || ' ' ||
PRES_REC.PRESCRIPTION_DATE);
END LOOP;
DBMS_OUTPUT.PUT_LINE('*** END OF REPORT ***');
END DISPLAY_PRESC;
I suggest you to keep p_month as date parameter and handle it in your program interface code before sending it as parameter for DISPLAY_PRESC procedure.
Date object is a full date which includes year,month, day, hour, minutes, seconds.
If you want to get only a month you should get either a VARCHAR2, or a number, and migrate it to the required date.
There's no "month" data type in Oracle, so if you want to handle only monthes you'll have to do it manually in your code.
The way most of the systems do the same thing is getting to full dates, which they extract data between these dates (with days, and year), and that way bypass the problem you face right now.
Also remember that each year there is June, so you would probably need the year as input - so send in 06/01/2014 00:00:00 is actually helpful a bit - since you get one object, which you can extract two pieces of data from.

SQL Function and Anonymous block

I'm taking a class about databases. I am very new to it, so excuse me if this is an obvious error, but I've been working on this problem for hours and not sure what else to do.
The code is suppose to create a function to that
A.) Outputs the average netwin for a year given by a parameter
(given by the formula (SeasonW-Seasonl) + (PlayoffW - PlayoffL)
B.) Outputs all of the coaches that have a netwin over the average
C.) Returns the number of coaches that fits this criteria.
D.)Have a anonymous block that calls this function, and outputs two different messages depending on the return on the function.
Now I have sucessfully done parts A, C, and D. But for some reason my function will not be created when I insert part B.
create or replace function GOOD_COACHES(season IN INT)
return INT
IS
netwin INT;
CNT INT;
BEGIN
--Calculated netwin
select AVG((SEASON_WIN-SEASON_LOSS) + (PLAYOFF_WIN-PLAYOFF_LOSS)) into netwin from COACHESSEASON where YEAR = season;
--Prints out A
dbms_output.put_line('Average Netwin is: ' || netwin);
--This Line messes up the function, I don't know why
select T.FIRSTNAME, T.LASTNAME from COACHESSEASON T where ((T.SEASON_WIN-T.SEASON_LOSS) + (T.PLAYOFF_WIN-T.PLAYOFF_LOSS))>netwin and YEAR = season;
--Calculates the number of teams that satisfy average
select count(T.FIRSTNAME) into CNT from COACHESSEASON T where ((T.SEASON_WIN-T.SEASON_LOSS) + (T.PLAYOFF_WIN-T.PLAYOFF_LOSS))>netwin and YEAR = season;
return CNT;
END;
--End of the Function
--Start of the Anonymous Block
DECLARE
x int := GOOD_COACHES(1998);
BEGIN
if x = 0 then dbms_output.put_line('We didn''t find any good_coaches!');
else dbms_output.put_line('The No. of good coaches is ' || x);
end if;
END;
/
That third line in the function messes it up and doesn't allow it to be called. If I comment it out, it works properly.
When I take it out of the function and make it into a regular SQL statement, it works.
select T.FIRSTNAME, T.LASTNAME from COACHESSEASON T where ((T.SEASON_WIN-T.SEASON_LOSS) + (T.PLAYOFF_WIN-T.PLAYOFF_LOSS))>0 and YEAR = /*RandYear*/;
If anyone understands why the function can not be created with that line it in, I would appreciate the advice. I also do not know how I would print out the results of the selected row in the function.
You are not selecting the T.FIRSTNAMEs and T.LASTNAMEs into anything so the statement is not valid for inclusion in a PL/SQL block.
There are likely to be multiple "good coaches" so you can't put the values into a single variable and will have to use a cursor or a collection.
Using collections, if you create a table type to collect the first and last names into:
CREATE TYPE VARCHAR2s_Table AS TABLE OF VARCHAR2(30);
/
Then you can use this to collect the first and last names (and as a bonus the collection size will tell you how many good coaches there are and you can skip the last query):
create or replace function GOOD_COACHES(season IN INT)
return INT
IS
netwin INT;
CNT INT;
firstnames VARCHAR2s_Table;
lastnames VARCHAR2s_Table;
BEGIN
--Calculated netwin
select AVG((SEASON_WIN-SEASON_LOSS) + (PLAYOFF_WIN-PLAYOFF_LOSS))
into netwin
from COACHESSEASON
where YEAR = season;
--Prints out A
dbms_output.put_line('Average Netwin is: ' || netwin);
--This Line messes up the function, I don't know why
select T.FIRSTNAME, T.LASTNAME
BULK COLLECT INTO firstnames, lastnames
from COACHESSEASON T
where ((T.SEASON_WIN-T.SEASON_LOSS) + (T.PLAYOFF_WIN-T.PLAYOFF_LOSS))>netwin
and YEAR = season;
FOR i IN 1 .. firstnames.COUNT LOOP
DBMS_OUTPUT.put_line( firstnames(i) || ' ' || lastnames(i) );
END LOOP;
return firstnames.COUNT;
END;
/
SQLFIDDLE

Oracle Procedure and multi value of optional param

I'm writing a procedure on Oracle. I need to use it for a search.
The form search have 8 params, each params can contains many values.
For exemple you can add two movies title (you can choose to not type whole title).
you also can tyoe a year and not a title..
I don't know if I can have multi values for my parameters ?
I want to build only on sql query for it but it is possible ? Because my where clause containt 8 params.. I'm lost with this search !
I have this procedure (doesn't worked)
create or replace procedure listerFilms (
unTitreFilm Film.titre%TYPE DEFAULT NULL,
uneAnneeMin Film.annee%TYPE DEFAULT NULL,
uneAnneeMax Film.annee%TYPE DEFAULT NULL,
uneVoFilm Film.langue%TYPE DEFAULT NULL,
unPaysProd Pays.pays%TYPE DEFAULT NULL,
unGenreFilm Genre.genre%TYPE DEFAULT NULL,
unNomRea Equipe_Tournage.nomComplet%TYPE DEFAULT NULL,
unNomActeur Equipe_Tournage.nomComplet%TYPE DEFAULT NULL)
is
titreFilm Film.titre%TYPE;
anneeFilm Film.annee%TYPE;
cursor lignesFilm(leTitreFilm Film.titre%TYPE, laAnneeMin Film.annee%TYPE, laAnneeMax Film.annee%TYPE, laVoFilm Film.langue%TYPE, lePaysProd Pays.pays%TYPE, leGenreFilm Genre.genre%TYPE, leNomRea Equipe_Tournage.nomComplet%TYPE, leNomActeur Equipe_Tournage.nomComplet%TYPE) is
select distinct
f.titre, f.annee
from
Film f, Pays p, Genre g, Equipe_Tournage rea, Equipe_Tournage act
where
f.titre like '%'||leTitreFilm||'%' and f.annee >= laAnneeMin and f.annee <= laAnneeMax
and f.langue like '%'||laVoFilm||'%' and p.pays like '%'||lePaysProd||'%'
and g.genre like '%'||leGenreFilm||'%' and rea.nomComplet like '%'||leNomRea||'%'
and act.nomComplet like '%'||leNomActeur||'%';
begin
open lignesFilm(unTitreFilm,uneAnneeMin, uneAnneeMax, uneVoFilm, unPaysProd, unGenreFilm, unNomRea, unNomActeur);
DBMS_OUTPUT.PUT_LINE('---------------------');
DBMS_OUTPUT.PUT_LINE('-- Liste des Films --');
DBMS_OUTPUT.PUT_LINE('---------------------');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Aucun film ne correpond au(x) critere(s) de recherche');
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'Exception levée par la procédure');
loop
fetch lignesFilm into titreFilm, anneeFilm;
-- fetch retoune la ligne suivante
EXIT WHEN lignesFilm%NOTFOUND;
-- quit lorsque cursor fini
DBMS_OUTPUT.PUT_LINE(titreFilm || ' (' || anneeFilm || ')');
end loop;
close lignesFilm;
end;
/
Please help me
It is possible if you use a bit of PL/SQL magic. Here's an example how you can search for multiple titles (just add other parameters that you need).
Firstly, you need a table type as a parameter to hold multiple titles - that's the t_str_tab type.
Secondly, you use the TABLE function on that table parameter to be able to query it, and you access its values by using the pseudocolumn COLUMN_VALUE.
Thirdly, you use a subquery to check if currently processed row has a title matching any of the titles provided in table parameter.
CREATE TABLE movies (id NUMBER, title VARCHAR2(20), release_year NUMBER);
CREATE TYPE t_str_tab IS TABLE OF VARCHAR2(20);
CREATE TYPE t_num_tab IS TABLE OF NUMBER;
INSERT INTO movies VALUES (1, 'First movie', 2010);
INSERT INTO movies VALUES (2, 'Second movie', 2010);
INSERT INTO movies VALUES (3, 'Third movie', 2010);
COMMIT;
CREATE OR REPLACE PROCEDURE search_proc(p_titles IN t_str_tab, p_years IN t_num_tab DEFAULT NULL) AS
CURSOR c_movies IS
SELECT m.id, m.title, m.release_year
FROM movies m
WHERE
((SELECT COUNT(1) FROM TABLE(p_titles)) = 0
OR EXISTS (
SELECT 1
FROM TABLE(p_titles) -- magic
WHERE upper(m.title) LIKE '%' || UPPER(column_value) || '%')
)
AND
((SELECT COUNT(1) FROM TABLE(p_years)) = 0
OR EXISTS (
SELECT 1
FROM TABLE(p_years)
WHERE m.release_year = column_value)
)
;
BEGIN
FOR v_movie IN c_movies
LOOP
dbms_output.put_line(
'Id: ' || v_movie.id ||
', title: ' || v_movie.title ||
', release year: ' || v_movie.release_year);
END LOOP;
END;
/
BEGIN
dbms_output.put_line('First search:');
search_proc(t_str_tab('d'));
dbms_output.put_line('Second search:');
search_proc(t_str_tab('st', 'nd'));
END;
Output:
First search:
Id: 2, title: Second movie, release year: 2010
Id: 3, title: Third movie, release year: 2010
Second search:
Id: 1, title: First movie, release year: 2010
Id: 2, title: Second movie, release year: 2010
Edit: changed the code to allow optional parameter.

PL/SQL Storing and calling procedure

I am currently working on a PL/SQL problem where I have to create a cursor inside a procedure that will give the appropriate discount for a given item (10% for prices >= $100 and 5% for prices >= $10). When I have to call the procedure, I need to display the Order Number, customer First Name, Last Name, and the Total Net Cost of the items after the discount for a specific order number (in this case I need to display for order number 2). I can't get it to display this information.
Here is my code for creating the procedure with a cursor so far.
CREATE OR REPLACE PROCEDURE ComputeOrderTotal
(no_id IN orders.o_id%TYPE,
cfirst IN customer.c_first%TYPE,
clast IN customer.c_last%TYPE,
TotalNetCost OUT orders.ordertotal%TYPE) IS
CURSOR OrderCursor IS
SELECT order_line.inv_id, inv_price, ol_quantity, inv_price*ol_quantity AS ExtPrice,
CASE
WHEN inv_price*ol_quantity >= 100 THEN 0.9*(inv_price*ol_quantity)
WHEN inv_price*ol_quantity >= 10 THEN 0.95*(inv_price*ol_quantity)
ELSE
inv_price*ol_quantity
END AS NetCost
FROM inventory, order_line, orders, customer
WHERE orders.o_id = customer.c_id;
OrderRow OrderCursor%ROWTYPE;
BEGIN
OPEN OrderCursor;
LOOP
FETCH OrderCursor INTO OrderRow;
EXIT WHEN OrderCursor%NOTFOUND;
TotalNetCost :=TotalNetCost + OrderRow.NetCost;
END LOOP;
DBMS_OUTPUT.PUT_LINE('Order Number: ' || no_id || 'First Name: ' || cfirst || 'Last Name: ' ||
clast || 'Total Net Cost: ' || TO_CHAR(TotalNetCost, '$0999.99'));
END;
And here is my code for calling the procedure.
DECLARE
no_id orders.o_id%TYPE;
cfirst customer.c_first%TYPE;
clast customer.c_last%TYPE;
TotalNetCost orders.ordertotal%TYPE;
BEGIN
ComputeOrderTotal(2, cfirst, clast, TotalNetCost);
END;
Thanks for the help!
i am not sure but your conditions could be the problem as your second condition contradicts the first. for example 110 satisfy the first condition but it also satisfy the second condition.
WHEN inv_price*ol_quantity >= 100 THEN 0.9*(inv_price*ol_quantity)
WHEN inv_price*ol_quantity >= 10 THEN 0.95*(inv_price*ol_quantity)
i would suggest changing second condition to <100 and >=10
Add this line to the top of the code calling the procedure.
Alter session set serveroutput on;