Increment of counter variable in SQL Transaction before commit - sql-server

I am using SQL Transaction and inside transaction I need to insert multiple record in one go.
But problem is that my Id is not identity. So, I need to get max id and insert it.
But when I get the max and increment +1. It is always same.
Following is the my code.
BEGIN
DECLARE #maxId INT;
SET #maxId = (SELECT MAX(id) FROM [dbo].[tempUser]);
BEGIN TRAN
BEGIN TRY
INSERT INTO [dbo].[tempUser] (id, principal, first_name, last_name, email, isActive)
SELECT #maxId+1, A.User_Id, A.FirstName,A.LastName, 'ab#gmail.com', 1 FROM ADUser_Table A
LEFT JOIN [dbo].[tempUser] B on A.User_Id =B.principal where B.principal Is NULL
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
END CATCH
END
How can I insert max id +1 with every record in sql transaction?

Instead of #maxid+1 you can add the #maxId with the row_number() as below.
SELECT #maxId+ (row_number()over(order by A.User_Id)) , A.User_Id,..
from [yourtable]

Use Row_Number while inserting as next:-
BEGIN
DECLARE #maxId INT;
SET #maxId = (SELECT MAX(id) + 1 FROM [dbo].[tempUser]);
BEGIN TRAN
BEGIN TRY
INSERT INTO [dbo].[tempUser] (id, principal, first_name, last_name, email, isActive)
SELECT row_number() over(order by A.User_Id) + #i , A.User_Id, A.FirstName,A.LastName, 'ab#gmail.com', 1 FROM ADUser_Table A
LEFT JOIN [dbo].[tempUser] B on A.User_Id =B.principal where B.principal Is NULL
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
END CATCH
END

Related

Best approach to Read bulk records and update individual records without locking table

Here is my code and I am looking for best approach without locking records while updating.
Declare #maxRowId int
Set #maxRowId=0
Declare #rowIndex int
Set #rowIndex=1
Declare #recordId bigint
Set #recordId=0
Begin Try
Declare #ReservedTickets table(RowId int, Id bigint,TicketCodeId nvarchar(100), Status nvarchar(100),DateTimeReserved datetime)
insert into #ReservedTickets
select ROW_NUMBER() OVER (ORDER BY Id) RowId,Id ,TicketCodeId, Status, DateTimeReserved
from [dbo].[Ticket] with(nolock)
where status='reserved' and
datetimeReserved <= DATEADD(minute, -30, getdate())
select #maxRowId=max(RowId) from #ReservedTickets
while #rowIndex<=#maxRowId
BEGIN
Select #recordId=Id from #ReservedTickets where Rowid=#rowIndex
Begin TRAN
UPDATE upd_Ticket
set
upd_Ticket.Status='available',
upd_Ticket.DateTimeReserved=null,
RequestReference=null
from [dbo].[Ticket] upd_Ticket
INNER JOIN [dbo].[Ticket] slct_Ticket with(nolock)
ON upd_Ticket.Id = slct_Ticket.Id
where slct_Ticket.Id= #recordId and
slct_Ticket.status='reserved' and
slct_Ticket.datetimeReserved <= DATEADD(minute, -30, getdate())
Commit TRAN
set #rowIndex=#rowIndex+1
END
select RV.Id, RV.status 'InitialStatus' ,V.status 'UpdatedStatus', RV.DateTimeReserved
from #ReservedTickets RV
Left join [dbo].[Ticket] V on
RV.Id=v.Id

sql server trigger updates only one row

