oracle - commit over dblink? - sql

If I connect to an oracle database as user smith, and issue the following 3 commands:
update smith.tablea
set col_name = 'florence' where col_id = 8;
insert into bob.other_table#mylink
values ('blah',2,'uncle','new');
commit;
Does this mean that the update to the local table (smith.tablea) and the insert to the remote db table (bob.other_table) have both been committed or that just the update to the local table has been committed?
Note: that 'mylink' represents a dblink to a remote database.

From documentation
The Oracle two-phase commit mechanism is completely transparent to
users who issue distributed transactions. In fact, users need not even
know the transaction is distributed. A COMMIT statement denoting the
end of a transaction automatically triggers the two-phase commit
mechanism to commit the transaction. No coding or complex statement
syntax is required to include distributed transactions within the body
of a database application.
so - yes, if everything goes fine, both operations are commited.

In this case the transaction should only work if the remote transaction and your local transaction are successfull.
More information about distributed transactions:
http://docs.oracle.com/cd/B19306_01/server.102/b14231/ds_txnman.htm

Related

ACID transactions across multiple technologies

I have an application that uses a local database and a remote database to synchronize to. The local database uses SQLite and for the remote database I'm using postgres. I need to move data from one database to the other database and avoid duplicating information.
Roughly what I do right now:
BEGIN; //remote database (start transaction)
SELECT * FROM local.queued TOP 1; //local database (select first queued element)
INSERT INTO remote.queued VALUES ( element ) //remote database (insert first queued element on remote database)
BEGIN; //local database (start transaction)
DELETE * FROM local.queued LIMIT 1; //local database (delete first queued element on local database)
END; //local database (finalize transaction local database)
END; //remote database (finalize transaction remote database)
This works relatively well most of the times but incidentally, after giving a hard reset to the program I've noticed a data record was duplicated. I believe this is has something to do with the transaction finalizing. Because I'm using two distinct technologies it would be impossible to create a single atomic commit with WAL archiving.
Any ideas how I could improve this concept to avoid duplicative entries.
The canonical way to do that is a distributed transaction using the two-phase commit protocol.
Unfortunately SQLite doesn't seem to support it, but since PostgreSQL does, you can still use it if only two databases are involved:
BEGIN; -- on PostgreSQL
BEGIN; -- on SQLite
/*
* Do work on both databases.
* On error, ROLLBACK both transactions.
*/
PREPARE TRANSACTION 'somename'; -- PostgreSQL
COMMIT; -- SQLite
COMMIT PREPARED 'somename'; -- PostgreSQL
Now if an error happens during the SQLite COMMIT, you run ROLLBACK PREPARED 'sonename' on PostgreSQL. The idea is that everything that can fail during commit is done during PREPARE TRANSACTION, and the state of the transaction is persisted so that it stays open, but will still survive a server restart.
This is safe, but there is a caveat. Prepared transactions are dangerous, because they will hold locks and keep VACUUM from cleaning up (like all other transactions), but they are persistent and stick around until you explicitly remove them. So you need some piece of software, a distributed transaction manager, that is crash safe and keeps track of all distributed transactions. This transaction manager can clean up all prepared transactions after some outage.
I think it would make sense to make your DML actions idempotent - that is to say that if you call them multiple times they have the same overall effect. For example, we can make the INSERT a no-op if the data exists:
INSERT INTO x(id, name)
SELECT nu.id, nu.name
FROM
(SELECT 1 as id, 'a' as name) as nu
LEFT JOIN x ON nu.id = x.id
WHERE
x.id IS NULL
You can run this as many times as you like, and it'll only insert one record
https://www.db-fiddle.com/f/nbHmy3PVDQ3RrGMqLni1su/0
YOu'll need to decide what to do if the record exists in an altered state - eg do you want to leave it alone, or reset it to the incoming values - a question for another time

What does COMMIT do?

