I have a sequence used in a stored proc that update multiple table just like below:
create procedure()
-- retrieve new sequence number
sequence.nextval();
-- update table_A using newly created sequence number
insert into table_A(theID) values(sequence.currval());
-- update table_B using newly created sequence number
insert into table_B(theID) values(sequence.currval());
end procedure;
May I know whether the code above is a thread-safe implementation? For each procedure's execution, can I guarantee theID in table_A and table_B always retrieving the same sequence number when there are more than one execution at a time?
One of Informix's primary objectives is to make sure that procedure works exactly as you need it to, regardless of how many thousands of users are also running the same procedure at the same time. Indeed, you can have multiple concurrent sessions of your own each running the procedure and each session is isolated from all the other sessions.
So, the code shown is 'thread safe'.
Related
I have a stored procedure which starts by creating a temp table, then it populates the temp table based on a query.
It then does a Merge statement, basically updating a physical table off of the temp table. Lastly it does an Update on the physical table. Pretty basic stuff.
It takes ~8 sec to run. My question is, at what point does it lock the physical table? Since the whole query is compiled prior to its run, is the physical table locked during the entire execution of the stored procedure, or does it wait until it gets to the statements that actually works with the physical table?
I'm not necessarily trying to resolve an issue, as much as make sure I don't cause one. We have other processes that need to be reworked to alleviate blocking, I don't want to create another.
OK, for SQL Server:
a stored procedure is "compiled" (e.g. an execution plan is determined) before it's first usage (and that execution plan is cached in the plan cache until it's either tossed out due to space constraints, or due to a restart). At the time an execution plan is determined, nothing happens on the table level - no locks, nothing
SQL Server will by default use row-level locks, e.g. a row is locked when it's read, inserted, updated or deleted - then and only then. So your procedure will place shared locks on the tables where it selects the data from to insert those rows into the temp table, it will put exclusive locks on the rows being inserted into the temp table (for the duration of the operation / transaction), and the MERGE will also place locks as and when needed
Most locks (not the shared locks, typically) in SQL Server are by default held until the end of the transaction. Unless you specifically handle transactions explicitly, each operation (MERGE, UPDATE, INSERT) runs inside its own implicit transaction, so any locks held will be released at the end of that transaction (e.g. that statement).
There are lots more aspects to locks - one could write entire books to cover all the details - but does this help you understand locking in SQL Server at least a little bit ?
I have written a stored procedure that includes a SELECT on a number of tables that applies logic to calculate values and transforms some of the data.
I have been asked if I can exclude records from the resultset in the stored procedure and write the record to a separate log table. I was looking to loop through the result set from the SELECT statement and delete the record I want to exclude once I have written it to a table. At the moment I am struggling to find the syntax to delete from the result set of a SELECT statement in a stored procedure and can only find how to use the cursor reference to delete from the original database table.
I need to remove the records in the same stored procedure and I am looking to avoid duplicating the logic by using some of the logic to find the records to include and repeat some of the logic again to be able to find the records to exclude. The only other alternative I can think of is using a temporary table, but I think what I am trying to do should be possible.
Any help appreciated.
When you have an open cursor in a stored procedure (or in an application), you can perform positioned deletes. You can execute the statement,
DELETE WHERE CURRENT OF cursorname;
Please be aware that by default issuing a COMMIT statement will close any open cursors, so if you plan to have this delete operation spread over multiple transactions you will need to declare your cursors using WITH HOLD.
I am just now starting to dig into Teradata's locking features and Google is fairly convoluted with explanations on this. Hopefully, I can get a very simple and streamlined answer from SE.
After encountering numerous issues with identity columns in Teradata, I've decided to create a mechanism that mimics Oracle's sequence. To do this, I am creating a table with two fields, one that holds a table name and the other that stores its last-used sequence. I am going to then create a stored procedure that takes a table name. Within the procedure, it will perform the following options:
Select the last-used sequence from the sequence table into a variable (select LastId from mydb.sequence where tablename = :tablename)
Add 1 to the variable's value, thus incrementing it
Update the sequence table to use the incremented value
Return the sequence variable to the procedure's OUT param so I can access the sequenced ID in my .NET app
While all of those operations are taking place, I need to lock the sequence table for all read and write access to ensure that other calls to the procedure do not attempt to sequence the table while it is currently in the process of being sequenced. This is obviously to keep the same sequence from being used twice for the same table.
If this were .NET, I'd use a Sync Lock (VB.NET) or lock (C#) to keep other threads from entering a block of code until the current thread was finished. I am wondering if there's a way to lock a table much in the same way that I would lock a thread in .NET.
Consider using an explicit locking mechanism for a rowhash lock for the transaction:
BEGIN TRANSACTION;
LOCKING ROW EXCLUSIVE
SELECT LastId + 1 INTO :LastID
FROM MyDB.SequenceCols
WHERE TableName = :TableName
AND DatabaseName = :DatabaseName;
UPDATE MyDB.SequenceCols
SET LastId = :LastID
WHERE TableName = :TableName
AND DatabaseName = :DatabaseName;
END TRANSACTION;
The rowhash lock will allow the procedure to be used by other processes against other tables. To ensure row level locking you must fully qualify the primary index of the SequenceCols table. In fact, the primary index of the SequenceCols table should be UNIQUE on DatabaseName and TableName.
EDIT:
The exclusive rowhash lock would prevent another process from reading the row until the END TRANSACTION is processed owner of the rowhash lock.
I am putting together a job on SQL Enterprise Manager 2000 to copy and delete records in a couple database tables. We've run a straight up mass copy and delete stored procedure, but it could be running it on millions of rows, and therefore hangs the server. I was interested in trying to run the service in 100-ish record chunks at a time, so the server doesn't grind to a halt (this is a live web database). I want this service to run once a night, which is why I've put it in an agent job. Is there any way to loop the calls to the stored procedures that actually do the copy and delete, and then "sleep" in between each call to give the server time to catch up? I know there is the WAITFOR command, but I'm unsure if this will hold the processor or let it run other queries in the meantime.
Thanks!
"Chunkifying" your deletes is the preferred way to delete excessive amounts of data without bloating up transaction log files. BradC's post is a reasonable example of this.
Managing such loops is best done within a single stored procedure. To spread such work out over time, I'd still keep it in the procedure. Inserting a WAITFOR in the loop will put a "pause" between each set of deletes, if you deem that necessary to deal with possible concurrency issues. Use a SQL Agent job to determine when the procedure start--and if you need to make sure it stops by a certain time, work that into the loop as well.
My spin on this code would be:
-- NOTE: This is a code sample, I have not tested it
CREATE PROCEDURE ArchiveData
#StopBy DateTime
-- Pass in a cutoff time. If it runs this long, the procedure will stop.
AS
DECLARE #LastBatch int
SET #LastBatch = 1
-- Initialized to make sure the loop runs at least once
WHILE #LastBatch > 0
BEGIN
WAITFOR DELAY '00:00:02'
-- Set this to your desired delay factor
DELETE top 1000 -- Or however many per pass are desired
from SourceTable
-- Be sure to add a where clause if you don't want to delete everything!
SET #LastBatch = ##rowcount
IF getdate() > #StopBy
SET #LastBatch = 0
END
RETURN 0
Hmm. Rereading you post implies that you want to copy the data somewhere first before deleting it. To do that, I'd set up a temp table, and inside the loop first truncate the temp table, then copy in the primary keys of the TOP N items, insert into the "archive" table via a join to the temp table, then delete the source table also via a join to the temp table. (Just a bit more complex than a straight delete, isn't it?)
Don't worry about waiting between loops, SQL server should handle the contention between your maintenance job and the regular activity on the server.
What really causes the problem in these types of situations is that the entire delete process happens all at once, inside a single transaction. This blows up the log for the database, and can cause the kinds of problems it sounds like you are experiencing.
Use a loop like this to delete in manageable chunks:
DECLARE #i INT
SET #i = 1
SET ROWCOUNT 10000
WHILE #i > 0
BEGIN
BEGIN TRAN
DELETE TOP 1000 FROM dbo.SuperBigTable
WHERE RowDate < '2009-01-01'
COMMIT
SELECT #i = ##ROWCOUNT
END
SET ROWCOUNT 0
You can use similar logic for your copy.
WAITFOR will let other processes 'have a go'. I've used this technique to stop large DELETE's locking up the machine. Create a WHILE loop, delete a block of rows, and then WAITFOR a few seconds (or less, whatever is appropriate).
I am attemting to create a storedproc that reads like:
Select
ep.EmployeeID, GetEmployeeFirstName(ep.EmployeeID),
GetEmployeeLastName(ep.EmployeeID), ed.EmployeeDateOfBirth,
ed.EmployeeAddress, ed.EmployeeAddress2, ed.City, ed.State, ed.ZipCode
From
EmployeeProfile ep, EmployeeDetail ed
Where
ep.EmployeeID = ed.EmployeeID
This block of code will be a stored procedure.
My issue is that GetEmployeeFirstName is a stored proc that has to be passed an EmployeeID to get the employees first and last name.
How can I call a storedproc within a stored proc.
Thanks
Mike
These would probably be better suited as a function.
GetEmployeeFirstName(ep.EmployeeID), GetEmployeeLastName(ep.EmployeeID)
Better yet, just join the table that has the names.
I don't understand what these stored procedures do. Even if the first and last name are not in the EmployeeProfile table, and even if you have to do some manipulation of the strings before they are returned, a join would be a much better solution than a stored procedure or function. Especially when you take performance into account.
If you have the GetEmployeexName sprocs because you use them elsewhere, that's fine. Whatever they do, I would not consider it code duplication if they don't get called from your query.
You need to understand that for every row in your result set, two other procedures or functions get called. This is extremly costly and can render an application unacceptably slow, even for relatively small result sets of a few thousand employees. I know what I am talking about - I removed a lot of function calls from queries during a recent database tuning initiative.
In SQL Server, in order to call the GetEmployeeLastName within the Select statement list I would convert it to a database Function.
You can use EXEC or sp_executesql to execute a stored procedure from another stored procedure. (BTW, you have not specified your RDBMS).
Doesn't your table EmployeeDetail contain the employee's first and last name?
Select
ep.EmployeeID, ed.FirstName
ed.LastName, ed.EmployeeDateOfBirth,
ed.EmployeeAddress, ed.EmployeeAddress2,
ed.City, ed.State, ed.ZipCode
From
EmployeeProfile ep
inner join EmployeeDetail ed ON ep.EmployeeID = ed.EmployeeID