tags:

views:

46

answers:

2

I want to empty a temporary table, load it via bcp, and then modify its rows before moving to a production table. The commands would look something like this:

  1. truncate table temp1
  2. bcp db.schema1.temp1 in import1.txt -h "TABLOCK"
  3. exec sp_modify_temp1_rows_move_to_production_table

My question is how can I execute all this as one transaction, not necessarily in the interest of rolling it back, but in order to provide isolation.

Here is the imagined scenario. User1 goes to load import1.txt (the implementation details of how this done, steps 1-3, are hidden to the user). Before step 2 finishes for User1, User2 initiates their own import. Locking mechanisms prevent their import from starting right away. The primary concern is that User2 step 1 will begin as soon as User2's step 2 is done thus clearing the table before User1's step 3 and the process can be completed.

Three additional notes:

  1. Client side execution here via ADO (not to be mistaken with ADO.NET)
  2. BULK INSERT is NOT an option
  3. Prefer doing this via extending some lock mechanism via ADO that can include step 2 in with steps 1 and 2 of the process - if possible - knowing that it is impossible helps here also

Edit: marc_s - please don't change my post with a personal opinion of how to name the product. Do a search on Wikipedia or Microsoft's Bing or here at Stack Overflow for MSSQL. I already tagged the post as sql-server. MSSQL is another search parameter a person may want to use. Its not my fault that this is a common acronym. If you have some link to provide as to why its particularly harmful to use this alias then please provide. Otherwise it looks like you are going out of your way to force your views on others. Thanks.

+1  A: 

1) first solution

  • add SessionId field to temp1 table
  • add load_session_state table PK sessionId, State tinyint (Process step 1, process step 2... Done...)
  • sp_modify_temp1_rows_move_to_production_table will execute ONLY records with some sessionId and State = Done. (sp_modify_temp1_rows_move_to_production_table can be executed asynchronously by using a sql agent job)
  • truncate should be replaced with DELETE WHERE SessionId = (it is slow, but it has to be done). (it can be executed by the same sql agent job but after moving records to production)
  • TABLOCK should be removed from bcp command

2) second solution

  • CREATE TABLE temp_XXX
  • bcp db.schema1.temp_XXX in import1.txt -h "TABLOCK"
  • exec sp_modify_tempXXX_rows_move_to_production_table @table_name = 'temp_XXX' (use dynamic sql with the @table_name parameter)
  • DROP TABLE temp_XXX

ADDED

3) use the table load_session_state as a progress view and for lock your process. if there are any records in it, so notify user for waiting. use your previous solution without changes. just use table load_session_state as logical lock

igor
I have considered both 1 and 2 but more along the lines of using network login instead of session id and login in place of XXX. I was hoping (high hopes), however, there was some way to extend the table locking to User1 via some sp_ that I could somehow wrap the bcp with from the client. Thanks for yours answer. I'll wait and see and go with #2 as the answer if such a thing is impossible.
sp_modify_temp1_rows_move_to_production_table can be executed asynchronously by using a sql agent job.
igor
Your last comment is going over my head. I'm not sure what this gains for me? I can also execute the sp async from ADO too. How do I avoid the isolation problem by running it as a job? Though for some odd reason thinking about this popped an idea into my head that I could run the sp in an INSTEAD OF, INSERT trigger. Load data to temp1 via BCP, INSTEAD OF does actual insert from inserted, then EXEC sql? Shouldn't that maintain isolation? Not sure the of performance hit? So hard to test these scenarios given my environment and getting timing just perfect.
use the table load_session_state as a progress view and for lock your process. if there are any records in it, so notify user for waiting. use your previous solution without changes. just use table load_session_state as logical lock.
igor
Your #2 still sticks out strongly and I'm weighing options. I think my trigger option is a performance concern so I'm tossing that too. It just seems cleaner than having to create a table to manage locks. While the locking table would be easy to create, its just this extra table out there I would rather not have. Plus I have to write the EXISTS and INSERTS and DELETES to add to existing code. So its messier all around than #2 where I'm basically getting the CREATE TABLE for the existing temp table and then embedding it in place of the TRUNCATE.
+3  A: 

The typical solution is to use applocks acquired via

sp_getapplock 'importing-ABC', 'Exclusive', 'Session';

This way you locks a logical resource (an arbitrary string/name) and, by convention, other users attempting to do the same must attempt to obtain the same applock. While the applock is held, the bcp command can run w/o interference. Note that this does not prevent another user/application that does not respect the protocol (because of ignorance, malevolence or stupidity).

Remus Rusanu
This seems like an interesting option. I can call sp_getapplock from the GUI via ADO for User1 and its the call of it again (from User2) before being released by User1 that keeps the code from running. I noticed the key word 'Session'. I'll have to check, but I'm running BCP by making a call to a shell. So BCP should definitely be in a different session than the ADO Connection that is calling TRUNCATE and the sp_modify_temp1_*. Do you know if this is a factor off the top of your head?
@Remus Rusanu. +1 It is a good solution.
igor
Igor, agree as replacement for options #1 and #3 instead of having table. I will have to test to see if this works as intended. Marking this "the Answer" though all the options work. This is just a "I learned something new today" and I see it being a useful thing for other projects even though I might still go with #2. Thanks.
The 'Session' scope for the applock is the appropriate scope for long operations, like a BCP. 'Transaction' scope is for short operations.
Remus Rusanu