Oracle Reports SELECT INTO Not Selecting - oracle

I'm working on a Report in Oracle Reports Builder, and having issues with my AFTERPFORM trigger. When viewing the report, it seems like the year is being processed properly, while the period and subperiod are using their initial values. I'm confused because they are the exact same select statement, but are not working as intended. Any help would be greatly appreciated!
function AfterPForm return boolean is
v_subpdenddt_user date;
v_subpdenddt_max date;
v_rowcount integer;
begin
select value into year from wos_report_param where parameter = 'year' and sequence_num = :sequencenum;
select value into period from wos_report_param where parameter = 'period' and sequence_num = :sequencenum;
select value into user_subpd from wos_report_param where parameter = 'subpd' and sequence_num = :sequencenum;

you have to check variable datatypes are they matched the database columns?
try to change the above code to work with varchar2 variables, then convert varchar2 to date if you want to deal with dates, or to numbers if you want to deal with numbers.

Related

comparar fechas de diferentes tablas al detalle de los segundos

Good evening,
I have a SP and I want to compare 2 dates from different tables, but in the form 'dd/mm/yyyy hh:mi:ss'
I am using to_char(date01,'dd/mm/yyyy hh:mi:ss')> to_char(date02,'dd/mm/yyyy hh:mi:ss')
but it throws me errors.
For example: if the date is 02/12/2016 07:40:12>02/02/2022 06:40:46
it indicates that it is true, and it is not, it is considering the day and not the entire date.
when I only use date01>date02, I have the problem you consider for example.
'02/15/2022 07:48:50'='02/15/2022 07:50:22' (only considers the date)
How can I compare date, minutes and seconds regardless of the server configuration.
Thank you,
PROCEDURE SPU_CUENTA
(
p_nro in varchar2,
pr_Ret OUT number
) is
vfecha varchar(100);
vcount int;
begin
select COUNT(DFEC_SISTEMA) into vcount from TAB Where c=1;
IF vcount>0 THEN
select to_char(DFEC_SISTEMA,'dd/mm/yyyy hh:mi:ss') into vfecha from TAB Where c=1;
EXECUTE IMMEDIATE 'SELECT COUNT(DFEC_ANULA) FROM tablab WHERE to_char(DFEC_ANULA,'dd/mm/yyyy hh:mi:ss')>'''||vfecha||'''' into pr_Ret;
END IF;
end;
Code you suggest would make sense if columns involved were VARCHAR2 (which is a bad idea; store dates into DATE datatype columns).
If those columns really are DATEs, then part of your question (which suggests format) is meaningless - we compare dates as they are, simply by e.g. date1 > date2. Converting them to characters - in a format you specified - is plain wrong.
If those columns are strings, then you'll have to convert them TO_DATE, not TO_CHAR
Procedure you wrote should be a function; they are designed to return a value. Yes, you can use a procedure, but - why would you? You can't use it in SQL (only in PL/SQL).
Besides, code can be heavily shortened/optimized, as you need just one select statement. You don't have to first check whether there any rows in tab that satisfy the condition, and then select some other info - use a subquery instead.
Finally, why are you using dynamic SQL? There's nothing dynamic in your code.
I'd suggest something like this, see if it makes sense.
FUNCTION spu_cuenta (p_nro IN VARCHAR2)
RETURN NUMBER
IS
pr_ret NUMBER;
BEGIN
SELECT COUNT (dfec_anula)
INTO pr_ret
FROM tablab
WHERE dfec_anula > (SELECT dfec_sistema
FROM tab
WHERE c = 1);
RETURN pr_ret;
END;

getting error of character string buffer too small even after changing to varchar(32000)

getting error QRA-06502 of character string buffer too small even after changing to varchar(32000)
create or replace function product_purchase(CUSTOMER_COD VARCHAR2) return varchar is
CODE VARCHAR2(32000);
BEGIN
CODE := ' ';
FOR PRODUCTS_INFO IN (SELECT CUSTOMER_CODE,PRODUCT_NAME
FROM ORDER_DETAIL JOIN ORDERS
ON ORDER_DETAIL.ORDER_ID = ORDERS.ORDER_ID)
LOOP
CODE := CODE || PRODUCTS_INFO.CUSTOMER_CODE||PRODUCTS_INFO.PRODUCT_NAME ||',';
END LOOP;
RETURN CODE;
END product_purchase;
/
ERROR MAINLY OCCURS WHILE SELECTING DATA AND PUTTING IT IN THE FUNCTION
SELECT CUSTOMER_CODE ,product_purchase(CUSTOMER_CODE),COUNT(PRODUCT_NAME)
FROM ORDER_DETAIL JOIN ORDERS
ON ORDER_DETAIL.ORDER_ID = ORDERS.ORDER_ID
GROUP BY CUSTOMER_CODE,product_purchase(CUSTOMER_CODE)
HAVING COUNT(PRODUCT_NAME)=3;
Even after changing to varchar2(32000)? Well, that doesn't guarantee that you'll get the result (obviously).
The way I see it, it is cursor query that is wrong. If you're calling a function as
product_purchase(CUSTOMER_CODE)
it means that you're passing a parameter to it (you didn't post the whole function code so I'll presume that parameter's name is par_customer_code; note that it shouldn't be just customer_code as it is equal to column name and you'll get unexpected result once you use the parameter).
Exactly - use the parameter. Code you wrote doesn't use it. Should've been
SELECT CUSTOMER_CODE,PRODUCT_NAME
FROM ORDER_DETAIL JOIN ORDERS
ON ORDER_DETAIL.ORDER_ID = ORDERS.ORDER_ID
WHERE customer_code = par_customer_code --> this
I guess that - applying that change - you won't have problems.
However, if you do, then check whether code you wrote actually works, run the cursor query itself and see how many rows it returns. Then, restrict number of rows. How? A simple way is to use rownum:
SELECT CUSTOMER_CODE,PRODUCT_NAME
FROM ORDER_DETAIL JOIN ORDERS
ON ORDER_DETAIL.ORDER_ID = ORDERS.ORDER_ID
WHERE customer_code = par_customer_code
AND rownum <= 10; --> this
and run your code again.
If it works, and if it turns out that varchar2 isn't capable of storing that much data, use CLOB datatype instead:
DECLARE
CODE CLOB; --> this

Create simple PL/SQL variable - Use Variable in WHERE clause

Thanks for looking...
I've spent hours researching this and I can't believe it's that difficult to do something in PL/SQL that is simple in TSQL.
I have a simple query that joins 2 tables:
Select DISTINCT
to_char(TO_DATE('1899123000', 'yymmddhh24')+ seg.NOM_DATE, 'mm/dd/yyyy') AS "Record Date"
, cd.CODE
, EMP.ID
, EMP.SHORT_NAME
FROM
EWFM.GEN_SEG seg join EWFM.SEG_CODE cd ON seg.SEG_CODE_SK = cd.SEG_CODE_SK
join EMP on seg.EMP_SK = EMP.EMP_SK
where NOM_DATE = vMyDate;
I use Toad Date Point and I'm querying against an Oracle Exadata source. The resulting query will be dropped into a visualization tool like QlikView or Tableau. I'd like to create a simple variable to use the the WHERE clause as you can see in the code.
In this example, NOM_DATE is an integer such as 42793 (2/27/2017) as you can see in the first row "Record Date". Nothing new here, not very exciting... Until... I tried to create a variable to make the query more dynamic.
I've tried a surprising variety of examples found here, all have failed. Such as:
declare
myDate number(8);
Begin
myDate := 42793;
--Fail ORA-06550 INTO Clause is expected
variable nomDate NUMBER
DEFINE nomDate = 42793
EXEC : nomDate := ' & nomDate'
...where NOM_DATE = ( & nomDate) ;
--ORA-00900: invalid SQL statement
and
variable nomDate NUMBER;
EXEC nomDate := 42793;
select count(DET_SEG_SK) from DET_SEG
where NOM_DATE = :nomDate;
--ORA-00900: invalid SQL statement
and several more.. hopefully you get the idea. I've spent a few hours researching stackoverflow for a correct answer but as you can see, I'm asking you. From simple declarations like "Var" to more complex " DECLARE, BEGIN, SELECT INTO...." to actually creating Functions, using cursors to iterate the output.... I still can't make a simple variable to use in a Where clause.
Please explain the error of my ways.
--Forlorn SQL Dev
Since you are using an implicit cursor, you have to select then INTO variables. Now I d not know the data types of you variables, so I have just guessed in this example below, but hopefully you get the point.
Two other things I should mention
Why are you TO_CHARing you DATE. Just use a DATE datatype. Also, I think your format mask is wrong too 1899123000 does not match yymmddhh24.
In explicit cursor expects exactly one row; no rows and you get NO_DATA_FOUND; more than one and you get TOO_MANY_ROWS
Declare
myDate number(8) := 42793;
/* These 4 variable data types are a guess */
v_record_date varchar2(8);
v_cd_code varchar2(10);
v_emp_id number(4);
v_emp_short_name varchar2(100);
BEGIN
Select DISTINCT to_char(TO_DATE('1899123000', 'yymmddhh24')
+ eg.NOM_DATE, 'mm/dd/yyyy') AS "Record Date"
, cd.CODE
, EMP.ID
, EMP.SHORT_NAME
INTO v_record_date, v_cd_code, v_emp_id, v_emp_short_name
FROM EWFM.GEN_SEG seg
join EWFM.SEG_CODE cd
ON seg.SEG_CODE_SK = cd.SEG_CODE_SK
join EMP
on seg.EMP_SK = EMP.EMP_SK
where NOM_DATE = myDate;
END;
/
VARIABLE vMyDate NUMBER;
BEGIN
:vMyDate := 42793;
END;
/
-- or
-- EXEC :vMyDate := 42793;
SELECT DISTINCT
TO_CHAR( DATE '1899-12-30' + seg.NOM_DATE, 'mm/dd/yyyy') AS "Record Date"
, cd.CODE
, EMP.ID
, EMP.SHORT_NAME
FROM EWFM.GEN_SEG seg
join EWFM.SEG_CODE cd
ON seg.SEG_CODE_SK = cd.SEG_CODE_SK
join EMP
on seg.EMP_SK = EMP.EMP_SK
WHERE NOM_DATE = :vMyDate;
You put the variables with getter and setter in a package.
Then use a view that uses the package getter
Personally I prefer to use a collection that way I can do a select * from table (packagage.func(myparam))

Force Oracle to process one row at a time

I have a query that in the select statement uses a custom built function to return one of the values.
The problem I have is every now and then this function will error out because it returns more than one row of information. SQL Error: ORA-01422: exact fetch returns more than requested number of rows
To further compound the issue I have checked the table data within the range that this query should be running and can't find any rows that would duplicate based on the where clause of this Function.
So I would like a quick way to identify on which Row of the original query this crashes so that I can take the values from that query that would be passed into the function and rebuild the Functions query with these values to get it's result and see which two or more rows are returned.
Any ideas? I was hoping there could be a way to force Oracle to process one row at a time until it errors so you can see the results UP to the first error.
Added the code:
FUNCTION EFFPEG
--Returns Effective Pegged Freight given a Effdate, ShipTo, Item
DATE1 IN NUMBER -- Effective Date (JULIANDATE)
, SHAN IN NUMBER -- ShipTo Number (Numeric)
, ITM IN NUMBER -- Short Item Number (Numeric)
, AST IN VARCHAR -- Advance Pricing type (varchar)
, MCU IN VARCHAR Default Null --ShipFrom Plant (varchar)
) RETURN Number
IS
vReturn Number;
BEGIN
Select ADFVTR/10000
into vReturn
from PRODDTA.F4072
where ADEFTJ <= DATE1
and ADEXDJ >= DATE1
and ADAN8 = SHAN and ADITM = ITM
and TRIM(ADAST) = TRIM(AST)
and ADEXDJ = (
Select min(ADEXDJ) ADEXDJ
from PRODDTA.F4072
where ADEFTJ <= DATE1
and ADEXDJ >= DATE1
and ADAN8 = SHAN
and ADITM = ITM
and TRIM(ADAST) = TRIM(AST));
Query that calls this code and passes in the values is:
select GLEXR, ORDTYPE,
EFFPEG(SDADDJ, SDSHAN, SDITM, 'PEGFRTT', SDMCU),
from proddta.F42119
I think the best way to do it is trough Exceptions.
What you need to do is to add the code to handle many rows exception in your function:
EXCEPTION
WHEN TOO_MANY_ROWS THEN
INSERT INTO ERR_TABLE
SELECT your_columns
FROM query_that_sometimes_returns_multiple_rows
In this example the doubled result will go to separated table or you can decide to simply print out with dbms_output.
An easy page to start can be this, then just google exception and you should be able to find all you need.
Hope this can help.

How to handle to_date exceptions in a SELECT statment to ignore those rows?

I have the following query that I am attempting to use as a COMMAND in a crystal report that I am working on.
SELECT * FROM myTable
WHERE to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate}
This works fine, however my only concern is that the date may not always be in the correct format (due to user error). I know that when the to_date function fails it throws an exception.. is it possible to handle this exception in such a way that it ignores the corresponding row in my SELECT statement? Because otherwise my report would break if only one date in the entire database is incorrectly formatted.
I looked to see if Oracle offers an isDate function, but it seems like you are supposed to just handle the exception. Any help would be greatly appreciated. Thanks!!
Echoing Tony's comment, you'd be far better off storing dates in DATE columns rather than forcing a front-end query tool to find and handle these exceptions.
If you're stuck with an incorrect data model, however, the simplest option in earlier versions is to create a function that does the conversion and handles the error,
CREATE OR REPLACE FUNCTION my_to_date( p_date_str IN VARCHAR2,
p_format_mask IN VARCHAR2 )
RETURN DATE
IS
l_date DATE;
BEGIN
l_date := to_date( p_date_str, p_format_mask );
RETURN l_date;
EXCEPTION
WHEN others THEN
RETURN null;
END my_to_date;
Your query would then become
SELECT *
FROM myTable
WHERE my_to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate}
Of course, you'd most likely want a function-based index on the MY_TO_DATE call in order to make this query reasonably efficient.
In 12.2, Oracle has added extensions to the to_date and cast functions to handle conversions that error
SELECT *
FROM myTable
WHERE to_date(myTable.sdate default null on conversion error, 'MM/dd/yyyy') <= {?EndDate}
You could also use the validate_conversion function if you're looking for all the rows that are (or are not) valid dates.
SELECT *
FROM myTable
WHERE validate_conversion( myTable.sdate as date, 'MM/DD/YYYY' ) = 1
If your data is not consistent and dates stored as strings may not be valid then you have 3 options.
Refactor your DB to make sure that the column stores a date datatype
Handle the exception of string to date in a stored procedure
Handle the exception of string to date in a (complex) record selection formula
I would suggest using the first option as your data should be consistent.
The second option will provide some flexibility and speed as the report will only fetch the rows that are needed.
The third option will force the report to fetch every record in the table and then have the report filter down the records.
I have the same problem... an old legacy database with varchar fields for dates and decades of bad data in the field. As much as I'd like to, I can't change the datatypes either. But I came up with this solution to find if a date is current, which seems to be what you're doing as well:
select * from MyTable
where regexp_like(sdate, '[0-1][0-9].[0-3][0-9].[0-9][0-9][0-9][0-9]')
-- make sure it's in the right format and ignore rows that are not
and substr(sdate,7,10) || substr(sdate,1,2) || substr(sdate,4,5) >= to_char({?EndDate}, 'YYYYMMDD')
-- put the date in ISO format and do a string compare
The benefit of this approach is it doesn't choke on dates like "February 30".
Starting from Oracle 12c there is no need to define a function to catch the conversion exception.
Oracle introduced an ON CONVERSION ERROR clause in the TO_DATE function.
Basically the clause suppress the error in converting of an invalid date string (typical errors are ORA-01843, ORA-01841, ORA-011861, ORA-01840) and returns a specified default value or null.
Example of usage
select to_date('2020-99-01','yyyy-mm-dd') from dual;
-- ORA-01843: not a valid month
select to_date('2020-99-01' default null on conversion error,'yyyy-mm-dd') from dual;
-- returns NULL
select to_date('2020-99-01' default '2020-01-01' on conversion error,'yyyy-mm-dd') from dual;
-- 01.01.2020 00:00:00
Solution for the Legacy Application
Let's assume there is a table with a date column stored as VARCHAR2(10)
select * from tab;
DATE_CHAR
----------
2021-01-01
2021-99-01
Using the above feature a VIRTUAL DATE column is defined, that either shows the DATE or NULL in case of the conversion error
alter table tab add (
date_d DATE as (to_date(date_char default null on conversion error,'yyyy-mm-dd')) VIRTUAL
);
select * from tab;
DATE_CHAR DATE_D
---------- -------------------
2021-01-01 01.01.2021 00:00:00
2021-99-01
The VIRTUAL column can be safely used because its format is DATE and if required an INDEX can be set up on it.
select * from tab where date_d = date'2021-01-01';
Since you say that you have "no access" to the database, I am assuming that you can not create any functions to help you with this and that you can only run queries?
If that is the case, then the following code should get you most of what you need with the following caveats:
1) The stored date format that you want to evaluate is 'mm/dd/yyyy'. If this is not the case, then you can alter the code to fit your format.
2) The database does not contain invalid dates such as Feb 30th.
First, I created my test table and test data:
create table test ( x number, sdate varchar2(20));
insert into test values (1, null);
insert into test values (2, '01/01/1999');
insert into test values (3, '1999/01/01');
insert into test values (4, '01-01-1999');
insert into test values (5, '01/01-1999');
insert into test values (6, '01-01/1999');
insert into test values (7, '12/31/1999');
insert into test values (8, '31/12/1999');
commit;
Now, the query:
WITH dates AS (
SELECT x
, sdate
, substr(sdate,1,2) as mm
, substr(sdate,4,2) as dd
, substr(sdate,7,4) as yyyy
FROM test
WHERE ( substr(sdate,1,2) IS NOT NAN -- make sure the first 2 characters are digits
AND to_number(substr(sdate,1,2)) between 1 and 12 -- and are between 0 and 12
AND substr(sdate,3,1) = '/' -- make sure the next character is a '/'
AND substr(sdate,4,2) IS NOT NAN -- make sure the next 2 are digits
AND to_number(substr(sdate,4,2)) between 1 and 31 -- and are between 0 and 31
AND substr(sdate,6,1) = '/' -- make sure the next character is a '/'
AND substr(sdate,7,4) IS NOT NAN -- make sure the next 4 are digits
AND to_number(substr(sdate,7,4)) between 1 and 9999 -- and are between 1 and 9999
)
)
SELECT x, sdate
FROM dates
WHERE to_date(mm||'/'||dd||'/'||yyyy,'mm/dd/yyyy') <= to_date('08/01/1999','mm/dd/yyyy');
And my results:
X SDATE
- ----------
2 01/01/1999
The WITH statement will do most of the validating to make sure that the sdate values are at least in the proper format. I had to break out each time unit month / day / year to do the to_date evaluation because I was still getting an invalid month error when I did a to_date on sdate.
I hope this helps.
Trust this reply clarifies...
there is no direct EXCEPTION HANDLER for invalid date.
One easy way is given below once you know the format like DD/MM/YYYY then below given REGEXP_LIKE function will work like a charm.
to_date() also will work, when invalid_date is found then cursor will goto OTHERS EXCEPTION. given below.
DECLARE
tmpnum NUMBER; -- (1=true; 0 = false)
ov_errmsg LONG;
tmpdate DATE;
lv_date VARCHAR2 (15);
BEGIN
lv_date := '6/2/2018'; -- this will fail in *regexp_like* itself
lv_date := '06/22/2018'; -- this will fail in *to_date* and will be caught in *exception WHEN OTHERS* block
lv_date := '07/03/2018'; -- this will succeed
BEGIN
tmpnum := REGEXP_LIKE (lv_date, '[0-9]{2}/[0-9]{2}/[0-9]{4}');
IF tmpnum = 0
THEN -- (1=true; 0 = false)
ov_errmsg := '1. INVALID DATE FORMAT ';
DBMS_OUTPUT.PUT_LINE (ov_errmsg);
RETURN;
END IF;
tmpdate := TO_DATE (lv_date, 'DD/MM/RRRR');
--tmpdate := TRUNC (NVL (to_date(lv_date,'DD/MM/RRRR'), SYSDATE));
tmpnum := 1;
EXCEPTION
WHEN OTHERS
THEN
BEGIN
tmpnum := 0;
ov_errmsg := '2. INVALID DATE FORMAT ';
DBMS_OUTPUT.PUT_LINE (ov_errmsg || SQLERRM);
RETURN;
END;
-- continue with your other query blocks
END;
-- continue with your other query blocks
DBMS_OUTPUT.PUT_LINE (tmpnum);
END;

Resources