Oracle. Select data from one session but commit it to another. Is it possible? - oracle

Probably I ask for the impossible, but I'll ask anyway.
Is there an easy way to select from one Oracle session and then insert/commit into another?
(I guess, technically it could be done with pl/sql procedure calls and PRAGMA AUTONOMUS Transactions, but it would be a hassle)
I have the following scenario:
I run some heavy calculations and update / insert into some tables.
After the process is completed I would like to 'backup' the results
(create table as select or insert into another temp table) and then rollback my current session without loosing the backups.
Here is desired/expected behavior:
Oracle 11g
insert into TableA (A,B,C) values (1,2,3);
select * from TableA
Result: 1,2,3
create table [in another session] TempA
as select * from TableA [in this session];
rollback;
select * from TableA;
Result null
select * from TempA;
Result 1,2,3
Is this possible?

Is there an easy way to select from one Oracle session and then insert/commit into another?
Create a program in a third-party language (C++, Java, PHP, etc.) that opens two connections to the database; they will have different sessions regardless of whether you connect as different users or both the same user. Read from one connection and write to the other connection.

you can insert your "heavy calculation" into a Oracle temp Table .
CREATE GLOBAL TEMPORARY TABLE HeavyCalc (
id NUMBER,
description VARCHAR2(20)
)
ON COMMIT DELETE ROWS;
the trick is that when you commit the transaction all rows are deleted from temporary table.
Then you first insert data into the temp table, copy the result to you backup table and commit the transaction.

Related

Would a Temporary table be dropped automatically in Oracle?

Forgive me to ask a silly question.
Would a temporary table be dropped automatically in Oracle (12c)?
Yesterday I have executed the following DDL to create a temporary table:
Create global temporary table my_1st_t_table on commit preserve rows as
select
*
from
other_table
where
selected_col = 'T';
After that I have executed following statements:
commit;
select count(*) from my_1st_t_table;
Yesterday, the last select statement returned 2000 rows.
After that I disconnected my VPN and also switched off my client laptop.
Today I rerun the last select statement after restarted my computer and reconnected to the VPN.
It returned 0 rows. So this means the table was still there but just all rows being deleted after my session.
However, may I ask when will my temporary table be dropped?
Thanks in advance!
A temporary table in Oracle is much different than a temp table in other database platforms such as MS SQL Server, and the "temporary" nomenclature invariably leads to confusion.
In Oracle, a temporary table just like other tables, and does not get "dropped". However, the rows in the table only exist within the context of the session that inserted the rows. Once the session is terminated, assuming the session did not delete the rows, Oracle will delete the rows in the table for that session.
So bottom line, the data is temporary, the table structure is permanent, until the table is dropped.

The different 'on commit' setting with Oracle Global Temp Table

