Transaction for sql query stored in temp table - tsql

I have a stored procedure build update query and store in temp table each time in loop.
Total query built UNKNOWN (2 to 4)
How can I put multi query in a transaction?
For example, temp table contains following rows in column EXPSQL (nvarchar)
id EXPSQL
1 Update tableA SET Name = 'Test' WHERE id=1
2 Update tableB SET Name = 'Test2' WHERE id=10
How can I begin a transaction for a loop to exec above query? or is there any other way?
while #id < total
begin
set #id = #id +1
select #SQL = EXPSQL FROM #TEMPTABLE WHERE id=#id
EXEC (#SQL)
end
Thanks

If you want to execute all as one:
begin transaction
while #id < total
begin
set #id = #id +1
select #SQL = EXPSQL FROM #TEMPTABLE WHERE id=#id
EXEC (#SQL)
-- if a error occurs go back to the original state
if(##error <> 0)
rollback transaction
end
commit transaction
If you want to execute each one individually:
while #id < total
begin
set #id = #id +1
select #SQL = EXPSQL FROM #TEMPTABLE WHERE id=#id
begin transaction
EXEC (#SQL)
-- if a error occurs go back to the original state
if(##error <> 0)
rollback transaction
else
commit transaction
end

Related

Is it worth Parallel/Concurrent INSERT INTO... (SELECT...) to the same Table in Postgres?

I was attempting an INSERT INTO.... ( SELECT... ) (inserting a batch of rows from SELECT... subquery), onto the same table in my database. For the most part it was working, however, I did see a "Deadlock" exception logged every now and then. Does it make sense to do this or is there a way to avoid a deadlock scenario? On a high-level, my queries both resemble this structure:
CREATE OR REPLACE PROCEDURE myConcurrentProc() LANGUAGE plpgsql
AS $procedure$
DECLARE
BEGIN
LOOP
EXIT WHEN row_count = 0
WITH cte AS (SELECT *
FROM TableA tbla
WHERE EXISTS (SELECT 1 FROM TableB tblb WHERE tblb.id = tbla.id)
INSERT INTO concurrent_table (SELECT id FROM cte);
COMMIT;
UPDATE log_tbl
SET status = 'FINISHED',
WHERE job_name = 'tblA_and_B_job';
END LOOP;
END
$procedure$;
And the other script that runs in parallel and INSERTS... also to the same table is also basically:
CREATE OR REPLACE PROCEDURE myConcurrentProc() LANGUAGE plpgsql
AS $procedure$
DECLARE
BEGIN
LOOP
EXIT WHEN row_count = 0
WITH cte AS (SELECT *
FROM TableC c
WHERE EXISTS (SELECT 1 FROM TableD d WHERE d.id = tblc.id)
INSERT INTO concurrent_table (SELECT id FROM cte);
COMMIT;
UPDATE log_tbl
SET status = 'FINISHED',
WHERE job_name = 'tbl_C_and_D_job';
END LOOP;
END
$procedure$;
So you can see I'm querying two different tables in each script, however inserting into the same some_table. I also have the UPDATE... statement that writes to a log table so I suppose that could also cause issues. Is there any way to use BEGIN... END here and COMMIT to avoid any deadlock/concurrency issues or should I just create a 2nd table to hold the "tbl_C_and_D_job" data?

PostgreSQL read commit on different transactions

I was running some tests to better understanding read commits for postgresql.
I have two transactions running in parallel:
-- transaction 1
begin;
select id from item order by id asc FETCH FIRST 500 ROWS ONLY;
select pg_sleep(10);
commit;
--transaction 2
begin;
select id from item order by id asc FETCH FIRST 500 ROWS ONLY;
commit;
The first transaction will select first 500 ids and then hold the id by sleeping 10s
The second transaction will in the mean while querying for first 500 rows in the table.
Based my understanding of read commits, first transaction will select 1 to 500 records and second transaction will select 501 to 1000 records.
But the actual result is that both two transactions select 1 to 500 records.
I will be really appreciated if someone can point out which part is wrong. Thanks
You are misinterpreting the meaning of read committed. It means that a transaction cannot see (select) updates that are not committed. Try the following:
create table read_create_test( id integer generated always as identity
, cola text
) ;
insert into read_create_test(cola)
select 'item-' || to_char(n,'fm000')
from generate_series(1,50) gs(n);
-- transaction 1
do $$
max_val integer;
begin
insert into read_create_test(cola)
select 'item2-' || to_char(n+100,'fm000')
from generate_series(1,50) gs(n);
select max(id)
into max_val
from read_create_test;
raise notice 'Transaction 1 Max id: %',max_val;
select pg_sleep(30); -- make sure 2nd transaction has time to start
commit;
end;
$$;
-- transaction 2 (run after transaction 1 begins but before it ends)
do $$
max_val integer;
begin
select max(id)
into max_val
from read_create_test;
raise notice 'Transaction 2 Max id: %',max_val;
end;
$$;
-- transaction 3 (run after transaction 1 ends)
do $$
max_val integer;
begin
select max(id)
into max_val
from read_create_test;
raise notice 'Transaction 3 Max id: %',max_val;
end;
$$;
Analyze the results keeping in mind that A transaction cannot see uncommitted DML.

Send email via Stored Procedure Fails

DECLARE
#id nvarchar(MAX),
#Counter int,
#CheckCount int
DECLARE CC1 CURSOR READ_ONLY
FOR
SELECT Ename, Ecount
FROM TestDB.dbo.TestEmail
OPEN CC1
FETCH NEXT FROM CC1 INTO #id, #Counter
WHILE ##FETCH_STATUS = 0
BEGIN
Begin
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Bob',
#recipients = #id,
#body = 'The stored procedure finished successfully. #$%',
#subject = 'Automated Success Message' ;
update TestDB.dbo.TestEmail
set Ecount = Ecount + 1
where Ename = #id
print #id
END
Select
#CheckCount = Ecount
from TestDB.dbo.TestEmail
where Ename = #id
print #CheckCount
If #CheckCount > 3
Begin
exec msdb.dbo.sp_update_job #job_id = N'6a81d6b3-af2d-4a27-b688-b9c69098f840', #enabled = 0
return
PRINT 'Count reached 3+'
End
FETCH NEXT FROM CC1 INTO #id, #Counter
END
CLOSE CC1
DEALLOCATE CC1
I have a table TestEmail with two columns Ename, Ecount. By using this table, I am trying to send emails to users. If the Ecount is greater than 3, the scripts exits, and nothing happens.
One user will receive emails for three times only. But my functionality fails, can't figure out where I am going wrong. My script doesn't throw any error but doesn't work either.
Marc's edit worked for me. The time I wrote the question, I had no idea about TSQL programming.
Although, the code in my question is working fine, Here is a wonderful tutorial to send an email via SQl server.
Send email in SQL server

TSQL - How to iterate a list of strings

I want to create a procedure that will insert all my jobs to the DB.
(a. All my jobs have equal characteristics. b. SSDT doesn't support jobs code management)
Now, I thought to create a script to insert all of them and as a c# developer I thought I need to initialize a list with their names.
I discovered while googling that the way to do it is with an in memory table and the best I could come with is this.
declare #jobsNames table(Id int, JobName nvarchar(100))
insert into #jobsNames (Id,JobName)
select 1,'JobName1' union
select 2,'JobName2' union
......
BEGIN TRANSACTION
DECLARE JobsCursor CURSOR FOR SELECT JobName FROM #jobsNames
OPEN JobsCursor
FETCH NEXT FROM JobsCursor INTO #JobName
WHILE ##Fetch_status = 0
BEGIN
.. do stuff
FETCH NEXT FROM JobsCursor INTO #JobName
WHILE ##Fetch_status = 0
END
COMMIT TRANSACTION
Question -
Is this the shortest/recommended way?
(It seems a hellotof code for a foreach)
declare #jobNames table(Id int, JobName nvarchar(100))
insert #jobNames values
(1, 'JobName1'),
(2, 'JobName2'),
--
(10, 'JobName10')
while exists(select 1 from #jobNames)
begin
declare #id int, #name nvarchar(100)
select top 1 #id = Id, #name = JobName from #jobNames
delete from #jobNames where Id = #Id
-- Do stuff here
end
Personally I avoid Cursors like the plague. Please make sure that you HAVE to iterate instead of doing your work set based. They don't call it RBAR for nothing.
DECLARE #counter INT, #max INT
SELECT #counter = 1, #max = max(id)
FROM #jobsNames
WHILE #counter <= #max
BEGIN
SELECT #val1 = val1 ... FROM #jobNames where ID = #counter
-- .. do stuff
SET #counter = #counter + 1
END

Loop in T-SQL, how get field value

In an SQL Server 2005 database, I have a stored procedure. I get some date in put them in a temp table. I'd like loop in this temp table and depending of the value of some fields change the value of others and make some check. I have to do this for each row.
How can I do this ?
thanks,
UPDATE1
BEGIN
SET NOCOUNT ON
--Create temp table
CREATE TABLE #MyTempTable(
id int IDENTITY(1, 1),
PriceMax int,
PriceMin int
)
-- Insert in temp table
INSERT INTO #tmpReconciliation (PriceMax, PriceMin)
SELECT PriceMax = PriceMaxProduct,
PriceMin = PriceMinProduct
FROM Products
DECLARE #RowNum int
SELECT #RowNum = Count(*) From #MyTempTable
WHILE #RowNum > 0
BEGIN
if(....)
PriceMin = 0
....
END
--Drop temp table
DROP TABLE #MyTempTable
END
I read MSDN documentation for WHILE loop and CURSOR.
For example, let's imagine your temp table is named Employee :
DECLARE #Emp_id int
DECLARE Employee_Cursor CURSOR FOR
SELECT EmployeeID
FROM Employee;
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor INTO #Emp_id;
WHILE ##FETCH_STATUS = 0
BEGIN
-- Here your actions
PRINT #Emp_id
FETCH NEXT FROM Employee_Cursor INTO #Emp_id;
END;
CLOSE Employee_Cursor;
DEALLOCATE Employee_Cursor;
GO
Here I decided to print EmployeeId, but everything is possible.
Tell us what are your checks, and what your temp table looks like if you need more help.
Can't you just use a cursor and inside the cursor run an update statement??
Cursors: http://www.jackdonnell.com/articles/SQL_CURSOR.htm