I have a trigger after update an I want to update not only one row by one update, but also more rows. But when I try to update more rows, error Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. appears. Is there a way to update more rows?
ALTER TRIGGER ChangeActorWhileUpdate
ON Herec
AFTER UPDATE
AS
BEGIN
BEGIN TRANSACTION
BEGIN TRY
DECLARE #od DATE;
DECLARE #idHerce INT;
SELECT #idHerce = (SELECT id_herce FROM DELETED);
IF EXISTS(SELECT COUNT(*) FROM historie_hercu WHERE Herec_id_herce = #idHerce)
BEGIN
SELECT #od = (SELECT MAX(do) FROM historie_hercu WHERE Herec_id_herce=#idHerce)
END
IF(SELECT COUNT(*) FROM historie_hercu WHERE Herec_id_herce = #idHerce) = 0
BEGIN
SELECT #od = (SELECT datum_narozeni FROM DELETED);
END
INSERT INTO historie_hercu (jmeno_herce, prijmeni_herce, datum_narozeni, datum_umrti, mesto_narozeni, mesto_umrti, zeme, od, do, Herec_id_herce)
SELECT jmeno_herce, prijmeni_herce, datum_narozeni, datum_umrti, mesto_narozeni, mesto_umrti, zeme, #od, CAST(GETDATE() AS DATE), #idHerce FROM DELETED;
COMMIT;
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE();
ROLLBACK;
END CATCH
END
Triggers are fired once for the batch of rows affected by the triggering action, they are not fired once for each row. You need to write your code in a way that it can handle more than 1 row.
Try something like.....
ALTER TRIGGER ChangeActorWhileUpdate
ON Herec
AFTER UPDATE
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
WITH X AS (
SELECT d.* , h.do AS H_Do
,ROW_NUMBER() OVER (PARTITION BY h.Herec_id_herce ORDER BY do DESC) rn
FROM deleted d
LEFT JOIN historie_hercu h ON d.id_herce = h.id_herce
)
INSERT INTO historie_hercu
(jmeno_herce, prijmeni_herce, datum_narozeni, datum_umrti
, mesto_narozeni, mesto_umrti, zeme, od, do, Herec_id_herce)
SELECT x.jmeno_herce, x.prijmeni_herce, x.datum_narozeni, x.datum_umrti
, x.mesto_narozeni , x.mesto_umrti, x.zeme
, ISNULL(x.H_Do ,x.datum_narozeni)
, CAST(GETDATE() AS DATE), x.id_herce
FROM x
WHERE x.rn = 1;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE();
ROLLBACK TRANSACTION;
END CATCH
END

Safest way to get the last inserted ID to be unique - SQL

I know that the SCOPE_IDENTITY() will get the last inserted row from insert statement. However, for the following case, I am not too sure is SCOPE_IDENTITY() is safe. As SELECT MAX(ID) FROM TableA will have go through scan the table to get the max id and it will have performance issue, even slightly, I believe.
Here is the case:
DECLARE #DaysInMonth INT
DECLARE #FirstID INT
DECLARE #SecondID INT
DECLARE #ThirdID INT
DECLARE #FourthID INT
SET #DaysInMonth = DAY(EOMONTH('2016-09-01'))
BEGIN TRY
BEGIN TRANSACTION
WHILE #DaysInMonth > 0
BEGIN
-- First Insert -- Begin
INSERT INTO tableA ('first insert - ' + #DaysInMonth)
-- First Insert -- End
SET #FirstID = SCOPE_IDENTITY()
-- Second Insert -- Begin
INSERT INTO tableB ('second insert - ' + #DaysInMonth)
-- Second Insert -- End
SET #SecondID = SCOPE_IDENTITY()
-- Third Insert -- Begin
INSERT INTO tableC ('third insert - ' + #DaysInMonth)
-- Third Insert -- End
SET #ThirdID = SCOPE_IDENTITY()
-- Fourth Insert -- Begin
INSERT INTO tableD ('fourth insert - ' + #DaysInMonth)
-- Fourth Insert -- End
SET #FourthID = SCOPE_IDENTITY()
SET #DaysInMonth = #DaysInMonth - 1
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
THROW
END CATCH
As from the case above, I have to insert the records every loop for fourth times for how many days in the month that I have declared.
From what I know, there are 4 to get the last inserted ID:
SCOPE_IDENTITY
##IDENTITY
SELECT MAX(ID) FROM tableA
IDENT_CURRENT
From the following post:
Post
Is mentioned that SCOPE_IDENTITY() is what generally that you want to use.
What I mean with 'Safe' is, do the ID will be unique during the loop?
Thank you.
You can use OUTPUT column in the last insert statement, Ofcourse this is another option where you will get what exactly input statement executed.. Below is just an example
CREATE TABLE #tablea (
id int IDENTITY (1, 1),
val char(10)
)
DECLARE #outputtbl TABLE (
id int,
val char(10)
)
INSERT INTO #tablea (val)
OUTPUT INSERTED.* INTO #outputtbl
VALUES ('test')
SELECT id
FROM #outputtbl

Is it safer to declare in SQL Server stored procedure or I can use it like this?

This is one of my stored procedures and I've got the following question:
Is this a safer way to declare #LoginTime for example or I can directly use LoginTime since it's in the same table INFO? Both way works but I want to know what's better and safer?
#AccID varchar(21)
AS
DECLARE #id char(21)
SELECT TOP 1 #id = CharNum FROM USERONLINE WHERE AccID = #accid
DECLARE #LoginTime bigint
SELECT #LoginTime = LoginTime FROM INFO WHERE UserId = #id
DECLARE #Time bigint
SELECT #Time = Time FROM INFO WHERE UserId = #id
BEGIN TRAN
UPDATE INFO
SET Time = #Time + (DATEDIFF(s,'19700101', GETDATE()) - #LoginTime)
WHERE UserId = #id
COMMIT TRAN
Try below query
BEGIN TRY
BEGIN TRAN
UPDATE iFO
SET
iFO.Time = iFO.Time + (DATEDIFF(s,'19700101', GETDATE()) - iFO.LoginTime)
FROM INFO iFO
INNER JOIN
(
SELECT TOP 1 CharNum
FROM USERONLINE
WHERE
AccID = #AccID
)uOL ON uOL.CharNum = iFO.UserId
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
END CATCH
you can use Time and LoginTime directly in the update. and it'll be only one atomic operation, with your queries outside the transaction those values could change before you update.
I missed the #id part, you can also get the id with a subquery inside update (or with a join as in Gaurav's answer)
UPDATE yourtable
SET ...
WHERE UserId = (SELECT TOP 1 Id FROM ...)
why don't you do in this way-
DECLARE #id char(21)
SELECT TOP 1 #id = CharNum FROM USERONLINE WHERE AccID = #accid
BEGIN TRAN
UPDATE INFO
SET Time = (Time + (DATEDIFF(s,'19700101', GETDATE())) - LoginTime)
FROM INFO
WHERE UserId = #id
COMMIT TRAN

After Insert Trigger not Working

I'm trying to run an after insert trigger but it does not work, is there any way to see a log of it?
Here is my code
ALTER TRIGGER [dbo].[SUBSID_INSERT] on [dbo].[STUDENT_MASTER] AFTER INSERT AS
BEGIN
SET NOCOUNT ON
DECLARE #numrows int, #hostname char(30), #sysuser char(30);
SELECT #numrows = ##rowcount;
IF #numrows = 0 RETURN;
SELECT #hostname = substring(hostname, 1, 30)
FROM master.dbo.sysprocesses
WHERE spid = ##spid;
SELECT #sysuser = substring(system_user, 1, 30);
INSERT INTO insert statement,
FROM STUDENT_MASTER JOIN INSERTED I ON STUDENT_MASTER.ID_NUM = I.ID_NUM
JOIN NAME_MASTER ON NAME_MASTER.ID_NUM = STUDENT_MASTER.ID_NUM
WHERE
STUDENT_MASTER.id_num not in ( select DISTINCT id_num from subsid_master where subsid_cde LIKE '0%')
END
GO
You are checking ##ROWCOUNT after declaring a variable. This will always show the result of the last statement (in this case 0), so stop doing that. Instead just use:
SELECT #numrows = COUNT(*) FROM inserted;
I see lots of other potential improvements but this is precisely why it currently appears to be "not working."

Resources