Fetch changed data for table in Oracle Database to csv - sql

I Need to fetch the changed data for table in oracle. Need help with the sql query which will fetch only the changed data for every 20 mins.
Or is there another workaround for this where we can fetch changed data?

If you know how to detect "only changed data" - I presume you must have some kind of a "timestamp" column - then that would be
select columns
from your_table
where timestamp_column >= sysdate - 20/(24*60);
because of this: 20 minutes / (24 hours in a day * 60 minutes in an hour)
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss'
Session altered.
SQL> select sysdate right_now,
2 sysdate - 20/(24*60) twenty_minutes_ago
3 from dual;
RIGHT_NOW TWENTY_MINUTES_AGO
------------------- -------------------
24.10.2019 08:40:18 24.10.2019 08:20:18
SQL>
How to create a CSV?
One option is to store your query into a .SQL file and use operating system's Task Scheduler (for MS Windows) or cron (for Unix) or whatever your operating system is. Let the script spool the result. There are numerous SQL*Plus SET options you can use to make the output pretty; for example:
set termout off
set trimspool on
set echo off
set verify off
set autoprint off
set serveroutput off
set arraysize 1000
set pagesize 0
set linesize 100
set long 10000
set numwidth 10
set feedback off
set colsep ';'
col empno format 99999
col ename format a10
col sal format 999G990
spool emps.txt
select empno, ename, sal from emp
where timestamp_column >= sysdate - 20/(24*60);
spool off
Another option is to create a stored procedure which will use UTL_FILE package and create that file in a directory (you'll have to talk to your DBA about it; it is - usually - on the database server; DBA should create a directory (as an Oracle object) and grant read/write privileges to you).
Then you'd create a database job (using DBMS_JOB or DBMS_SCHEDULER packages), scheduling it to run every 20 minutes.
Maybe the first one is simpler to implement; if nothing else, CSV is created on your local computer, not on the server.

You need a last_updated column of type datetime/date in your table. Then to get updated in last 20 minutes, the query can become like below.
select t.*
from table t
where t.last_updated >= sysdate - 20/(24*60)
where t is your tablename and last_updated is last updated datetime.

If you don't have a way to detect when rows change within the table then you can use a flashback query on your database (if it is set up to support it).
To get the new/modified rows:
SELECT *
FROM table_name
MINUS
SELECT *
FROM table_name AS OF TIMESTAMP( SYSTIMESTAMP - INTERVAL '20' MINUTE )
and to get the deleted rows:
SELECT *
FROM table_name AS OF TIMESTAMP( SYSTIMESTAMP - INTERVAL '20' MINUTE )
WHERE primary_key_column NOT IN ( SELECT primary_key_column FROM table_name )

Related

Inserting date&time, but only date is inserted

I'm trying to insert in a table the date and the time,but at the end only the date is insrted.
here's the code:
create table myDate(date_value Date PRIMARY KEY );
INSERT INTO myDate(date_value ) VALUES (to_date('14/09/2010 18:00','dd/mm/yyyy hh24:mi'));
And I get only 14/09/2010 stocked in the table myDate.What's the problem(is there a way to do that without timestamp)?
I think it worth noting that an alternatives to setting the session NLS parameter is the explicit use of the to_char function. I prefer the to_char because it leaves no doubt to anyone reading the code. Reliance on NLS can be iffy because it can be set at different levels. Besides, if you don't explicitly use to_char, oracle will still call it in the background, using the controlling setting of NLS_DATE_FORMAT.
SQL> -- create table
SQL> Create table mytest (dob date);
Table created.
Elapsed: 00:00:00.08
SQL> insert into mytest values (sysdate);
1 row created.
Elapsed: 00:00:00.03
SQL> commit;
Commit complete.
Elapsed: 00:00:00.00
SQL> select dob nls_default,
2 to_char(dob,'dd-mm-yyyy') dob1,
3 to_char(dob,'yyyy-mm-dd') dob2,
4 to_char(dob,'dd-mm-yyyy hh24:mi:ss') dob3
5 from mytest;
NLS_DEFAU DOB1 DOB2 DOB3
--------- ---------- ---------- -------------------
11-DEC-20 11-12-2020 2020-12-11 11-12-2020 11:45:31
1 row selected.
Elapsed: 00:00:00.03
SQL> drop table mytest purge;
Table dropped.
For more on the subject, see this article.
Depending on the tool you are using is most likely a display issue.
you might want to try something like this
alter session set nls_date_format = 'DD-MON-YYYY HH12:MI:SS PM';
This will alter your sesssion to display the date with full timestamp.
I use oracle sql developer https://www.oracle.com/database/technologies/appdev/sqldeveloper-landing.html
If I don't set the date to my session while I'm writing a query I may see an unexpected date format.
Let us know if that is the case for you as well.

How do I limit number of records in table, so that old records are deleted, but new rows are kept?

I have a table in our oracle database that is tracking downloads based on the users session in a web application. Over the past few years, this table has grown to millions of records, which is slowing down the application.
I would like to limit this table to only keep the items from the past week, and have it automatically remove the older records. The table has a date field called DOWNLOAD_DATE which could be used for this query.
Is this something I can do with triggers? If so, what is the best way to do so?
I don't think that a trigger is the right thing for that, as it is executed on every insert/update/delete. I would recommend to create a procedure for that which performs a statement like "delete from table_name where trunc(download_date) < trunc(sysdate - 7);"
Then you schedule that procedure to be executed every hour or day based on your needs.
After the delete you may want to refresh the table stats with "DBMS_STATS.GATHER_TABLE_STATS ('schema_name', 'table_name');"
More regarding gathering stats can be found here: http://www.dba-oracle.com/t_dbms_stats_gather_table_stats.htm
Is this something I can do with triggers? If so, what is the best way
to do so?
Well i would say, a Statement level trigger is well suited for this problem. Read about statement level trigger Here
See demo:
SQL> CREATE TABLE tab2 (
col VARCHAR(1),
start_date DATE,
end_date DATE
);
/
Table created
SQL> Select * from tab2;
COL START_DATE END_DATE
--- ----------- -----------
A 11-07-2019 18-07-2019
A 11-07-2019 31-07-2019
A 06-07-2019 31-07-2019
A 01-07-2019 31-07-2019 --- See this row is 10 days older than Sysdate
-- Created Statement level trigger
SQL> CREATE OR REPLACE TRIGGER t1
2 BEFORE INSERT ON tab2
3 BEGIN
4 DELETE FROM tab2
5 WHERE start_date <= SYSDATE - 7; -- Deleting records older that 7 days from sysdate
6 END;
7 /
Trigger created
SQL> Insert into tab2 values('A',sysdate+1,sysdate+10);
1 row inserted
SQL> commit;
Commit complete
-- You can see the records older that 7 days from sysdate is deleted
SQL> Select * from tab2;
COL START_DATE END_DATE
--- ----------- -----------
A 11-07-2019 18-07-2019
A 11-07-2019 31-07-2019
A 06-07-2019 31-07-2019
A 12-07-2019 21-07-2019
IF I had requirement like this, I would go like this:
PROCEDURE p1
IS
BEGIN
LOOP
DELETE FROM table123 --- you can give your table name here
WHERE download_date <= SYSDATE - 7 AND ROWNUM <= 10000; ---if you want to restrict to delete the rows at a time
EXIT WHEN SQL%ROWCOUNT = 0;
COMMIT;
DBMS_LOCK.sleep (10); ----- if you want to give sleep time as per your requirement
END LOOP;
END;
this job will run for 24*7 and delete all the records which are older than 7 days

Setting NLS_SORT variable for a single select only

Good day,
my customer uses an application that was initially designed for MSSQL, which is probably doing case-insensitive searches by default. But the customer uses Oracle and hence, needs some extra tweaking.
So the question is: How can I tell Oracle to make a given SELECT LIKE-Statement search case-insensitive with the following limitations?
ALTER SESSION cannot be used individually (by trigger: maybe)
Other queries from the same session must not be affected
The SELECT-statement cannot be altered
I know about the possibility to set NLS_SORT on system level, but this will basically kill the performance, as all indexes are disabled.
You can use DBMS_ADVANCED_REWRITE to rewrite the SQL into a case-insensitive version.
Subtly changing queries like this can be confusing and can make troubleshooting and tuning difficult. The package also has some limitations that may make it impractical, such as not supporting bind variables.
1. Sample Schema
SQL> drop table test1;
Table dropped.
SQL> create table test1(a varchar2(100));
Table created.
SQL> insert into test1 values ('case INSENSITIVE');
1 row created.
SQL> commit;
Commit complete.
2. The query is initially case-sensitive and matches 0 rows
SQL> select count(*) total from test1 where a like '%case insensitive%';
TOTAL
----------
0
3. Create rewrite equivalence - add a LOWER function
SQL> begin
2 sys.dbms_advanced_rewrite.declare_rewrite_equivalence(
3 name => 'case_insensitive_1',
4 source_stmt => q'[select count(*) total from test1 where a like '%case insensitive%']',
5 destination_stmt => q'[select count(*) total from test1 where lower(a) like '%case insensitive%']',
6 validate => false
7 );
8 end;
9 /
PL/SQL procedure successfully completed.
4. Now the same query is case-insensitive and matches 1 row
SQL> alter session set query_rewrite_integrity = trusted;
Session altered.
SQL> select count(*) total from test1 where a like '%case insensitive%';
TOTAL
----------
1

How to get time string to Time in oracle sql

I have insert time in form of string in oracle VARCHAR2 column. But when I try to retrieve it in form of time it's not giving me right time, it's only giving me a date which I have not saved.
INSERT INTO table1
(timestr) Select substr(numtodsinterval(MAX(date1)-MIN(date2),'day'),
12,8) from table2 where ....; // stored timestr column value: 00:00:00
Retrieve ...
select TO_DATE(timestr,'hh24:mi:ss') from table1;
... is only giving 10/01/2015
You should use to_char to see the time
select to_char(timestr,'hh24:mi:ss') from table1;
That might be because of your client setting. If you are using SQL Developer, then go to Tools->Preference->Database->NLS and change setting to view timestamp also.
If you are using SQLPlus, the change nls_date_format or see below solution.
https://community.oracle.com/thread/312115?start=0&tstart=0
In Oracle there is no concept of a TIME datatype. There are only DATE and TIMESTAMP, both of which comprise date and time elements.
Your retrieval casts your time column to a DATE: TO_DATE(timestr,'hh24:mi:ss'). Oracle does what we ask it to do, so it displays a date. As there is no date element in timestr it assigns one for us: today's date.
You are only seeing 10/01/2015 because your client's NLS settings are configured to display only the date element. Change the setting to show the time element, in the client or by altering the session as here:
SQL> select to_date('10:08:23', 'hh24:mi:ss') ts from dual
2 /
TS
---------
01-OCT-15
SQL> alter session set nls_date_format='DD-MON-YYYY HH24:MI:SS'
2 /
Session altered.
SQL> select to_date('10:08:23', 'hh24:mi:ss') ts from dual
2 /
TS
--------------------
01-OCT-2015 10:08:23
SQL>
If you only want to see the time you can change the NLS settings ...
SQL> alter session set nls_date_format='HH24:MI:SS'
2 /
Session altered.
SQL> select to_date('10:08:23', 'hh24:mi:ss') ts from dual
2 /
TS
--------
10:08:23
SQL>
...but that will get annoying ....
SQL> select sysdate from dual
2 /
SYSDATE
--------
23:59:52
SQL>
So why not just display the column as a string all without casting to a DATE?
select timestr from table1;

Oracle vs. Hypersonic SQL

I need to select by date in a SQL query, for example
SELECT * FROM foo WHERE date = '2009-09-09'
That query works in my Hypersonic test database, but not Oracle, which seems to requires:
SELECT * FROM foo WHERE date = TO_DATE('2009-09-09', 'yyyy-mm-dd')
Is there a way to select by date uniformly across these two databases?
I found the answer - you can create the TO_DATE function in HyperSonic and then the second query works in both. For example, make the class:
public class Date {
public static String toDate( String value, String format ) {
return value;
}
}
And the query
SELECT * FROM foo WHERE date = TO_DATE('2009-09-09', 'yyyy-mm-dd')
works in both.
You could try H2 database as your in memory database (http://www.h2database.com). It should have decent Oracle compablity mode.
HSQLDB 2.0 supports ANSI date literals just as Oracle. So if you can upgrade to HSQLDB 2.0, you can use:
SELECT *
FROM foo
WHERE date_column = DATE '2009-09-09'
in both database (actually a lot more databases even)
A "date = 'literal string'" predicate in Oracle is usually not recommended - it is sensitive to NLS_DATE_FORMAT settings and often leads to misunderstanding on what you're looking for in a result set (in your example above do you want all records for the day or just those created exactly at midnight?)
If you need a uniform query string for both databases, you might rename the table in Oracle and create a view with the name foo and cast the date datatype to varchar2 in the view logic. You'll probably need to add a function-based index to the table to allow efficient searching on the recast value.
If you can, you can set your NLS_DATE_FORMAT in Oracle session, that way you do not need to use the TO_DATE function, oracle will do this for you behind the scenes.
SQL> select value from v$nls_parameters where parameter = 'NLS_DATE_FORMAT';
VALUE
----------------------------------------------------------------
DD/MM/YYYY
SQL> create table nls_date_test ( id number(10) , date_entered date );
Table created.
SQL> insert into nls_date_test values ( 1 , '31/05/2009' );
1 row created.
SQL> insert into nls_date_test values ( 2 , '30/05/2009' );
1 row created.
SQL> select * from nls_date_test where date_entered = '2009-09-09';
select * from nls_date_test where date_entered = '2009-09-09'
*
ERROR at line 1:
ORA-01861: literal does not match format string
SQL> alter session set nls_date_format = 'YYYY-MM-DD';
Session altered.
SQL> select * from nls_date_test where date_entered = '2009-05-30';
ID DATE_ENTER
---------- ----------
2 2009-05-30
SQL> select value from v$nls_parameters where parameter = 'NLS_DATE_FORMAT';
VALUE
----------------------------------------------------------------
YYYY-MM-DD
SQL>