Would any one please advise on the mechanism behind the two different settings for the Oracle GTT?
1) on commit preserve rows
2) on commit delete rows
For now I know there 'facts':
a) the records inserted into these 2 types of GTT have different lifecycle.
b) the definition of both types of GTT remains until we drop the GTT <REF>.
However, what I would like to know i whether there is any difference between the 2 types of GTT in terms of the fact b)?
I was told that, for the 'preserve' type of GTT, the table's definition will not only remain but will accumulate by the times of usage (i.e. if there are 10 sessions using the GTT, 10 copies of the table's definition will be created and won't be disappear until we drop the GTT). And if we don't drop the 'preserve' GTT on a regular basis, the SQL statement's performance will become slower and slower.
Please could anyone demystify?
【2018.08.21】
Thanks all for answering the question. Please allow me to refine the question, it is not the table definition of the GTT is being duplicated, but the tablespace being allocated by every sessions using the same GTT that wont be released by the end of session but a dedicated drop of the GTT. Would that be the truth?
You can check the temporary segments associated with the GTTs in v$tempseg_usage:
create global temporary table demo_gtt_preserve (id int) on commit preserve rows;
create global temporary table demo_gtt_delete (id int) on commit delete rows;
insert into demo_gtt_preserve values (1);
insert into demo_gtt_delete values (1);
select s.sql_text, tu.tablespace, tu.contents, tu.segtype, tu.segfile#, tu.segblk#
from v$tempseg_usage tu
join v$sql s on s.sql_id = tu.sql_id_tempseg
where tu.username = user
and tu.segtype = 'DATA'
and tu.session_num = dbms_debug_jdwp.current_session_serial;
Result:
SQL_TEXT TABLESPACE CONTENTS SEGTYP SEGFILE# SEGBLK#
---------------------------------------- ---------- --------- ------ -------- ----------
insert into demo_gtt_delete values (1) TEMP TEMPORARY DATA 401 438528
insert into demo_gtt_preserve values (1) TEMP TEMPORARY DATA 401 438400
Now if you commit and rerun the query, you only get one row:
SQL_TEXT TABLESPACE CONTENTS SEGTYPE SEGFILE# SEGBLK#
---------------------------------------- ---------- --------- ------- -------- ---------
insert into demo_gtt_preserve values (1) TEMP TEMPORARY DATA 401 438400
(Somewhat unhelpfully, v$tempseg_usage identifies the session by session_addr and session_num, which correspond to saddr and serial# in v$session, neither of which are exposed via sys_context. You could extend the query above by joining to v$session and filtering on sid = sys_context('userenv','sid') or audsid = sys_context('userenv','sessionid') if you want to limit it to your own session.)
The only way to clear the remaining entry is to disconnect the session, or drop or truncate the table.
Regarding the performance question, note the way this works: when your session uses a GTT, a completely new temporary segment is created just for you. If other sessions do the same thing, they each get their own separate temporary segments. As those sessions commit or disconnect, the corresponding temporary segments are dropped. There is nothing shared between sessions, because each session has its own separate instance of the temporary table. Therefore, the rumour that if we don't drop the 'preserve' GTT on a regular basis, the SQL statement's performance will become slower and slower doesn't make sense.

oracle replace view during query

What happens if a view is replaced while a query that uses that view is running?
I guess that since a view is just a stored query, nothing will happen. When running a query that includes a view, the view is replaced by the actual query. Is that assumption correct?
Thanks
Since SQL cursor is open Oracle fetches all data from rollback segment. So even you drop table in another session your first session will continue to fetch rows from dropped table. Same for view.
I think currently-running queries will continue to run even if their underlying views are modified.
Do not confuse Data Definition Language (DDL) changes with the Oracle mantra "readers and writers do not block each other". The technology that prevents that blocking does not prevent DDL from interfering with queries. There most certainly are cases where a DDL statement can break a currently-running SQL statement.
The examples below demonstrate a TRUNCATE breaking a SELECT, and then demonstrate a similar CREATE OR REPLACE VIEW not breaking a SELECT.
Truncate interfering with currently-running query
--Session #1 - Create table and data.
drop table table1;
create table table1(a number);
insert into table1 select level from dual connect by level <= 100000;
commit;
--Session #2 - Query table. Use an IDE and only retrieve the first N rows.
select * from table1;
--Session #1 - Truncate table.
truncate table table1;
--Session #2: Retrieve the rest of the rows.
--This may be difficult to reproduce, and may depend on memory
--or other environment settings.
ORA-08103: object no longer exists
View change does not interfere with currently-running query
--Session #1 - Create table and data.
drop table table1;
create table table1(a number);
insert into table1 select level from dual connect by level <= 100000;
commit;
create or replace view view1 as select a from table1;
--Session #2 - Query table. Use an IDE and only retrieve the first N rows.
select * from view1;
--Session #1 - Create a new version of the view.
create or replace view view1 as select 1 a from table1;
--Session #2: Retrieve the rest of the rows.
All rows are returned with the original values.
Warnings
This is only a simple example, do not take this as proof that a SELECT is always immune to view changes. I'm not sure if you could ever prove that view changes don't interfere with SELECT. Your assumption that a view is initially replaced by the query makes sense but I'm not 100% sure it is accurate. Oracle has mechanisms to re-optimize queries in the middle of an execution, it's possible that one of those re-optimizations
can break something.
If you need to be certain this will work, here are some options:
Contact Oracle Support.
Offer a bounty for a counter-example. That still won't prove it's impossible but it certainly will increase your confidence.
Thoroughly test your application.

Global Temporary Table with "On commit delete rows" is not holding any data

I have a global temporary table (GTT) defined in a creation script using the option to delete rows on commit. I wanted to be able to have different users see their own data in the GTT and not the data of other people's sessions. This worked perfectly in our test environment.
But then, I deployed GTT as part of an update to functionality to a client's database. The client called me up all upset and worried, because the GTT wasn't holding any data any more, and they didn't know why.
Specifically, if someone did:
insert into my_GTT (description) values ('Happy happy joy joy')
the database would respond:
1 row inserted.
However, if the same end user tried:
select * from my_GTT
The database would respond:
0 rows returned.
This issue is happening on the client site, and we can't reproduce it in house. What could be causing this behavior?
ON COMMIT DELETE ROWS = data in one transaction
ON COMMIT PRESERVE ROWS = data in one database session (one user with 2 sessions = 2 session = different content)
If GTT is defined with ON COMMIT DELETE ROWS, it would be empty after any explicit commit or implicit commit (= implicit commit = after any DLL command including for example truncate table, alter index, add partition, modify column, exchange partition):
CREATE GLOBAL TEMPORARY TABLE GTT__TEST (A NUMBER) ON COMMIT DELETE ROWS;
INSERT INTO GTT__TEST VALUES (1);
SELECT * FROM GTT__TEST; -- 1 ROW;
COMMIT; -- commit = delete rows
SELECT * FROM GTT__TEST; -- 0 ROWS;
INSERT INTO GTT__TEST VALUES (1);
SELECT * FROM GTT__TEST; -- 1 ROW;
ALTER TABLE GTT__TEST MODIFY A NOT NULL; -- DLL = commit = delete rows
SELECT * FROM GTT__TEST; -- 0 ROWS
If GTT is defined with ON COMMIT PRESERVE ROWS, it would hold data till end of session:
DROP TABLE GTT__TEST;
CREATE GLOBAL TEMPORARY TABLE GTT__TEST (A NUMBER) ON COMMIT PRESERVE ROWS;
INSERT INTO GTT__TEST VALUES (1);
SELECT * FROM GTT__TEST; -- 1 ROW
COMMIT;
SELECT * FROM GTT__TEST; -- 1 ROW
Do you have some setting turned on in your target environment where each statement is auto-committing?
(My experience is in SQL Server, where such is the default, but I understand in Oracle, the default is to keep the transaction open until an explicit commit. Mind, I haven't touched Oracle since ~2000)
I think Damien is right and there is an autocommit. The only other option I can come up with is some sort of connection pool issue (ie the select is being done from a separate session to the insert)

Data loading in Oracle

I am facing problem in loading data. I have to copy 800,000 rows from one table to another in Oracle database.
I tried for 10,000 rows first but the time it took is not satisfactory. I tried using the "BULK COLLECT" and "INSERT INTO SELECT" clause but for both the cases response time is around 35 minutes. This is not the desired response I'm looking for.
Does anyone have any suggestions?
Anirban,
Using an "INSERT INTO SELECT" is the fastest way to populate your table. You may want to extend it with one or two of these hints:
APPEND: to use direct path loading, circumventing the buffer cache
PARALLEL: to use parallel processing if your system has multiple cpu's and this is a one-time operation or an operation that takes place at a time when it doesn't matter that one "selfish" process consumes more resources.
Just using the append hint on my laptop copies 800,000 very small rows below 5 seconds:
SQL> create table one_table (id,name)
2 as
3 select level, 'name' || to_char(level)
4 from dual
5 connect by level <= 800000
6 /
Tabel is aangemaakt.
SQL> create table another_table as select * from one_table where 1=0
2 /
Tabel is aangemaakt.
SQL> select count(*) from another_table
2 /
COUNT(*)
----------
0
1 rij is geselecteerd.
SQL> set timing on
SQL> insert /*+ append */ into another_table select * from one_table
2 /
800000 rijen zijn aangemaakt.
Verstreken: 00:00:04.76
You mention that this operation takes 35 minutes in your case. Can you post some more details, so we can see what exactly is taking 35 minutes?
Regards,
Rob.
I would agree with Rob. Insert into () select is the fastest way to do this.
What exactly do you need to do? If you're trying to do a table rename by copying to a new table and then deleting the old, you might be better off doing a table rename:
alter table
table
rename to
someothertable;
INSERT INTO SELECT is the fastest way to do it.
If possible/necessary, disable all indexes on the target table first.
If you have no existing data in the target table, you can also try CREATE AS SELECT.
As with the above, I would recommend the Insert INTO ... AS select .... or CREATE TABLE ... AS SELECT ... as the fastest way to copy a large volume of data between two tables.
You want to look up the direct-load insert in your oracle documentation. This adds two items to your statements: parallel and nologging. Repeat the tests but do the following:
CREATE TABLE Table2 AS SELECT * FROM Table1 where 1=2;
ALTER TABLE Table2 NOLOGGING;
ALTER TABLE TABLE2 PARALLEL (10);
ALTER TABLE TABLE1 PARALLEL (10);
ALTER SESSION ENABLE PARALLEL DML;
INSERT INTO TABLE2 SELECT * FROM Table 1;
COMMIT;
ALTER TABLE 2 LOGGING:
This turns off the rollback logging for inserts into the table. If the system crashes, there's not recovery and you can't do a rollback on the transaction. The PARALLEL uses N worker thread to copy the data in blocks. You'll have to experiment with the number of parallel worker threads to get best results on your system.
Is the table you are copying to the same structure as the other table? Does it have data or are you creating a new one? Can you use exp/imp? Exp can be give a query to limit what it exports and then imported into the db. What is the total size of the table you are copying from? If you are copying most of the data from one table to a second, can you instead copy the full table using exp/imp and then remove the unwanted rows which would be less than copying.
try to drop all indexes/constraints on your destination table and then re-create them after data load.
use /*+NOLOGGING*/ hint in case you use NOARCHIVELOG mode, or consider to do the backup right after the operation.

Resources