comparar fechas de diferentes tablas al detalle de los segundos - oracle

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;

Related

OUT parameter with multiples values

create or replace PROCEDURE Show_R(A IN VARCHAR2, B OUT VARCHAR2)
IS
BEGIN
select func_w(day),TO_CHAR(hour, 'HH24:MI')INTO B
from task t
inner join mat m
on t.id_p = m.id_a
where m.cod_mod = A;
END;
I have a issue with this code, this select gets two types of columns data that are not the same type of data, i don't know how to add into B two types of data in only one "out parameter"
You can't put 2 values into 1 OUT parameter. So, use 2 OUT parameters.
Firstly don't store day and hour in separate columns. Just use a single DATE column as, in Oracle, the DATE data type has year, month, day, hour, minute and second components and so can store both the date and time.
Secondly, don't use A, B, show_R or func_w identifiers; use meaningful names as it will be far easier to debug your code in 6-months if you can tell what it is intended to do.
Third, your SELECT ... INTO statement will fail as you have two columns but only one variable to select into; you need 2 variables in INTO clause and this means (unless you are going to concatenate the two values) that you need 2 OUT parameters.
CREATE PROCEDURE Show_w_day_and_hour(
i_cod_mod IN mat.cod_mod%TYPE,
o_w_day OUT VARCHAR2,
o_hour OUT VARCHAR2
)
IS
BEGIN
SELECT func_w(day),
TO_CHAR(hour, 'HH24:MI')
INTO o_w_day,
o_hour
FROM task t
INNER JOIN mat m
ON ( t.id_p = m.id_a )
WHERE m.cod_mod = i_cod_mod;
END;
/
db<>fiddle

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

Confusion on using variables & constants in PL/SQL