please I want to understand the difference between the folowing two statements:
insert into table_name values (,,,,,);
and
insert into table_name values (,,,,,);
commit;
If you insert data without commit you can select data from database and see it. But other users can't.
It's better to look to sql documentation:
Until you commit a transaction:
You can see any changes you have made during the transaction by
querying the modified tables, but other users cannot see the changes.
After you commit the transaction, the changes are visible to other
users' statements that execute after the commit.
You can roll back (undo) any changes made during the transaction with
the ROLLBACK statement (see ROLLBACK.
for example here Oracle Documentation
and some info about transactions
All the DML (insert, update , delete) to be inserted in the database you have to commit them, like approve that you want to add them in the database. If you dont commit DML statment , it will not be enter in the database.
what is commit ?
Docs.oracle cant describe it better
Use the COMMIT statement to end your current transaction and make
permanent all changes performed in the transaction. A transaction is a
sequence of SQL statements that Oracle Database treats as a single
unit. his statement also erases all savepoints in the transaction and
releases transaction locks.

When do I have to commit?

I heard in SQL I do not have to commit every statement. Perhaps create I don't have to.
So can you answer me which Statements I have to commit?
I read, that I have to commit all transactions, but I don't know what this is and can't find it anywhere.
Thanks for your help.
Per the SQL standard, most statements that require a transaction will automatically open one.
Some database engines, such as SQL Server, will (by default) automatically commit the transaction if the statement completes successfully. See Autocommit Transactions.
Autocommit mode is the default transaction management mode of the SQL Server Database Engine. Every Transact-SQL statement is committed or rolled back when it completes
SQL Server also has an Implicit Conversions mode which will leave the transaction open until it's explicitly commited.
When operating in this second such mode (which is the default, I believe, for Oracle), or if you've explicitly created a transaction, it's up to you as a developer when to commit the transaction. It should be when you've accomplished a "complete" set of operations against the database.
If you BEGIN a transaction then you have to either ROLLBACK or COMMIT
Example:
BEGIN TRAN
--Your code
INSERT INTO
NewTable
SELECT *
FROM TABLE
COMMIT TRAN
If you do not use that, it is committed upon execution. So the follow will either fail or be committed:
INSERT INTO
NewTable
SELECT *
FROM Table
If there is an error (like there is no NewTable in the DB) the execution will raise an error and the transaction will roll back. If there is no error the transaction will be committed.

PostgreSQL Locking Questions

I am trying to figure out how to lock an entire table from writing in Postgres however it doesn't seem to be working so I am assuming I am doing something wrong.
Table name is 'users' for example.
LOCK TABLE users IN EXCLUSIVE MODE;
When I check the view pg_locks it doesn't seem to be in there. I've tried other locking modes as well to no avail.
Other transactions are also capable of performing the LOCK function and do not block like I assumed they would.
In the psql tool (8.1) I simply get back LOCK TABLE.
Any help would be wonderful.
There is no LOCK TABLE in the SQL standard, which instead uses SET TRANSACTION to specify concurrency levels on transactions. You should be able to use LOCK in transactions like this one
BEGIN WORK;
LOCK TABLE table_name IN ACCESS EXCLUSIVE MODE;
SELECT * FROM table_name WHERE id=10;
Update table_name SET field1=test WHERE id=10;
COMMIT WORK;
I actually tested this on my db.
Bear in mind that "lock table" only lasts until the end of a transaction. So it is ineffective unless you have already issued a "begin" in psql.
(in 9.0 this gives an error: "LOCK TABLE can only be used in transaction blocks". 8.1 is very old)
The lock is only active until the end of the current transaction and released when the transaction is committed (or rolled back).
Therefore, you have to embed the statement into a BEGIN and COMMIT/ROLLBACK block. After executing:
BEGIN;
LOCK TABLE users IN EXCLUSIVE MODE;
you could run the following query to see which locks are active on the users table at the moment:
SELECT * FROM pg_locks pl LEFT JOIN pg_stat_activity psa ON pl.pid = psa.pid WHERE relation = 'users'::regclass::oid;
The query should show the exclusive lock on the users table. After you perform a COMMIT and you re-run the above-mentioned query, the lock should not longer be present.
In addition, you could use a lock tracing tool like https://github.com/jnidzwetzki/pg-lock-tracer/ to get real-time insights into the locking activity of the PostgreSQL server. Using such lock tracing tools, you can see which locks are taken and released in real-time.

Why do I get an open transaction when just selecting from a database View?

If I execute a simple select statement in pl/sql developer against a database table, I get a standard set of results back as I would expect.
Recently, I pasted a query from a stored procedure that happened to select from a view, and noticed that a transaction was seemingly left open. This was appraent by the rollback and commit options were available in PL/SQL developer.
A poll of other developers revealed that this seems to affect some but not others, which lead me to suspect PL/SQL Developer settings.
Why on earth would this be the case? The view itelf has a DBLink to another database, but I wouldn't expect this to have any effect.
Any thoughts?
Contrary to your expectation, it looks like the database link is the source of the open transaction. I've noticed behaviour like this before when running SELECT queries on remote tables in PL/SQL Developer.
To quote Tom Kyte (source):
distributed stuff starts a transaction "just in case".
EDIT: 'Any SQL statement starts a transaction in Oracle'? No, it does not, and here's a demonstration of it. This demonstration uses the data dictionary view V$TRANSACTION, which lists the active transactions. This is all running on my local Oracle XE database, which has no users other than me connected to it.
We'll use the following table during this demonstration. It contains only a single column:
SQL> desc test;
Name Null? Type
----------------------------------------- -------- ----------------------------
A NUMBER(38)
SQL> select count(*) from v$transaction;
COUNT(1)
----------
0
No active transactions at the moment. Let's run a SQL query against this table:
SQL> select * from test;
A
----------
2
SQL> select count(*) from v$transaction;
COUNT(1)
----------
0
Still no active transactions. Now let's do something that will start a transaction:
SQL> insert into test values (1);
1 row created.
SQL> select count(*) from v$transaction;
COUNT(1)
----------
1
As expected, we now have an active transaction.
SQL> commit;
Commit complete.
SQL> select count(*) from v$transaction;
COUNT(1)
----------
0
After committing the transaction, it's no longer active.
Now, let's create a database link. I'm using Oracle XE, and the following creates a database link from my Oracle XE instance back to itself:
SQL> create database link loopback_xe connect to user identified by password using 'XE';
Database link created.
Now let's see what happens when we select from the table over the database link:
SQL> select count(*) from v$transaction;
COUNT(1)
----------
0
SQL> select * from test#loopback_xe;
A
----------
2
1
SQL> select count(*) from v$transaction;
COUNT(1)
----------
1
As you can see, simply selecting from a remote table opens a transaction.
I'm not sure exactly what there is to commit or rollback here, but I have to admit to not knowing the ins and outs of distributed transactions, within which the answer probably lies.
Any SQL Statement starts a transaction in Oracle.
From the manual:
A transaction begins with the first executable SQL statement. A transaction ends when it is committed or rolled back, either explicitly with a COMMIT or ROLLBACK statement or implicitly when a DDL statement is issued. [...] An executable SQL statement is a SQL statement that generates calls to an instance, including DML and DDL statements
Most probably those who are not seing this are running in auto-commit mode where the transaction started by a statement is immediately committed after the statement has finished.
Others have claimed that a SELECT is not DML, but again the manual clearly states:
Data manipulation language (DML) statements query or manipulate data in existing schema objects. They enable you to:
* Retrieve or fetch data from one or more tables or views (SELECT)
* Add new rows of data into a table or view (INSERT)
[...]
You absolutely cannot open a transaction strictly with a normal query. You may open one across a database link. The guy who posted a link to the doctors either deliberately or utterly carelessly left out the 2nd sentence.
"A transaction in Oracle Database begins when the first executable SQL
statement is encountered. An executable SQL statement is a SQL
statement that generates calls to an instance, including DML and DDL
statements."
SELECT is neither a DML nor a DDL. It is also TRIVIAL to actually test this. I don't want to come off like a troll here, but its really annoying when people just throw out answers on a forum to try to get points and the answers are complete garbage.
Read the rest of the doc and TEST IT FIRST.
login to a session
run a select
see if you have an open transaction by joining v$Session (for your session) to v$transaction.
If a record comes back, you have a transaction. If not, you don't.
Note, according to the Oracle 11g Admin Guide, if you do a plain old SELECT across a database link you will start a transaction, which needs to be committed (or rolled back).
Select is a part of DML only but lock is not acquired, A row lock is fetched upon insert/update/delete/ select for update. -Ross is right.
https://docs.oracle.com/cd/E11882_01/server.112/e41084/ap_locks001.htm#SQLRF55502
ROW Lock Table lock
SELECT ... FROM table... ----- None
INSERT INTO table ... Yes SX
Same with update delete and select for update.
TLDR : On select from remote database you also create session and connection for remote DB. That session and connection persists as long as local user session. As you can guess this can lead to some problems with keeping up with session and connections.
SO ALWAYS DO A COMMIT :
SELECT * FROM emp#sales;
COMMIT;
I like a long read section :
This was bugging me also so much why there is an transaction on selects from db_links
and decided to finally end this so from oracle documentation :
Oracle® Database Administrator's Guide
11g Release 2 (11.2)
https://docs.oracle.com/html/E25494_01/ds_appdev002.htm
Controlling Connections Established by Database Links
When a global object name is referenced in a SQL statement or remote procedure call, database links establish a connection to a session in the remote database on behalf of the local user. The remote connection and session are only created if the connection has not already been established previously for the local user session.
The connections and sessions established to remote databases persist for the duration of the local user's session, unless the application or user explicitly terminates them. Note that when you issue a SELECT statement across a database link, a transaction lock is placed on the undo segments. To rerelease the segment, you must issue a COMMIT or ROLLBACK statement.
Terminating remote connections established using database links is useful for disconnecting high cost connections that are no longer required by the application. You can terminate a remote connection and session using the ALTER SESSION statement with the CLOSE DATABASE LINK clause. For example, assume you issue the following transactions:
SELECT * FROM emp#sales;
COMMIT;
The following statement terminates the session in the remote database pointed to by the sales database link:
ALTER SESSION CLOSE DATABASE LINK sales;
To close a database link connection in your user session, you must have the ALTER SESSION system privilege.
Note:
Before closing a database link, first close all cursors that use the link and then end your current transaction if it uses the link.
See Also:
Oracle Database SQL Language Reference for more information about the ALTER SESSION statement