SQL Function and Anonymous block - sql

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

Related

adding to variables inside of a oracle procedure

I created a procedure that takes the account number of a user and it returns every transaction they've made plus their running balance. everything works except the balance. The code compiles and runs but nothing shows up in the running balance column of the dbms.output
CREATE OR REPLACE PROCEDURE print_dett(
in_account_nbr NUMBER
)
IS
dCount NUMBER;
BEGIN
dbms_output.put_line(' Date : transaction amount : balance - For: ' || in_account_nbr);
FOR r IN(
SELECT t.tx_nbr, t.tx_date, t.tx_amount, t.tx_type_code, a.balance
FROM transaction t, account a
WHERE t.account_nbr = a.account_nbr
AND t.account_nbr = in_account_nbr
ORDER BY tx_date)
LOOP
IF r.tx_type_code = 'D' OR r.tx_type_code = 'R' THEN
dCount := dCount + r.tx_amount;
ELSE
dCount := dCount - r.tx_amount;
END IF;
dbms_output.put_line(r.tx_date || ' : ' || r.tx_amount || ' : ' || dCount );
END LOOP;
END;
What can I change in the code so the running balance will actually show up when printed?
Number variables get initialized with NULL. Your variable dCount is hence null first and adding values doesn't change this.
Initialize it with zero instead:
CREATE OR REPLACE PROCEDURE print_dett(
in_account_nbr NUMBER
)
IS
dCount NUMBER := 0;
...

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 to use two columns with clause definescore oracle text

I have this code:
declare
sName varchar(25);
iRank number := 0;
sDesc varchar(510);
cursor q is
SELECT *
FROM trec_topics ORDER BY num;
BEGIN
for ql in q
loop
sDesc := replace(replace(replace(ql.title, '?', '{?}'), ')', '{)}'), '(', '{(}');
--dbms_output.put_line(ql.num||'-'||sDesc);
declare
cursor c is
SELECT /*+ FIRST_ROWS(100) */ docno,
CASE
WHEN SCORE(10) >= SCORE(20) THEN SCORE(10)
ELSE SCORE(20)
END AS SCORE
FROM txt_search_docs WHERE CONTAINS(txt, 'DEFINESCORE(ql.title, OCCURRENCE)', 10) > 0 OR
CONTAINS(txt, 'DEFINESCORE(sDesc, OCCURRENCE)', 20) > 0
order by SCORE desc;
begin
iRank := 1;
for c1 in c
loop
dbms_output.put_line(ql.num||' Q0 '||c1.docno||' '||lpad(iRank,3, '0')||' '||lpad(c1.score, 2, '0')||' myUser');
iRank := iRank + 1;
exit when c%rowcount = 100;
end loop;
end;
end loop;
end;
As you can see I'm doing select on two different tables, however, I need to change the standard score, as it did not perform well. I'm trying to use the DEFINESCORE clause that has this 'DEFINESCORE (query_term, scoring_expression)' format.
How can I call the table columns within this clause? That is, I need to call my columns instead of "query_term", as there are several documents to do the search. Because the way I’m calling him, he’s looking for exactly the term ql.title
Anyone a suggestion to help me with this problem?
I finally managed to solve it.
It was about:
create a variable: topics varchar (525);
store the column value: topics := replace(replace(replace(ql.title, '?', '{?}'), ')', '{)}'), '(', '{(}');
and after calling it in the CONTAINS clause: FROM txt_search_docs WHERE CONTAINS(txt, 'DEFINESCORE(('''||topics||'''), OCCURRENCE)', 1) > 0

Using ACCEPT, CASE to create CURSOR in pl/sql

I am trying to create a script that will allow the user to select which CASE population to use from an ACCEPT when gathering student contact info.
PROMPT 'Select a popluation for emails'
PROMPT '1. Currently registered'
PROMPT '2. New Applicants'
PROMPT
ACCEPT cnt number PROMPT 'Selection: ';
...
CURSOR stu_lst IS
CASE &cnt
WHEN 1 THEN -- Current registered students.
select distinct SFRSTCA_PIDM pidm
from SFRSTCA
where SFRSTCA_TERM_CODE = '201403' and
SFRSTCA_LEVL_CODE = '01' and
SFRSTCA_RSTS_CODE = 'RE';
WHEN 2 THEN -- New applicants
select app_pidm pidm
from app
where app_term = 'Fall 2014';
ELSE
-- Incorrect selection.
DBMS_OUTPUT.PUT_LINE('Incorrect selection made.');
exit;
END;
END;
Assuming the two queries return the same data type, you could use a union with a filter that checks the variable in each part; something like:
DECLARE
CURSOR stu_lst IS
-- Current registered students.
select distinct SFRSTCA_PIDM pidm
from SFRSTCA
where &cnt = 1 and
SFRSTCA_TERM_CODE = '201403' and
SFRSTCA_LEVL_CODE = '01' and
SFRSTCA_RSTS_CODE = 'RE';
UNION ALL
-- New applicants
select app_pidm
from app
where &cnt = 2 and
app_term = 'Fall 2014';
invalid_argument EXCEPTION;
...
BEGIN
IF &cnt NOT IN (1, 2) THEN
RAISE invalid_argument;
END IF
FOR rec IN stu_lst LOOP
h_pidm := rec.pidm;
...
END LOOP;
EXCEPTION
WHEN invalid_argument THEN
dbms_output.put_line('Incorrect selection made.');
END;
/
You could also declare a cursor variable and open that with the appropriate query, inside a case statement within the main body of the block. This sticks with your explicit cursor syntax though.

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.