Difference in inserting values into SQL Server - sql

I am using SQL Server 2012
The query is:
drop table x
create table x(id int primary key)
insert into x values(5)
insert into x values(6)
begin tran
insert into x values(1),(2),(3),(3),(4)--Primary key violation
commit tran
select* from x
This returns
5
6
and another query
drop table x
create table x(id int primary key)
insert into x values(5)
insert into x values(6)
begin tran
insert into x values(1)
insert into x values(2)
insert into x values(3)
insert into x values(3) --Primary key violation
insert into x values (4)
commit tran
select * from x
This returns
1
2
3
4
5
6
So what is the difference in inserting values in SQL Server?
Between those 2 queries and why the different result sets?

Sample 1 has a single insert statement for the 1,2,3,3,4,5. This is a "bulk" insert statement (however SQL Server uses the term bulk insert in a different fashion). Essentially it means all the inserts in this line are executed as 1 single action.
Sample 2 has separate insert statements. Since there is no exception handling in place, there is no reason for the transaction to abort. The error is ignored, the other records are added, and the result is then what you see.

SQL server executes the queries as batches. So if any error occurs in the batch, according to MSDN, one the following is possible.
No statements in the batch are executed.
No statements in the batch are executed and the transaction is rolled back.
All of the statements before the error statement are executed.
All of the statements except the error statement are executed.
In your first case, "No statements in the batch are executed". And in your second case, "All of the statements except the error statement or executed".
For more about SQL batches, please refer the following MSDN articles,
Batches of SQL Statements
Executing Batches
Errors and Batches

Related

Deadlock on inserting a new row

I have a table that stores the keys of other tables for a many-to-many relationship. Example:
create table for_example(a uuid, b uuid, primary key(a, b));
Multiple transactions can insert data into a table at the same time. In this case, it may turn out that two transactions are inserting the same data. In this case, one of these transactions will see that the record already exists and will wait for the parallel transaction to complete its work in order to continue its work. But it may turn out that before the lock occurred, the second transaction had already inserted any data into this table, and in this case, if the first transaction tries to insert new data that the second transaction has already inserted, a deadlock will occur. Example:
begin;
--check exists row in table before insert in condition "IF"
select 1 from for_example where a = '002e202d-c564-45cd-a0a7-d8fe8b930d99' and b = '4ab9d0ed-80c7-42ee-a641-59834a61f06d';
--if not exists then insert
insert into for_example(a, b)
values('002e202d-c564-45cd-a0a7-d8fe8b930d99', '4ab9d0ed-80c7-42ee-a641-59834a61f06d')
ON CONFLICT DO NOTHING;
we pass at this time to the second transaction
begin;
--check exists row in table before insert in condition "IF"
select 1 from for_example where a = '4ab9d0ed-80c7-42ee-a641-59834a61f06d' and b = '002e202d-c564-45cd-a0a7-d8fe8b930d99';
--if not exists then insert
insert into for_example(a, b)
values('4ab9d0ed-80c7-42ee-a641-59834a61f06d', '002e202d-c564-45cd-a0a7-d8fe8b930d99')
ON CONFLICT DO NOTHING;
we return to the first transaction and try to insert the data that the second transaction has already inserted
--check exists row in table before insert in condition "IF"
select 1 from for_example where a = '4ab9d0ed-80c7-42ee-a641-59834a61f06d' and b = '002e202d-c564-45cd-a0a7-d8fe8b930d99';
--if not exists then insert
insert into for_example(a, b)
values('4ab9d0ed-80c7-42ee-a641-59834a61f06d', '002e202d-c564-45cd-a0a7-d8fe8b930d99')
ON CONFLICT DO NOTHING;
blocking occurs, we are waiting for the second transaction to finish its work, but at this time the second transaction is trying to insert data that the first transaction has already inserted
--check exists row in table before insert in condition "IF"
select 1 from for_example where a = '002e202d-c564-45cd-a0a7-d8fe8b930d99' and b = '4ab9d0ed-80c7-42ee-a641-59834a61f06d';
--if not exists then insert
insert into for_example(a, b)
values('002e202d-c564-45cd-a0a7-d8fe8b930d99', '4ab9d0ed-80c7-42ee-a641-59834a61f06d')
ON CONFLICT DO NOTHING;
error occurs: [40P01] ERROR: deadlock detected.
Is it possible to somehow check for the existence of a row in a table with the condition that it may already have been inserted by a parallel transaction?
This is a real issue we're having on a production server right now.

Insert batch statement difference between two queries

