I've noticed that MS SQL may begin another transaction just before a previous transaction is complete (or committed). Is there a way how we can ensure a transaction must complete first before the next transaction begins?
My problem is that I want to perform an SQL SELECT almost immediately after an SQL INSERT. What I'm seeing right now is; when the SELECT statement is run; it does not return the (very) recently inserted data.
As I traced this scenario using SQL profiler, I've noticed that the SQL INSERT and SELECT performs simultaneously, as in the SELECT occurs before the INSERT is completed.
Is there a way to fix this problem of mine? thanks!
From the sounds of it, you're looking for the OUTPUT clause
From the examples in the documentation http://msdn.microsoft.com/en-us/library/ms177564.aspx
DECLARE #MyTableVar table( NewScrapReasonID smallint,
Name varchar(50),
ModifiedDate datetime);
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO #MyTableVar
VALUES (N'Operator error', GETDATE());
You can run your transactions in SERIALIZABLE isolation level. In this way you will ensure that the select will be performed after the insert. In lower isolation levels, the select is performed in paralell and returns the snapshot of the data - the way it is seen with all transactions completed before the one that issues select has been started.
I'm guessing you want to get an auto-generated identifier back after the insert? I'm not sure the MSSQL way to do this, but in PostgreSQL, there is INSERT ... RETURNING extension to solve exactly this problem.
http://www.postgresql.org/docs/9.1/static/sql-insert.html
Are you locked into MSSQL?
Related
This is quite a basic and somewhat strange question, I guess. Suppose I have a stored procedure that contains an INSERT (or MERGE) statement, followed by a SELECT statement.
Can I always assume that the INSERT statement has finished writing/committing data when I run SELECT? Is it to be expected that the SELECT statement (sometimes) doesn't select all recently inserted rows? If so, what options do I have to make the SELECT statement wait for the INSERT statement to have finished (in a stored procedure) or include possibly uncommitted data?
If it is in the same session it will see it , whether committed or not, unless it has been rolled back.
Once committed other sessions can see it.
If 'select' is from different session and you want read uncommited data
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
I believe I am using HOLDLOCK incorrectly.
The reason I think this is because I have a table that acts like a queue. The queue receives its items from the SQL below and gets processed, one by one in a console application. I haven't tested yet but I believe when this console application starts processing this table, during some of the long selects the code below fails. Why do I think that...because I am logging the GameID when grabbing everything from the table queue and processing them one by one in that console application. The funny thing is the games that I believe didn't make it through didn't make it in the log, therefore I dont believe they are being inserted in my queue table and I believe it's because of the HOLDLOCK below.
Thoughts?
MERGE Test WITH (HOLDLOCK) AS GL
USING (SELECT #GameId AS ID) AS NewTest ON GL.ID = NewTest.ID
WHEN NOT MATCHED THEN
INSERT
(
Id,
FailedAttempts,
DateCreated
)
VALUES
(
NewTest.ID,
0,
SYSDATETIME()
);
I suspect your issue is unrelated to your use of MERGE or HOLDLOCK. I see no reason to introduce cumbersome MERGE syntax here, since it provides no benefit, and especially given the potential issues it can cause in other areas. I suggest a very simple INSERT ... WHERE NOT EXISTS:
INSERT dbo.Test(Id, FailedAttempts, DateCreated)
SELECT #GameId, 0, SYSDATETIME()
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.Test WITH (HOLDLOCK)
WHERE Id = #GameId
);
I'd prefer this over just blindly trying to insert and getting a PK violation for the reasons outlined here and here - in almost all cases, forcing SQL Server to try and get an exception instead of checking yourself first will yield worse performance.
So let me first admit that I am a SQL Server newbie.
Here's the deal: I'm trying to create a trigger on a table in SQL Server 2012, and whenever I try any kind of SELECT statement in the trigger, the table quits working (as in NOTHING can be inserted until the trigger is deleted). As soon as I drop the trigger, everything starts working again. If I don't do any SELECTs, everything is peachy. Is there a permission or something somewhere that I'm missing?
Example:
CREATE TRIGGER sometrigger
ON sometable
FOR INSERT
AS
BEGIN
SELECT * FROM inserted
END
GO
Command completes successfully, but the table becomes frozen as described above.
CREATE TRIGGER sometrigger
ON sometable
FOR INSERT
AS
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#recipients = N'someaddress#somedomain.com',
#subject = 'test',
#body = 'test body',
#profile_name = 'someprofile'
END
GO
Works like a charm.
You're may be falling foul of the disallow results from triggers option being set to 1, as it should be.
Note the warning on that page:
This feature will be removed in the next version of Microsoft SQL Server. Do not use this feature in new development work, and modify applications that currently use this feature as soon as possible. We recommend that you set this value to 1.
I suspect that wherever you're running your inserts from is hiding an error message or exception, since you should get:
Msg 524, Level 16, State 1, Procedure , Line
"A trigger returned a resultset and the server option 'disallow_results_from_triggers' is true."
Or, in the alternative, you're working with a database layer that wraps all inserts in a transaction and will roll the transaction back if anything unexpected happens - such as receiving a result set or even just an extra information message saying (x rows affected).
But all of this is dancing around the main issue - you shouldn't be issuing a select that attempts to return results from inside of a trigger. I might have been able to offer more help if you'd actually told us what you're trying to achieve.
If it's the second case, and it's something tripping over the (x rows affected) messages, that can be cured by placing SET NOCOUNT ON at the top of the trigger.
You should never return data from a trigger anyway, mainly for simplicity and maintenance reasons. It's confusing: I did an INSERT but get a resultset back.
If you need to get the values you just inserted, you'd use the OUTPUT clause
INSERT sometable (...)
OUTPUT INSERTED.*
VALUES (...);
This at least tells you that the INSERT gives results.
And it is nestable too as per, say, SQL Server concurrent transaction issue
If I have the following:
Begin transaction
numberOfRecords = select count from table where foreignKey = "some value"
Insert into table (set SequenceNumber = numberOfRecords + 1)
End Transaction
and multiple users are executing the above code, would each insert have a unique increasing number?
In other words, does the begin transaction queue up other transactions even reads so that each insert will have the correct sequence number? or do I require Insert into..Select statement to achieve what I want?
Thanks.
No, a transaction with the default SQL Server isolation level (READ COMMITTED) is not sufficient. Putting it into one INSERT...SELECT statement won't fix it either. You basically have two options to fix this:
Option 1: Set your isolation level to SERIALIZABLE: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE. This will ensure that transactions from two different users occur as if they occurred in sequence. This, however, might create deadlocks if many such transactions occur in parallel.
Option 2: Exclusively lock the table at the beginning of the transaction (SELECT * FROM table WITH (TABLOCKX, HOLDLOCK) WHERE 1=0). Note that this might impact performance if table is used frequently.
Both approaches have been analyzed in detail in my answer to the following SO question:
In tsql is an Insert with a Select statement safe in terms of concurrency?
No, transactions does not queue up commands, it is not like a lock.
Usually you want to use an identity column, but in some cases when you want to generate SequenceNumber without gaps you need to use the above code with a unique constraint on the SequenceNumber column and be prepared to retry if the commit transaction throws an exception.
I once used a SQL DB as a logger for a massive data export, to get a sequential "identity" I created a "on insert" trigger that dealt with issuing the next number.
Worked well for me, however it was only a single user DB so not sure if there's any issues with multiple users and what I did.
Now that I've re-read the question, this may not be what your looking for but I think you could also do a trigger for a select?
USE [ExportLog]
GO
/****** Object: Trigger [dbo].[Migration_Update_Event_Date] Script Date: 02/10/2011 17:06:11 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[Migration_Update_LogState]
ON [dbo].[MigrationLog] FOR INSERT NOT FOR REPLICATION
AS
UPDATE [MIGRATION-DATA].dbo.MIGRATIONSTATE
SET LASTPROCESSID = ID
WHERE MACHINENAME IN (SELECT MACHINENAME FROM INSERTED)
GO
if you want to insert the record in unique ,then first you create on sequence then record should be insert thorugh sequance .like
create sequnce seq_num ;
now use seq_num to insert the rcords .
insert into <table name>(col1) values(seq_num.nextval);
You need to set the transaction isolation level to a level that provides the right isolation level. In your code, two processes could execute the first line, then the second line. Both will insert the same value.
Set the isolation level to serializable and perform the select statement WITH (UPDLOCK). This will reduce concurrency in your system but it will be safe.
Other strategies are possible, but they are more time-consuming to implement (and test!).
I have a random question. If I were to do a sql select and while the sql server was querying my request someone else does a insert statement... could that data that was inputted in that insert statement also be retrieved from my select statement?
Queries are queued, so if the SELECT occurs before the INSERT there's no possibility of seeing the newly inserted data.
Using default isolation levels, SELECT is generally given higher privilege over others but still only reads COMMITTED data. So if the INSERT data has not been committed by the time the SELECT occurs--again, you wouldn't see the newly inserted data. If the INSERT has been committed, the subsequent SELECT will include the newly inserted data.
If the isolation level allowed reading UNCOMMITTED (AKA dirty) data, then yes--a SELECT occurring after the INSERT but before the INSERT data was committed would return that data. This is not recommended practice, because UNCOMMITTED data could be subject to a ROLLBACK.
If the SELECT statement is executed before the INSERT statement, the selected data will certainly not include the new inserted data.
What happens in MySQL with MyISAM, the default engine, is that all INSERT statements require a table lock; as a result, once an INSERT statement is executed, it first waits for all existing SELECTs to complete before locking the table, performs the INSERT, and then unlocks it.
For more information, see: Internal Locking Methods in the MySQL manual
No, a SELECT that is already executing that the moment of the INSERT will never gather new records that did not exist when the SELECT statement started executing.
Also if you use the transactional storage engine InnoDB, you can be assured that your SELECT will not include rows that are currently being inserted. That's the purpose of transaction isolation, or the "I" in ACID.
For more details see http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html because there are some nuances about read-committed and read-uncommitted transaction isolation modes.
I don't know particulars for MySQL, but in SQL Server it would depend on if there were any locking hints used, and the default behavior for locks. You have a couple of options:
Your SELECT locks the table, which means the INSERT won't process until your select is finished.
Your SELECT is able to do a "dirty read" which means the transaction doesn't care if you get slightly out-of-date data, and you miss the INSERT
Your SELECT is able to do a "dirty read" but the INSERT happens before the SELECT hits that row, and you get the result that was added.
The only way you do that is with a "dirty read".
Take a look at MYSql's documentation on TRANSACTION ISOLATION LEVELS to get a better understanding of what that is.