Perl + oracle dba - oracle

I'm newbie in perl and trying to list out all oracle schema tables which were created yesterday i.e. (sysdate - 1)
trying to achieve above via perl script and run against oracle database.
Q:
1. List all recently created oracle table using perl script
2. columns in result (Schema, table_name, created datetime)
3. Only provide output if any new table was created orelse no email via scheduler
e.g.
select schema, table_name, created
from dba_objects
where created = sysdate - 1
and object_type = 'TABLE';

A date in Oracle, by definition, includes both a day component and a time to the second. Even if your session's NLS_DATE_FORMAT only specifies that you want to display the day component, the time is still there. And the time is almost certainly causing your query to return no rows. sysdate - 1 returns exactly 24 hours ago. So if today is January 14, 2013 at 13:41:35, sysdate - 1 returns January 13, 2013 at 13:41:35. Your query will only return tables that were created at that exact second.
trunc(sysdate) returns today at midnight and trunc(sysdate - 1) returns yesterday at midnight. So, most likely, you want something like
SELECT owner, object_name
FROM dba_objects
WHERE created >= trunc(sysdate - 1)
AND created < trunc(sysdate)
AND object_type = 'TABLE'
Since dba_objects doesn't have columns schema or table_name, I'm assuming that you mean owner and object_name.

Related

Date operations in Oracle

