SQL Server - Non-distributed transaction over two DBs - sql

I am trying to localize an issue I have
with 2 DBs and their transactional behavior.
I create two DBs on a single SQL Server 2008 R2 instance.
test1
test2
test1 has one table Table_1
test2 has one table Table_2
Both tables have an ID (int) and a value (string) columns.
Both have just one row which has ID=1.
Then in SQL Server Management Studio,
in some window 1 I do
use test1
begin transaction
update test1.dbo.Table_1
set
value = 'TEST-100'
where
ID = 1
update test2.dbo.Table_2
set
value = 'TEST-200'
where
ID = 1
commit transaction --- but I don’t run the commit yet ---
and then in another window 2, I do
select * From test1.dbo.Table_1
with (nolock)
select * From test2.dbo.Table_2
with (nolock)
This way I can see the two uncommitted yet values.
But if in window 2, I do
select * From test1.dbo.Table_1
or
select * From test2.dbo.Table_2
these SELECT staments hang.
So my question is: is that transaction from
window 1, spanning two DBs? Seems so because
select * From test2.dbo.Table_2
hangs too which means to me that test2.dbo.Table_2
is enlisted in the same transaction which I started
in window 1.
But why is this transaction acting like a distributed one?
Is that normal? What is the explanation? Is that some
undocummented feature in SQL Server? Are there any
references which would explain that behavior I am seeing?

When a query spans over 2 databases, then your transaction is promoted to a distributed transaction handled by MSDTC. It's the normal way to be and it happens without explicitly using BEGIN DISTRIBUTED TRANSACTION statement.

Related

Table Sync from DB2 to SQL Server

We have a tables in Db2, That which we need to get that table to MS SQL server (only for read), And I want it to be in sync for every 15 minutes (one way from DB2 to SQL Server). Can you suggest the best approach?
Have a SQL Agent job execute an SSIS package every 15 minutes.
I know that all the time MERGE is the right option to sync the tables in the SQL. But I am not sure, whether we can use it in linked servers also. Anyway, after some research I got this task accomplished by using the merge join. Merge will update, insert, delete what ever required. But it will take a little bit more time to update the table for every 15 min, when the job runs. So, you can create a #Temptable to insert the transactions that were done from the lastjob done.You can use the datetime stamp in that source table to retrieve the transactions that were done from the last job done(15min). If you don't have the date time in source table, you can use the audit table for that source table(if applicable).
(JLT table have 3 columns (last_job_end)( cur_job_start)(some job identity). JLT is the job log table we need to create in linked server to get the last job end and cur job start time, We need to update last job end every time at the end of query in JOB. As well as cur job start in the beginning of the job )
SELECT *
INTO #TEMPtable
FROM OPENQUERY([DB2], 'Select * from source_table
where some_id_column in
(select some_id_column
from audit_table AT, Job_log_table JLT
where datetime > last_job_end
and datetime <= cur_job_start
and c_job = ''some_job_id'')’)`
If you don't have the audit table and you have the datetime in Source.
SELECT *
INTO #TEMPtable
FROM OPENQUERY([DB2], 'Select *
from source_table s, JOB_CYCLE_TABLE pr
where s.DATETIME <= pr.cur_job_start
and s.DATETIME > pr.last_job_end
and pr.c_job = ''some_job_id''')

Atomic update column in SQL Server

I am using MS SQL Server, and I have a table. I want each SQL Server client to acquire one row and update it, so that the client knows that only he acquired that record, and no one else!
record1 a b c 0
record2 d e f 0
...
One client should acquire and update last value from 0 to 1.
The solution should work at least from SQL Server 2005 and above.
ATOMIC
{
select top 1 * from table where column='0' // get one row where column is '0'
update table set column='1'
}
You could do something like:
update top (1) table with (readpast)
set Column1=1
output inserted.*
where Column1 = 0
Which should do it all in one go. You only need the readpast hint if, having acquired the unique value, the connection keeps a transaction open for a long period of time. If you're just doing the update you can omit it.
You can update a table through a Common Table Expression.
Below I create a CTE that picks just one row and then that is used in the UPDATE.
WITH
singleRow AS
(
select top 1 * from table where column='0'
)
UPDATE
singleRow
SET
column= '1'
;
NOTE: Remember to ensure any preceding commands are terminated with a ;, else the WITH syntax doesn't work.

lock a table in sql server 2008 after select

I have a main currency table. Which has two fields, one currency Type and currency value. User can not be changed once a user start working with the DB. I need to lock my Currency table through SQL Server 2008 Query once user select one value.
Can any one help me or suggest me for DB LOCK query.
We had the same problem on a table in our database. Found this and it worked for us:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
SELECT * FROM dbo.MyTable WITH (TABLOCKX);
The table will be locked until a COMMIT TRANSACTION or ROLLBACK TRANSACTION is executed.
Hope it helps somebody in the future...
You can use NOLOCK for your objects.
For example :
SELECT TOP 10 * FROM Orders WITH(NOLOCK) where UserName = 'VadaVici'

SQL Query Execution more than once?

I have 2 tables (A,B), and 1 query
My query is something like this
Read From A
Update B with this data from A
Using the updated table B, set final value of A.
Example execution can be find in below question:
Proper way to keep a single data in sql server?
Now since all the process is connected, this query should not be executed twice at the same time, or by 2 different users until the process ends. How do I prevent this ? Or does it already work securely like this?
Use transaction lock :
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
--select * from A
-- update B ....
--update A
WAITFOR DELAY '00:00:02' -- tables remain locked for 2 secs hh:mm:ss
commit TRANSACTION
during the transaction execution, any try to read or write from/to tables will timeout...
EDIT :
u must use, some lock to lock the db while updating. http://msdn.microsoft.com/en-us/library/ms173763.aspx
psedo code for u:
int x=(select val from tableB)+1
query="update tableB set tableB.field="+x+"where......."
if query executed successfully:
update tableA
I hope that your table A and B must be having some Primary Key eg EmployeeID. In such case a simple solution is to create a table (say Lock_Table) which keeps a record of the EmployeeID beign modified.
So here you would need to go like this:
BEGIN TRANSACTION
1- Read EmployeeID From A
2- Check if EmployeeID already exists in Lock_Table. If Yes then Quit Else insert that EmployeeID in Lock_Table
3- Update B with this data(EmployeeID in this case) from A
4- Using the updated table B, set final value of A.
5- Delete this EmployeeID from the Lock_Table
COMMIT TRANSACTION
On any error ROLLBACK the Transaction.
Hope it helps.

SQL Server 2005 and SELECT and UPDATE locked

I want to perform a update then select the result. I don't want anything to be able to update the row I am updating until after the select has occurred. How would I do this?
My goal is to increment a value of a row and return that incremented value. I have thus far found that I end up with an issue where update (to increment) followed by a select in a situation where two queries happen at near the same time the selects seem to return the same number. So I am guessing that something like update > update > select > select is happening.
I miss labeled this as SQL Server 2005. I am actually working with Server 2000. So the output clause does not work (is not in that version).
BEGIN TRANSACTION
UPDATE Table SET Last=(Last+1) WHERE ID=someid;
SELECT * FROM Table WHERE ID=someid;
COMMIT TRANSACTION
BEGIN TRAN
UPDATE ...
SELECT...
COMMIT
Should do it even at the default transaction isolation level of read committed.
You could also use the OUTPUT clause to get the row directly back after the update. Example of this
UPDATE <YourTable>
SET ...
OUTPUT INSERTED.*
WHERE ...