I've new to PL/SQL (and it's been a while since I've used vanilla SQL). I've got a query that I inherited that I'm trying to schedule in TOAD. In order for that to work I have to change hard coded date references to be calculated at run time.
To that end I added a Declare statement to the front of the query, added the necessary constants, setting them at declaration, and then had the query use them.
When I try to execute an error gets thrown saying a Select Into. To my understanding, SELECT Into is used to set a variable based on a value in the db (based on Constants in Oracle SQL query), whereas I'm looking to define the value independent of any value in the db (in this case the date on the server). The full error follows:
ORA-06550: line 6, column 5:
PLS-00428: an INTO clause is expected in the Select statement
So I'm looking for a little guidance on where my understanding of variables/constants in PL/SQL is off, and also help with getting the following to execute:
DECLARE OLD CONSTANT char(11):= to_Char(SYSDATE - 6, 'DD-MON-YYYY');
NEW CONSTANT char(11):= to_char(SYSDATE, 'DD-MON-YYYY');
BEGIN
SELECT CASE
WHEN (userhost LIKE 'a%'
AND userid IN ('s',
'sub')) THEN 'BATCH'
WHEN userid LIKE 'N%' THEN 'N'
WHEN ((userhost LIKE 'b%'
OR userhost LIKE 'c%')
AND userid IN ('s',
'sub')) THEN 'Forms'
WHEN ((userid LIKE '%_IU%'
OR userid LIKE 'RPT%'
OR userid IN ('q',
'r',
'p'))
AND userhost <> 'n%') THEN 'Interface'
ELSE 'Other'
END app_type , round(sum(sessioncpu/100), 1) cpu_seconds , (sum(sessioncpu/100) / (119*1*60*60) * 100) pct_of_cpu,
trunc(ntimestamp#,'MI')
FROM PERFSTAT.AUD$_ARCHIVE
WHERE ntimestamp# BETWEEN to_timestamp(OLD || ' 23:59','DD-MON-YYYY HH24:MI') AND to_timestamp(NEW || ' 00:00','DD-MON-YYYY HH24:MI')
AND logoff$time < to_date(NEW || ' 00:00','DD-MON-YYYY HH24:MI')
GROUP BY CASE
WHEN (userhost LIKE 'a%'
AND userid IN ('s',
'sub')) THEN 'BATCH'
WHEN userid LIKE 'N%' THEN 'N'
WHEN ((userhost LIKE 'b%'
OR userhost LIKE 'c%')
AND userid IN ('s',
'sub')) THEN 'Forms'
WHEN ((userid LIKE '%_IU%'
OR userid LIKE 'RPT%'
OR userid IN ('q',
'r',
'p'))
AND userhost <> 'n%') THEN 'Interface'
ELSE 'Other'
END app_type,
trunc(ntimestamp#,'MI')
ORDER BY trunc(ntimestamp#,'MI'),
1;
END;
You have two issues here. The first is trying to use the CHAR datatype and then not giving it a length. This defaults to a CHAR(1), i.e. a single character. For memory concerns, you might also consider VARCHAR2 instead. https://docs.oracle.com/cd/E17952_01/refman-5.1-en/char.html
The second issue has to do with the INTO clause as mentioned in your question. When you run a SELECT statement in PL/SQL (not associated to DML), you have to give Oracle something to return the result set into. You can then use those variables, whether printing them, storing them, or doing processing with them.
I'd have to see the error, but I think that it may want you to set a length to your char. So, something like char(30).
Also, I'm a big fan of varchar2. Only uses as much space in the DB as the characters in the variable. So, it it's varchar2(500) and has 8 characters, it only uses 8 chars worth of memory.
Your query has an inherent flaw, in that anything that occurs between 23:59 and 0:00 will satisfy conditions at both ends of the range (e.g. something that happens at 23:59:30). If this query were my responsibility, I'd get rid of the variables and the text conversions altogether:
WHERE ntimestamp# >= TRUNC (SYSDATE) - 6
AND ntimestamp# < TRUNC (SYSDATE)
AND logoff$time < TRUNC (SYSDATE)
Using >= and < for dates where you want to avoid an overlap tends to be safer that using between.
Taking a closer look, I'm not sure what the point of your query using one minute before midnight on the lower bound is. That kind of thing is more typically done on the upper bound. Assuming that you're actually doing that for a reason, you can still get around transforming to a string by using either of the following:
WHERE ntimestamp# BETWEEN TRUNC (SYSDATE) - 6 - (1 / 24 / 60)
AND TRUNC (SYSDATE)
AND logoff$time < TRUNC (SYSDATE)
WHERE ntimestamp# BETWEEN TRUNC (SYSDATE)
- NUMTODSINTERVAL (6, 'DAY')
- NUMTODSINTERVAL (1, 'MINUTE')
AND TRUNC (SYSDATE)
AND logoff$time < TRUNC (SYSDATE)
All of that is really just an aside to your main problem though: you need to tell the interpreter what to do with the result of the query. That means that you need to provide a variable to put the result in, then (presumably) do something with the result. One way to do this is to use a cursor loop:
DECLARE
CURSOR cur_query IS
[your query goes here];
BEGIN
FOR r_query IN cur_query LOOP
DBMS_OUTPUT.put_line (r_query.app_type);
DBMS_OUTPUT.put_line (r_query.cpu_seconds);
DBMS_OUTPUT.put_line (r_query.pct_of_cpu);
END LOOP;
END;
Of course, the alternative is just to run your query as SQL, rather than PL/SQL. With the variables eliminated, that will be easier.
Comment Response
PL/SQL blocks are not intended to return query results, like you get if you run straight SQL in Toad. There are ways to fake it via functions that return user-defined types or pipelined functions, but you're better off writing SQL if you are able to (and, in this case, you should be able to).
I'm not sure what you mean by "the variables are supposed to dynamically set the date range to look at". The code provided is returning data relative to sysdate, not getting outside data. You can do that in the query as easily as you can in a PL/SQL block.

Oracle Reports SELECT INTO Not Selecting

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.

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