Is there any difference between these two queries:
create table tab(value int, ts timestamp) timestamp(ts)
-- q1:
insert batch 2 into tab values(1, systimestamp()),(2, systimestamp())
-- q2:
insert batch 2 into tab select cast(x as int),systimestamp() from long_sequence(2)
Should they be equivalent?
In q1 batch 2 has no effect.
In q2 batch 2 will delay commit until two insert statements have been seen.

Why is the ##ERROR always 0 and ##ROWCOUNT always 1?

I create a table like:
then I execute this statement:
update test
set tname = 'Joker'
where tid % 2 = 0
it shows:
which means 'there are two rows affected' in Chinese.
But if execute print ##rowcount immediately, the result is:
What else, if execute insert into test values('Paul','foo'), the result is:
which means:
Message 8101, Level 16, Status 1, Line 21.
Only when column list is used and IDENTITY_INSERT is ON, you can set values in the identity column of 'test' table explicitly'.
But if then execute print ##ERROR, it shows:
which I think should be 8101.
Could someone tell me why? Thanks
First issue is not replicable, I have prepared demo script
create table test(tid int identity(1,1), tname varchar(100))
insert into test values ('James'),('Jake'),('Tom'),('Mary')
update test
set tname = 'Joker'
where tid % 2 = 0
select ##ROWCOUNT --returns 2
Second issue is because you are running the select #error statement separately. That's why you are getting 0. If you run the last two statements together you will get 545 as result
set identity_insert test on
insert into test values ('James'),('Jake'),('Tom'),('Mary')
select ##ERROR --returns 545
For the second question (it would have been a good idea to post these as 2 separate questions), your SQL statement is trying to insert 'Paul' into the tid column. You need to separate the values like this:
INSERT INTO test VALUES ('Paul'), ('foo')
Better yet, be explicit with the column names:
INSERT INTO test (tname) VALUES ('Paul'), ('foo')

is insert into.. select statement transactional?

do i need to use transaction to provide all or not proposition for the following insert process?
INSERT INTO table1 ( column1 , column2)
SELECT col1, col2
FROM table2
expecting average row-count from table2 is around 150 and target database is ms sql server 2008 r2.
No, you don't need to. A single SQL statement is already in a transaction by default so there is no way that you will partually insert results or that results will be meanwhile moderated by another transaction. The fact that 2 tables are involved doesn't change the fact that a single SQL statement is used.
As your simple insert will not needed.
By default sqlserver manage this thing and at the end commit whatever you did.
If you explicitly want when multiple statement executed of insert/update or when you want to parent/child inserted in single unit of work, then you use transaction as
tran
declare #parentId int =0;
insert statement ---parent
set #parentId= ##identity
insert statement --child entry
values ( #parentId,...)
If ##ERROR > 0 then
ROLLBACK
else
COMMIT
http://www.codeproject.com/Articles/4451/SQL-Server-Transactions-and-Error-Handling
or you can use try catch block as c# in sqlserver side too.
http://msdn.microsoft.com/en-IN/library/ms175976.aspx

Simulate a deadlock using stored procedure

Does anyone know how to simulate a deadlock using a stored procedure inserting or updating values? I could only do so in sybase using individual commands.
Thanks,
Ver
Create two stored procedures.
The first should start a transaction, modify table 1 (and take a long time) and then modify table 2.
The second should start a transaction, modify table 2 (and take a long time) and then modify table 1.
Ideally, the modifications should affect the same rows, or create table locks.
Then, in a client application, start SP1, and immediately then also start SP2 (before SP1 has finished).
The simple and short answer to get a deadlock will be to access the tables data in a reverse order and hence introducing a cyclic deadlock between two connections. Let me show you code:
Create table vin_deadlock (id int, Name Varchar(30))
GO
Insert into vin_deadlock values (1, 'Vinod')
Insert into vin_deadlock values (2, 'Kumar')
Insert into vin_deadlock values (3, 'Saravana')
Insert into vin_deadlock values (4, 'Srinivas')
Insert into vin_deadlock values (5, 'Sampath')
Insert into vin_deadlock values (6, 'Manoj')
GO
Now with the tables ready. Just update the columns in the reverse order from two connections like:
-- Connection 1
Begin Tran
Update vin_deadlock
SET Name = 'Manoj'
Where id = 6
WAITFOR DELAY '00:00:10'
Update vin_deadlock
SET Name = 'Vinod'
Where id = 1
and from connection 2
-- Connection 2
Begin Tran
Update vin_deadlock
SET Name = 'Vinod'
Where id = 1
WAITFOR DELAY '00:00:10'
Update vin_deadlock
SET Name = 'Manoj'
Where id = 6
And this will result in a deadlock. You can see the deadlock graph from profiler.
Start a process which continously insert or update a table using while loop with script and run your desired sp.