For some reason, every time I try to run this, there is any error saying that the SQL statement at line 4, column 3 (SELECT) has been ignored. Also says, "YEAR": Invalid Identifier. Year is another column that exists on the same form. I am trying to make a column that displays the YEAR with a hyphen like for example this: "17-".
DECLARE
TERRA_NUMBER VARCHAR2(40);
BEGIN
SELECT CONCAT(YEAR , '-' )
INTO TERRA_NUMBER FROM DUAL;
RETURN TERRA_NUMBER;
END;
This is too long for a comment.
First, there is no YEAR variable in Oracle. Perhaps you intend TO_CHAR(sysdate, 'YYYY').
Second, an anonymous programming block does not return a value. So, RETURN is inappropriate.
If this is part of a function or stored procedure you should show the entire definition.
Maybe you can create FindYear function.
CREATE OR REPLACE FUNCTION FindYear RETURN VARCHAR2
IS
TERRA_NUMBER VARCHAR2(40);
BEGIN
SELECT CONCAT(TO_CHAR(sysdate, 'YYYY') , '-' )
INTO TERRA_NUMBER FROM DUAL;
RETURN(TERRA_NUMBER);
END;
/
Related
I am trying to write a PLSQL code to get current day name using PLSQL and assigning it to variable
Code
CREATE OR REPLACE PROCEDURE ABC_STORE
(
v_day varchar2
)
AS
BEGIN
select to_char(CURRENT_TIMESTAMP,'DAY') into v_day from dual;
DBMS_OUTPUT.PUT_LINE(v_day);
END;
Error :
You should add al least clause OUT for value v_day:
create or replace PROCEDURE ABC_STORE
(
v_day IN OUT varchar2
)
AS
BEGIN
select to_char(CURRENT_TIMESTAMP,'DAY') into v_day from dual;
DBMS_OUTPUT.PUT_LINE(v_day);
END;
Thank you
You don't need the query, because PL/SQL has an assignment operator :=.
You also don't need dbms_output if the requirement is just to bring back the day name.
Also, the 'DAY' format element includes blank padding by default (e.g. SUNDAY ), so 'fmDAY' provides the expected result SUNDAY. If it doesn't need to be all capitals, you can use 'fmDay'.
create or replace procedure day_name
( v_day out varchar2 )
as
begin
v_day := to_char(sysdate,'fmDay');
end day_name;
Just to add, if you are checking the day name as part of some business logic (for example, a batch step should not run at the weekend), you will also need to specify a date language to avoid the situation where you are checking for 'Sunday' and the procedure is called from a support desk in Paris and returns 'Dimanche'. You would do this using (for example - substitute any language you want)
to_char(sysdate,'fmDay','nls_date_language=English')
(Of course, if you were just checking for the weekend you would only need to get the English three letter abbreviation and check whether it's like 'S%', but this is not what you asked for. I mention it because I have seen production errors caused by poorly handled day name checks, and as a result I use it as an interview question, which it turns out very few candidates can answer.)
I am new to pl/sql. I want to create a procedure that has three parameters called 'startMonth', 'endMonth', 'thirdMonth'. In the procedure, I am executing a sql query which is in 'run_sql' column in table_query. Values for 'startMonth', 'endMonth', 'thirdMonth' are needed to this query. This is how I wrote the procedure. My plan is to put all the sql queries in a separate table and execute in the for loop in the procedure. There I am creating a table called table1 and in the next month I want to drop it and create the table again. This is how I have written the procedure.
CREATE OR REPLACE procedure schema.sixMonthAverage (startMonth varchar,endMonth varchar ,thirdMonth varchar )
IS
start_date varchar := startMonth;
end_date varchar := endMonth;
begin
for c_rec in(select run_sql from table_query)
loop
dbms_output.put_line(startmonth);
dbms_output.put_line(endmonth);
execute immediate c_rec.run_sql using start_date, end_date;
Execute IMMEDIATE 'commit';
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Exception');
END;
This is the query in the run_sql column in table_query.
create table table1
as
select account_num,bill_seq,bill_version,
to_char(start_of_bill_dtm,''YYYYMM-DD'') st_bill_dtm,
to_char(bill_dtm - 1,''YYYYMM-DD'') en_bill_dtm,
to_char(actual_bill_dtm,''YYYYMM-DD'') act_bill_dtm,
round((invoice_net_mny + invoice_tax_mny)/1000,0) mon_bill,
bill_type_id,bill_status
from billsummary
where to_char(bill_dtm - 1,''YYYYMM'') between'||chr(32)||
startMonth ||chr(32)||'and'|| chr(32)||endMonth ||chr(32)||
'and cancellation_dtm is null;
But when I try to compile the procedure it gives me the error 'PLS00215: String length constraints must be in range (1..32767). Though I searched for the error I could not find the exact reason. It seems to be a problem in variable assigning. But I could not resolve it.
--Update
As it is given in the answer I converted the strings to dates.
CREATE OR REPLACE procedure REPO.sixMonthAverage (startMonth varchar2,endMonth varchar2 ,thirdMonth varchar2 )
IS
start_date date := TO_DATE(startMonth, 'yyyymm');
end_date date := TO_DATE(endMonth, 'yyyymm');
But when executing the query it gives the error message that ORA-00904: "END_DATE": invalid identifier. But it does not show any error message for the start_date and what would be the reason for this error message?
The error is pointing you to where the problem is. String declarations (char, varchar, varchar2 - but you should only be using varchar2, not varchar) need a length; so for example:
CREATE OR REPLACE procedure sixMonthAverage (startMonth varchar2,endMonth varchar2 ,thirdMonth varchar2 )
IS
start_date varchar2(10) := startMonth;
end_date varchar2(10) := endMonth;
...
Notice the procedure arguments do not specify a length; only the local variable declarations.
If those represent dates then they, and passed-in arguments, should probably be dates, not strings. It depends what your dynamic SQL is expecting though - if that is converting the strings to dates and specifying the format mask then I guess it's OK; otherwise you should be passed dates, or convert the strings to dates. The example you showed doesn't seem to have any bind variables to populate, though.
Dropping and recreating tables is generally not something you want to be doing though. You could delete/truncate and repopulate a table; or use partitioning if you want to keep more than one month; or use a view (or materialized view).
I am new with PLSQL and I have issue with this procedure.
I don't know what the error mean at the same time I am sure the table and data are created successfully.
the procedure should receive the start and end date with the invoice number to show the details
create or replace PROCEDURE Invoicedetails (Fromdate IN DATE , Todate IN DATE , InvoiceNum NUMBER)
IS
INV_info invoicetable%ROWTYPE;
BEGIN
SELECT *
INTO INV_info
FROM invoicetable
WHERE InvoiceNum = INV_info.InvoiceNum AND INV_info,InvoiceDate betwen Fromdate And Todate;
dbms_output.put_line('ID:'|| INV_info.InvoiceNum);
dbms_output.put_line('Amount:'|| INV_info.Invoiceamount);
dbms_output.put_line('Date:'|| INV_info.InvoiceDate);
END Invoicedetails;
When I call the procedure like this
BEGIN
Invoicedetails('01-JAN-2020','05-JAN-2020',200651)
END;
Error report
ORA-01403 :no data found
ORA-06512 : at "APPS.Invoicedetails",line 5
ORA-06512 : at line 2
01403. 00000 - "no data found"
If you say you are new then you have done a good job.
Let's dig into the problem,
If you are learning then put into TODO list, the next topic about exception in PLSQL and how to handle.
The error you get say ORA-01403 :no data found which is self explanatory and mean we are searching for something and whatever code you have written didn't find it as expected which leads us to the select statement,
SELECT *
INTO INV_info
FROM invoicetable
WHERE InvoiceNum = INV_info.InvoiceNum
AND INV_info,InvoiceDate betwen Fromdate And Todate;
In the above if you see,
First small problem is syntactical which is INV_info,InvoiceDate which should be INV_info.InvoiceDate (this is anyway not correct as per the expectations of result which I will clarify below)
Second and most important problem is you are trying to compare the value of InvoiceNum with the rowtype variable which is InvoiceNum = INV_info.InvoiceNum and you have to understand here INV_info.InvoiceNum is a variable and doesn't hold any value at this very time.
So you should compare the table value with the input you provided via parameter as WHERE invoicetable.InvoiceNum = invoiceNum. Left side is the table column and right side is the parameter you passed.
Similarly the condition AND INV_info,InvoiceDate betwen Fromdate And Todate should change to AND invoicetable.InvoiceDate betwen Fromdate And Todate.
Having said all these there are some things you also need to consider interms of naming convention of variables and also usage of alias for table. (Which can be seen what changes I made to the procedure below)
Accumulating all points the procedure can be further modified as,
create or replace procedure invoicedetails
(
p_fromdate in date
, p_todate in date
, p_invoicenum number)
is
inv_info invoicetable%rowtype;
begin
select *
into inv_info
from invoicetable i
where i.invoicenum = p_invoicenum
and i.invoiceDate between p_fromdate and p_todate;
dbms_output.put_line('ID:'|| inv_info.invoicenum);
dbms_output.put_line('Amount:'|| inv_info.invoiceamount);
dbms_output.put_line('Date:'|| inv_info.invoicedate);
end invoicedetails;
/
Here is db<>fiddle for your reference. I have to do a little trick by calling dbms.output to print the result while calling the procedure which you don't need when you try in your machine
First thing out of the chute, you declare to input parameters as DATE, but then when you call the procedure you supply a CHARACTER STRING. Just because the input looks like a date to you does not mean that oracle interprets as a DATE. DATE is an internal, binary data type. Where will the input values actually originate? As per your example, you need to convert the input string to a DATE:
create or replace PROCEDURE Invoicedetails (Fromdate IN VARCHAR2 , Todate IN VARCHAR2 , InvoiceNum NUMBER)
IS
v_fromdate date;
v_todate date;
INV_info invoicetable%ROWTYPE;
BEGIN
v_fromdate := to_date(fromdate,'dd-Mon-yyyy');
v_todoate := to_date(todate,'dd-Mon-yyyy');
Then, in the rest of your code, reference v_fromdate and v_todate instead of your input parms.
As an aside, you should also develop the habit of being consistent in your coding style. Unlike some other rdbms products, oracle really doesn't support MixedCaseNames. (Well, you can jump through some hoops to force it to, but that is going against the grain and you really don't want to go there.) Instead of MixedCaseNames, the oracle standard is underscore_separated_names.
You need to:
Not name the procedure's arguments the same as columns in your table; it's confusing to debug and can confuse the SQL parser into comparing the column to itself rather than comparing the column to the parameter's argument.
Handle the NO_DATA_FOUND exception.
Handle the TOO_MANY_ROWS exception.
Using something like this:
CREATE PROCEDURE InvoiceDetails (
p_FromDate IN InvoiceTable.InvoiceDate%TYPE, -- Use the types from the table
p_ToDate IN InvoiceTable.InvoiceDate%TYPE,
p_InvoiceNum IN InvoiceTable.InvoiceNum%TYPE
)
IS
inv_info invoicetable%ROWTYPE;
BEGIN
SELECT *
INTO INV_info
FROM invoicetable
WHERE InvoiceNum = p_InvoiceNum -- Don't have the same variable name as the
-- column name. One practice is to prefix the
-- parameter names with p_ to distinguish
-- that they were passed from outside the
-- procedure.
AND InvoiceDate BETWEEN p_FromDate AND p_ToDate;
dbms_output.put_line('ID: ' || INV_info.InvoiceNum);
dbms_output.put_line('Amount: ' || INV_info.InvoiceAmount);
dbms_output.put_line('Date: ' || TO_CHAR( INV_info.InvoiceDate, 'YYYY-MM-DD' ) );
EXCEPTION
WHEN NO_DATA_FOUND THEN -- Handle the exception when no rows are found.
dbms_output.put_line('No Invoices exist.');
WHEN TOO_MANY_ROWS THEN -- Handle the exception when multiple rows are found.
dbms_output.put_line('Multiple invices exist.');
END InvoiceDetails;
/
So, if you have the table:
CREATE TABLE invoicetable (
invoicenum NUMBER(10,0),
invoiceamount NUMBER(17,2),
invoicedate DATE
);
and then execute your anonymous PL/SQL block:
BEGIN
Invoicedetails( DATE '2020-01-01',DATE '2020-01-05',200651);
END;
/
There will be no rows to match and the NO_DATA_FOUND exception will be raised and you get the output:
No Invoices exist.
If you then insert a row:
INSERT INTO invoicetable (invoicenum, invoiceamount, invoicedate )
VALUES ( 200651, 200, DATE '2020-01-04' );
and run the same anonymous PL/SQL block, you now get the output:
ID: 200651
Amount: 200
Date: 2020-01-04
and, if you insert a second row:
INSERT INTO invoicetable (invoicenum, invoiceamount, invoicedate )
VALUES ( 200651, 300, DATE '2020-01-05' );
and run the anonymous PL/SQL block again, you get the output:
Multiple invices exist.
db<>fiddle here
Create a dynamic procedure that will change the contents of any column for any row in the AA_EMPLOYEE table using the employee id. i.e.
BEGIN
dyn_aa_employee('emp_dob', '01-jan-18', 110);
END;
Will change the date of birth for employee ID 110
CREATE OR REPLACE PROCEDURE dyn_aa_employee
(p_col VARCHAR2,
p_dob IN aa_employee.emp_dob%TYPE,
p_id NUMBER)
IS
BEGIN
EXECUTE IMMEDIATE 'UPDATE aa_employee
SET '|| p_col ||' = :ph_dob
WHERE EMP_NUM = :ph_id'
USING p_dob, p_id;
BEGIN
dyn_aa_employee('emp_dob', '01-jan-18', 110);
END;
The top code has to work for the bottom code. The issue is it's changing the emp dob to 01-jan-0018, however I want it to change to exactly 01-jan-18.
My professor gave me a 0 for this assignment I'm just trying to figure out what I did wrong.
Assuming that aa_employee.emp_dob is of type date AND assuming that by '01-jan-18' you mean January 1st, 2018, either you do this:
BEGIN
dyn_aa_employee('emp_dob', date '2018-01-01', 110);
END;
or you could change your procedure to:
CREATE OR REPLACE PROCEDURE dyn_aa_employee (
p_col VARCHAR2,
p_dob IN aa_employee.emp_dob%TYPE,
p_id NUMBER)
IS
BEGIN
EXECUTE IMMEDIATE
'UPDATE aa_employee SET ' || p_col || ' = :ph_dob WHERE EMP_NUM = :ph_id'
USING TO_DATE (p_dob, 'DD-MON-YY'), p_id;
END;
It would be interesting to see the actual assignment, though. It's a rather confused scenario overall and I don't see how it teaches you much except to identify several things you probably shouldn't do.
Well first off you didn't specify the date format you were passing. Therefore accepting whatever format the professor had setup. This is a bad plan, to be safe always specify your date format and don't depend on the default. Defaults can and are changed too often. This is a good lesson why you should always specify the actual date format you're using. Try the following to see the difference:
with ds as
(select '01-jan-18' dt_stg from dual)
select to_date(dt_stg), to_date(dt_stg, 'dd-mon-rr') from ds;
Secondly, seems the assignment was to create a procedure that could update any column. This procedure can update only a column having the same type as "aa_employee.emp_dob%TYPE",presumable a date, but it cannot update any other column type.
Finally, if your trying to figure out what professor thinks you did wrong, then try something strange: ask your professor!
This is the error message: PLS-00428: an INTO clause is expected in this SELECT statement. Meanwhile, this is the procedure for testing displaying the system date:
CREATE OR REPLACE
PROCEDURE "TEST_PROCEDURE"
AS
BEGIN
SELECT SYSDATE FROM DUAL;
END;
In the first place I don't need to use that INTO Oracle is insisting for me to do. Is there other way around beside using a cursor (I've seen it here https://stackoverflow.com/a/6029963/1983024)? I think it should not be like that, this does run normally just like in MS SQL without using INTO or cursor.
In the first place I don't need to use that INTO Oracle is insisting
for me to do.
The fact is, Oracle is correct: you do need to use an INTO to take the return value.
After all, if you want to display the result of the query you're going to need a variable to put it in first.
you can write
CREATE OR REPLACE
PROCEDURE "TEST_PROCEDURE"
AS
BEGIN
for r_row in ( SELECT SYSDATE s FROM DUAL)
dbms_output.put_line(r_row.s);
end loop;
END;
or you have to have a variable.
CREATE OR REPLACE
PROCEDURE "TEST_PROCEDURE"
AS
v_Date date;
BEGIN
SELECT SYSDATE into v_date FROM DUAL;
dbms_output.put_line(v_date );
END;
output format is dictated by your NLS_DATE_FORMAT setting, or you can be explicit like to_char(v_date, 'dd-mm-yyyy') etc.
Finally found a solution to the output I want (based on your responses) :D
CREATE OR REPLACE
PROCEDURE "TEST_PROCEDURE"
RET_DATE CHAR(10);
BEGIN
SELECT TO_CHAR(SYSDATE, 'MM/DD/YYYY') INTO RET_DATE FROM DUAL;
DBMS_OUTPUT.PUT_LINE(RET_DATE);
END;
This displays the SYSDATE with format of MM/DD/YYYY. Still, thanks for the replies and ideas (mostly #Jeffrey Kemp). Oracle just lengthens what MS SQL can normally do in one line :D
You can use like
CREATE OR replace PROCEDURE Test_procedure
IS
date CHAR(10);
BEGIN
SELECT To_char(SYSDATE, 'MM/DD/YYYY')
INTO date
FROM dual;
dbms_output.Put_line(date);
END;
it will return date into char format.
If you want to get date into date format just declare the date type variable then assign the sysdate value INTO that variable.Then use DBMS_OUTPUT.PUT_LINE(variable) to print the DATE.
If you wanted to do it in one line, you could also use:
CREATE OR replace PROCEDURE Test_procedure
IS
BEGIN
dbms_output.put_line(to_char(sysdate, 'MM/DD/YY'));
END;