How to receive only the name of a month as input parameter and not entire date in pl/sql - 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.

Related

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

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).

How can I return multiple row values into one cursor variable in Oracle SQL? [duplicate]

This question already has answers here:
SQL Query to concatenate column values from multiple rows in Oracle
(10 answers)
Closed 8 years ago.
I have a package in oracle that contains one stored procedure. It takes a date as a input and a number as input, and outputs the data into a ref_cursor. The purpose of the stored procedure is to extract all of the "winning numbers" that correspond to a certain input date.
CREATE OR REPLACE PACKAGE GETWINNINGNUMBERSBYDATEPKG IS
TYPE lotgwndate_ref_cursor IS REF CURSOR;
PROCEDURE GetWinningNumbersDATE (lg_wndate IN date, lg_wnwgid IN number, lg_ref OUT
lotgwndate_ref_cursor);
END GETWINNINGNUMBERSBYDATEPKG;
This is the package body. Again, the purpose here is to extract all of the "winning numbers" that correspond to a certain date and "gameid" number. Then, I want to pair the "winning numbers" with the "game name" and "drawing date". (This is to extract records from lottery drawings)
PROCEDURE GetWinningNumbersDATE (lg_wndate IN date, lg_wnwgid IN number, lg_ref OUT
lotgwndate_ref_cursor) IS
BEGIN
OPEN lg_ref FOR
SELECT a.GAMENAME, b.DRAWINGDATE, c.BALLNUMBER
FROM GAMEDETAILS a
INNER JOIN WINNINGRECORDS b
on a.GAMEDETAILSID = b.GAMEDETAILSID
INNER JOIN WINNINGBALLS c
on b.WINNINGRECORDSID = c.WINNINGRECORDSID
WHERE b.DRAWINGDATE = lg_wndate
AND a.GAMEDETAILSID = lg_wnwgid;
END GetWinningNumbersDATE;
END GETWINNINGNUMBERSBYDATEPKG;
Here is the call procedure.
SET SERVEROUTPUT ON size 100000
DECLARE
v_cursor LOTTERYGAMEPKG.lotgwndate_ref_cursor;
v_gamename GAMEDETAILS.gamename%type;
v_drawingdate WINNINGRECORDS.drawingdate%type;
v_ballnumber WINNINGBALLS.ballnumber%type;
BEGIN
GETWINNINGNUMBERSBYDATEPKG.GetWinningNumbersDATE(lg_wndate =>
TO_DATE('06/08/2014','dd/mm/yyyy'), lg_wnwgid => '4', /*date/gamedetailsid #(1-4)*/
lg_ref => v_cursor);
LOOP
FETCH v_cursor
INTO v_gamename, v_drawingdate, v_ballnumber;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE
(v_gamename || ',' || v_drawingdate || ',' || v_ballnumber);
END LOOP;
END;
This works, and returns:
NorthstarCash,06-AUG-14,10
NorthstarCash,06-AUG-14,11
NorthstarCash,06-AUG-14,15
NorthstarCash,06-AUG-14,16
NorthstarCash,06-AUG-14,21
However, I want it to return
NorthstarCash,06-AUG-14,10,11,15,16,21
Are there any ways to do this? I have looked at many other questions but none are addressing the same issue as mine. ORACLE 11g
If you use Oracle 11g, just modify your SELECT query using LISTAGG
PROCEDURE GetWinningNumbersDATE (lg_wndate IN date, lg_wnwgid IN number, lg_ref OUT
lotgwndate_ref_cursor) IS
BEGIN
OPEN lg_ref FOR
SELECT a.GAMENAME, b.DRAWINGDATE, LISTAGG(c.BALLNUMBER,',') WITHIN GROUP (ORDER BY c.BALLNUMBER) AS BALLNUMBER
FROM GAMEDETAILS a
INNER JOIN WINNINGRECORDS b
on a.GAMEDETAILSID = b.GAMEDETAILSID
INNER JOIN WINNINGBALLS c
on b.WINNINGRECORDSID = c.WINNINGRECORDSID
WHERE b.DRAWINGDATE = lg_wndate
AND a.GAMEDETAILSID = lg_wnwgid
GROUP BY a.GAMENAME, b.DRAWINGDATE;
END GetWinningNumbersDATE;
And the caller, has to be modified like below.
v_ballnumber VARCHAR2(4000);

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

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;

Oracle - Comparing dates

I have this Oracle procedure:
CREATE OR REPLACE PROCEDURE xmas
IS
CURSOR curUser IS SELECT userID, coins FROM Users;
thisUser curUser%ROWTYPE;
d VARCHAR(7);
CURSOR curDate IS SELECT TO_CHAR(sysdate, 'DD-MON') FROM DUAL;
BEGIN
OPEN curDate;
FETCH curDate INTO d;
CLOSE curDate;
IF ((TO_DATE(d)) = (TO_DATE('25-DEC'))) THEN
OPEN curUser;
LOOP
FETCH curUser INTO thisUser;
EXIT WHEN (curUser%NOTFOUND);
thisUser.coins := thisUser.coins + 5.00;
UPDATE Users SET coins = thisUser.coins WHERE userID = thisUser.userID;
END LOOP;
CLOSE curUser;
END IF;
END xmas;
and when I call it, I get this error:
ORA-01840: input value not long enough for date format.
Tried different comparison methods for hours and nothing else has worked.
What's the problem??
You need to specify a date format for Oracle to know what actually '25-DEC' means.
select TO_DATE('25-DEC', 'DD-MON') from dual;
The problem should be on this line:
IF ((TO_DATE(d)) = (TO_DATE('25-DEC'))) THEN
As a note, it seems to me that you are also making a lot of unnecessary conversions. I don't know if this is an educational attempt, but why not just run the update below:
update USERS
set
coins = coins + 5
where
to_char(sysdate, 'DD-MON') = '25-DEC';