I'm trying to run this queries (Oracle 12c):
SELECT trunc(sysdate) - '25-SEP-18' FROM dual;
SELECT 1 FROM dual WHERE trunc(sysdate) = '04-SEP-19';
CREATE TABLE my_table (order_date date);
INSERT INTO my_table (order_date) VALUES ('04-SEP-19');
I expect implicit conversion and everything is good with the 2 last queries, but for the first i get error ORA-01722: invalid number. NLS_DATE_FORMAT = 'DD-MON-RR'. What is the problem?
The question is WHY is does not work? I didn't find any explanations in documentation.
The documentation has a section on Datetime/Interval Arithmetic which explains what is allowed. The table shows that arithmetic is only allowed between dates, timestamp, intervals and numbers. When you do:
SELECT trunc(sysdate) - '25-SEP-18'
you are trying to subtract a string from a date, which isn't possible. Oracle 'helpfully' tries anyway and interprets the string as a number, effectively doing:
SELECT trunc(sysdate) - to_number('25-SEP-18')
which understandably throws the error you see, "ORA-01722: invalid number". As already said, you should explicitly convert your string to a date:
SELECT trunc(sysdate) - to_number('25-SEP-18', 'DD-MON-RR')
or preferably with a four-digit year, and since you're using a month name it's safer to specify the language that is in:
SELECT trunc(sysdate) - to_number('25-SEP-2018', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=ENGLISH')
or more simply, if it's a fixed value, with a date literal:
SELECT trunc(sysdate) - DATE '2018-09-25'
I expect implicit conversion
You should not rely on implicit conversion, particularly where that is influenced by session NLS settins. As well as the date language I already mentioned, someone else running your statement could have a different NLS_DATE_FORMAT setting which could lead to errors or more subtle data mismatches or corruption; e.g.
alter session set nls_date_format = 'DD-MON-YYYY';
SELECT trunc(sysdate) - DATE '2018-09-25' FROM dual;
TRUNC(SYSDATE)-DATE'2018-09-25'
-------------------------------
344
SELECT trunc(sysdate) - to_date('25-SEP-18') FROM dual;
TRUNC(SYSDATE)-TO_DATE('25-SEP-18')
-----------------------------------
730831
SELECT 1 FROM dual WHERE trunc(sysdate) = '04-SEP-19';
no rows selected
CREATE TABLE my_table (order_date date);
INSERT INTO my_table (order_date) VALUES ('04-SEP-19');
The second query gets a much bigger value than expected; and the third gets no rows back from dual.
Looking at the implicitly converted date shows you why:
SELECT to_char(order_date, 'SYYYY-MM-DD HH24:MI:SS') FROM my_table;
TO_CHAR(ORDER_DATE,'
--------------------
0019-09-04 00:00:00
With a YYYY mask (and no FX modifier) a 2-digit year value like 19 is converted as 0019, not 2019. That sort of problem could go unnoticed for some time, giving you incorrect results in the meantime.
If the session's format mask had RRRR or - as you have - RR then it would be interpreted as 2019; but the point is that you usually have no control over the settings in another session that runs your code later.
You can also cause performance issues or errors by creating implicit conversions where you didn't expect, or where they behave in a way you didn't expect. Not in this example - "When comparing a character value with a DATE value, Oracle converts the character data to DATE" - but it still comes up. It's better to avoid the possibility.
When dealing with strings with dates in them you should use the to TO_DATE command, otherwise Oracle may not always figure out that the string contains a date.
SELECT trunc(sysdate) - TO_DATE('25-SEP-18') FROM dual;
Even better is to indicate the format of the date within the string
SELECT trunc(sysdate) - TO_DATE('25-SEP-18','DD-MON-RR') FROM dual;

How do I get a list of all the last modified tables in an Oracle database?

select ora_rowscn from table_name;
ORA_ROWSCN returns the conservative upper bound system change number (SCN) of the most recent change to the row. This pseudocolumn is useful for determining approximately when a row was last updated.
How do I get the timestamp from here? Also, is there any query by which I can get all the last modified tables in a particular schema?
SCN_TO_TIMESTAMP takes as an argument a number that evaluates to a system change number (SCN), and returns the approximate timestamp associated with that SCN.
SELECT SCN_TO_TIMESTAMP(ORA_ROWSCN)
FROM employees
WHERE employee_id = 188;
If you have 10g or above, you can use Oracle's flashback functionality to get this information. You would need to enable flashback;
select table_name ,max(commit_timestamp)
from FLASHBACK_TRANSACTION_QUERY
where table_owner = 'YOUR_SCHEMA'
and operation in ('INSERT','UPDATE','DELETE','MERGE')
group by table_name
There should be no need to get involved with SCNs for what you are asking. Why not just:
begin dbms_stats.flush_database_monitoring_info; end;
select * from dba_tab_modifications
where timestamp >= sysdate - 7
order by timestamp desc;
Unless you are (foolishly?) running with only BASIC statistics level (default is TYPICAL, which is higher), this should work fine in any 11g database or later.

I'd like to select an arbitrary date from Oracle

Everyone knows you can do SELECT SYSDATE FROM DUAL to get a single row representing today's date.
I'd like to do something similar, but it would look like:
SELECT MAGICDATE FROM DUAL
WHERE MAGICDATE = Prompted in the application software
Bit of a long story why...
I'm using a package called SAP Business Objects. It translates user queries into SQL and can get a little tricky. I would like to pull out the user prompts into separate little sub-queries, then use the results of the prompts to power my main queries.
The queries look like:
select user_id, test_centre, test_date, count(*)
from
Lots of tables
where user_id in Prompted User Id
and test_centre in Prompted test_centre
and test_date between Prompted Date 1 and Prompted Date 2
Here's one way of doing it:
SELECT TRUNC(SYSDATE) + dbms_random.value(-5000, 5000) random_date,
TRUNC(SYSDATE) + floor(dbms_random.value(-5000, 5000)) random_datetime
FROM dual;
I've provided ways to get both a random date and a random date + time. Note that I've used TRUNC(SYSDATE) as the seed date and range of 5000 days before and after. You may wish to change the seed date so that it's SYSDATE or even a fixed date, as well as to amend the date ranges as per your requirements.
Can you just do this?
select to_date([prompted_date],'someformat') from dual
The actual answer to your question is:
select #prompt(.....) from dual
But I don't think that is actually what you're looking for. Can you be more specific about what you are attempting to do?

IReport Creation Issue- Get all Months of Year

I want to create a crosstab for LeaveCount for Employees for respective Months with iReport (JasperReports). I'm using Oracle database.
The problem is, I'm getting only the months where the measure exists, I want to display all months of the year whether the measure(Leave of Employee) exists for this month or not.
You can create a table
CREATE TABLE ALLMONTHS
(
MONTHS_MM varchar2(2)
)
;
And insert all 12 months in this table(01,02,03,04...12).
Now using this table form query as below
SELECT a.MONTHS_MM,b.leavecount
FROM ALLMONTHS a
,(SELECT to_char(leavedate,'MM') AS MONTH,leavecount..."your query")b
WHERE a.MONTHS_MM=b.MONTH(+)
That solved the issue with the answer suggested by #Pu297 . Later I got an even better method which involves no table creation and saves trouble of creating a table every time i need to run report on a new database.
select a.mnth,b.leavecount from
(
SELECT to_char(to_date(LVL,'MM'),'MM') mnth
FROM (select level lvl from dual CONNECT BY LEVEL <=12)
) a
,(SELECT to_char(leavedate,'MM') AS MONTH,leavecount..."your query")b
WHERE a.mnth=b.MONTH(+)
This is better way according to me for this issue.
Cheers!!!

connected by months

Ok, I'm new using this connect by thing. But its always quite useful. I have this small problem you guys might be able to help me...
Given start month (say to_char(sysdate,'YYYYMM')) and end month (say, to_char(add_months(sysdate, 6),'YYYYMM')), want to get the list of months in between, in the same format.
Well, I want to use this into a partitions automation script. My best shot so far (pretty pitiful) yields invalid months e.g.'201034'... (and yea, I know, incredibly inefficient)
Follows the code:
SELECT id
from
(select to_char(add_months(sysdate, 6),'YYYYMM') as tn_end, to_char(sysdate,'YYYYMM') as tn_start from dual) tabla,
(select * from
(Select Level as Id from dual connect by Level <= (Select to_char(add_months(sysdate, 1),'YYYYMM')from dual)) where id > to_char(sysdate,'YYYYMM')) t
Where
t.Id between tabla.tn_start and tabla.tn_end
how do I do to make this query return only valid months? Any tips?
cheers mates,
f.
Best way might be to separate out the row generator from the date function. So generate a list from 0 to 6 and calculate months from that. If you want to pass the months in then do that in the with clause
with my_counter as (
Select Level-1 as id
from dual
connect by Level <= 7
)
select to_char(add_months(sysdate, id),'YYYYMM') from my_counter
The example below will allow you to plug in the dates you require to work out the difference.
with my_counter as (
Select Level-1 as id
from dual
connect by level <= months_between(add_months(trunc(sysdate,'MM'), 6),
trunc(sysdate,'MM')) + 1
)
select to_char(add_months(trunc(sysdate, 'MM'), id),'YYYYMM') from my_counter
For generating dates and date ranges, I strongly suggest you create a permanent calendar table with one row for each day. Even if you keep 20 years in this table it will be small ~7500 rows. Having such a table lets you attach additional (potentially non-standard) information to a date. For example your company may use a 6-week reporting period which you cannot extract using TO_CHAR / TO_DATE. Pre-compute it and store it in this table.
Oh, and Oracle 11g has automatic partition management. If you are stuck with 10g, then this article may be of interest to you? Automatic Partition Management for Oracle 10g
Try this:
with numbers as
( select level as n from dual
connect by level <= 7
)
select to_char (add_months (trunc(sysdate,'MM'), n-1), 'YYYYMM') id
from numbers;
ID
------
201012
201101
201102
201103
201104
201105
201106

Resources