varchar entry of 5118 causing SQL Server stored procedure to fail - sql-server

and thanks in advance
I have a very basic stored procedure that inserts a row into a table. It has been working flawlessly until today
Here is the script
(
#emp varchar(16),
#logdate date,
#logtime time,
#term char(20),
#SSID char(16)
)
AS
INSERT INTO AccessLog (EmployeeID, LogDate, LogTime, TerminalID, InOut, ChangedBy)
VALUES (#emp, #logdate, #logtime, #term, 3, #SSID)
When the string of 5118 is passed to it the insert will fail. There are several triggers that fire after this insert finishes.
Here's the strange part. You can pass it anyother number for the #emp variable and it works just fine, but pass it 5118, it fails.
The error I receive is below:
Msg 241, Level 16, State 1, Procedure UpdateTimeWorked, Line 27
Conversion failed when converting date and/or time from character
string.
Here is the procedure that is failing – the highlighted line is Line 27
TRIGGER [dbo].[UpdateTimeWorked] ON [dbo].[TimeLog]
FOR UPDATE
AS
SET NOCOUNT ON
DECLARE #ID int;
DECLARE #RCDIDIN int;
DECLARE #RCDIDOUT int;
DECLARE #ComboIn datetime;
DECLARE #ComboOut datetime;
SELECT #ID = ID FROM INSERTED;
SELECT #ComboIn = LoginCombo FROM INSERTED;
SELECT #ComboOut = LogoutCombo FROM INSERTED;
SELECT #RCDIDIN = RCDIDIN FROM INSERTED;
SELECT #RCDIDOUT = RCDIDOUT FROM INSERTED;
**IF ( UPDATE(LogoutCombo))**
BEGIN
IF (#RCDIDOUT != 0)
BEGIN
UPDATE TimeLog
SET LogOutRND = (select CAST(dbo.roundtime(LogOutRND,0.25) AS TIME))
WHERE ID = #ID
UPDATE TimeLog
SET LogOutComboRND = CAST(CAST(LogOutDate AS DATE) AS SMALLDATETIME) + CAST(LogOutRND AS TIME)
WHERE ID = #ID
UPDATE TimeLog
SET TimeWorked = dbo.gettime(DATEDIFF(ss,LogInComboRND,LogoutComboRND))
WHERE ID = #ID AND LogInEntered = 1 AND LogOutEntered = 1
UPDATE TimeLog
SET TimeWorked = (select CAST(dbo.roundtime(TimeWorked,0.25) AS TIME)), Rounded = 1
WHERE ID = #ID AND LogInEntered = 1 AND LogOutEntered = 1
END
END
IF ( UPDATE(LoginCombo))
BEGIN
IF (#RCDIDIN != 0)
BEGIN
UPDATE TimeLog
SET LogInRND = (select CAST(dbo.roundtime(LogInRND,0.25) AS TIME))
WHERE ID = #ID
UPDATE TimeLog
SET LogInComboRND = CAST(CAST(LogInDate AS DATE) AS SMALLDATETIME) + CAST(LogInRND AS TIME)
WHERE ID = #ID
UPDATE TimeLog
SET TimeWorked = dbo.gettime(DATEDIFF(ss,LogInComboRND,LogoutComboRND))
WHERE ID = #ID AND LogInEntered = 1 AND LogOutEntered = 1
UPDATE TimeLog
SET TimeWorked = (select CAST(dbo.roundtime(TimeWorked,0.25) AS TIME)), Rounded = 1
WHERE ID = #ID AND LogInEntered = 1 AND LogOutEntered = 1
END
END
GO
I am at a total blank to come up with why this is not working.
Anyone have any ideas?
Like I stated, pass it any other entry for the #emp and it will run fine. I can even pass it ‘5118’ and it will work, but not 5118.

Related

SQL Server trigger only if a field is false prior to update

I have written the trigger below that inserts values if the emailstudio column is updated. This column can be 0 or 1.
How can I write the trigger so that it only fires if the emailstudio column is changed from 0 to 1, not if it was already 1?
Thank you
ALTER TRIGGER [dbo].[RM_Est_Email_Trigger]
ON [dbo].[K3_BriefHeader]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #estimate int, #Email_Date datetime, #status int, #emailstudio bit
SET #estimate = (SELECT Estimate from inserted)
set #Email_Date = GETDATE()
SET #status = (SELECT Status from inserted)
SET #emailstudio = (SELECT EmailStudio from inserted)
IF UPDATE (EmailStudio)
BEGIN
INSERT INTO [dbo].[K3_EstimateEmailDate] ([Estimate], [Email_Date],[Status], [EmailStudio])
VALUES (#estimate, #Email_Date, #status, #emailstudio)
END
END
Insert INTO [dbo].[K3_EstimateEmailDate] (
[Estimate]
,[Email_Date]
,[Status]
,[EmailStudio]
)
SELECT Estimate
,GETDATE()
,status
,1
FROM inserted
LEFT JOIN deleted
ON deleted.<primarykey> = inserted.<primarykey>
WHERE inserted.emailstudio = 1
AND (deleted.emailstudio is null -- inserted
OR deleted.emailstudio = 0) -- updated

How to use STRING_SPLIT() in a update query?

I want to update a table, One of its fields is a comma-separated value, I want to update that table, I tried different code but not working
UPDATE [PATS].[ReportSubscription]
SET [ScheduleID] = #ScheduleID
,[ReferenceID] = #ReferenceID
,[ReferenceType] = #ReferenceType
,[Schedule] = #Schedule
,[Day] = #Day
,[Time] = #Time
,[ProjectID] = #ProjectID
,[LastSentDate] = #LastSentDate
,[UserID] = #UserID
--,[CreatedBy] = #CreatedBy
--,[CreatedDate] = #CreatedDate
,[UpdatedBy] = #UpdatedBy
,[UpdatedDate] = #UpdatedDate
WHERE ID=#ID AND #Day IN (SELECT * FROM STRING_SPLIT(#Day,','))
I think this (SELECT * FROM STRING_SPLIT(#Day,',')) should be joined with the code.
You should be selecting value from the table returned by STRING_SPLIT:
WHERE ID=#ID AND [Day] IN (SELECT value FROM STRING_SPLIT(#Day,','))
Also, you should be comparing the table's column [Day] against the CSV list, not the variable #Day.
But, it is usually not a good idea to mix CSV data with SQL databases. Consider storing your CSV list of days in a separate table if possible.
Its possible that the data in the variable has space.
You should be selecting value from the table returned by STRING_SPLIT and triming value for use in condition, for example:
DECLARE #day NVARCHAR(500) = N'12 , 25, 41,54 ,89'
IF '41' IN (SELECT RTRIM(LTRIM(value)) FROM STRING_SPLIT(#Day,','))
PRINT('Ok')
ELSE
PRINT('Not Found')
-- printed : 'Ok'
Your query would be:
UPDATE [PATS].[ReportSubscription]
SET [ScheduleID] = #ScheduleID
,[ReferenceID] = #ReferenceID
,[ReferenceType] = #ReferenceType
,[Schedule] = #Schedule
,[Day] = #Day
,[Time] = #Time
,[ProjectID] = #ProjectID
,[LastSentDate] = #LastSentDate
,[UserID] = #UserID
,[CreatedBy] = #CreatedBy
,[CreatedDate] = #CreatedDate
,[UpdatedBy] = #UpdatedBy
,[UpdatedDate] = #UpdatedDate
WHERE ID=#ID AND Day IN (SELECT value FROM STRING_SPLIT(#Day,','))
Visit https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql?view=sql-server-2017 for more info.
Note: Make sure to trim your selection for unwanted spaces accordingly!

Getting error when executing the stored procedure using SQL Server

I get an error
Subquery returned more than 1 value
when executing a stored procedure. I need to copy data from the database I am building to the live database. The code inserted the data into TestTextmessage table and updateed TextMessage table. The error occurred when try to insert into the TestMobileRecipient table that is the reason why the table is empty.
The table structure and code are below
Stored procedure
CREATE PROCEDURE [dbo].[TestSendITMessage]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #i int
DECLARE #idmessage int
DECLARE #numrows int
DECLARE #messagehold TABLE
(
idx SMALLINT PRIMARY KEY IDENTITY(1,1),
MessageId INT
)
DECLARE #InsertedID INT
INSERT INTO #messagehold
SELECT DISTINCT Id
FROM [MPFT_SendIT].dbo.TextMessage
WHERE DontSendBefore < GETDATE()
AND DateSent IS NULL
AND MessageSent = 0
SET #i = 1
SET #numrows = (SELECT COUNT(*) FROM #messagehold)
IF #numrows > 0
WHILE (#i <= (SELECT MAX(idx) FROM #messagehold))
BEGIN
SET #idmessage = (SELECT MessageId FROM #messagehold WHERE idx = #i)
--Do something with Id here
PRINT #idmessage
INSERT INTO [dbo].[TestTextMessage] ([Origin], [MessageBody], [MessageSent], [DateCreated], DontSendBefore)
SELECT
'LogIT', MessageBody, 0, GETDATE(), DontSendBefore
FROM
[MPFT_SendIT].dbo.TextMessage
WHERE Id = #idmessage
SET #InsertedID = SCOPE_IDENTITY();
INSERT INTO [dbo].[TestMobileRecipient] ([MessageId], MobileNumber])
VALUES (#InsertedID, (SELECT MobileNumber FROM MobileRecipient
WHERE MessageId = #idmessage))
UPDATE TextMessage
SET DateSent = GETDATE(),
MessageSent = 1
WHERE Id = #idmessage
SET #i = #i + 1
END
END
the error message is very clear. Your sub-query SELECT MobileNumber FROM MobileRecipient WHERE MessageId= #idmessage is returning more than 1 row
Change the insertion of table TestMobileRecipient to following
Insert into [dbo].[TestMobileRecipient]
(
[MessageId]
,[MobileNumber]
)
SELECT #InsertedID
, MobileNumber
FROM MobileRecipient
WHERE MessageId= #idmessage
You should update your this line
SET #idmessage = (SELECT MessageId FROM #messagehold WHERE idx = #i)
with
SET #idmessage = (SELECT top 1 MessageId FROM #messagehold WHERE idx = #i)

Trigger did not run?

I have a trigger "after insert/update/delete/". It is supposed to count Balance on Account table based on transactions in Transaction table. It is on Transaction table. I am getting Balance discrepancies rarely, so have decided to add some logging into it. It dumps inserted+deleted tables (they are combined into a table var) and tsql statement which fired it. Judging from my log, it looks like the trigger did not fire for some inserts into Transaction table. Can this happen ? Are there any TSQL statement which change table data without firing trigger (except truncate table etc)?
Here is the trigger :
CREATE TRIGGER [dbo].[trg_AccountBalance]
ON [dbo].[tbl_GLTransaction]
AFTER INSERT, UPDATE, DELETE
AS
set nocount on
begin try
declare #OldOptions int = ##OPTIONS
set xact_abort off
declare #IsDebug bit = 1
declare #CurrentDateTime datetime = getutcdate()
declare #TriggerMessage varchar(max), #TriggerId int
if #IsDebug = 1
begin
select #TriggerId = isnull(max(TriggerId), 0) + 1
from uManageDBLogs.dbo.tbl_TriggerLog
declare #dbcc_INPUTBUFFER table(EventType nvarchar(30), Parameters Int, EventInfo nvarchar(4000) )
declare #my_spid varchar(20) = CAST(##SPID as varchar(20))
insert #dbcc_INPUTBUFFER
exec('DBCC INPUTBUFFER ('+#my_spid+')')
select #TriggerMessage = replace(EventInfo, '''', '''''') from #dbcc_INPUTBUFFER
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
declare #Oper int
select #Oper = 0
-- determine type of sql statement
if exists (select * from inserted) select #Oper = #Oper + 1
if exists (select * from deleted) select #Oper = #Oper + 2
if #IsDebug = 1
begin
select #TriggerMessage = '#Oper = ' + convert(varchar, #Oper)
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
if #Oper = 0 return -- No data changed
declare #TomorrowDate date = dateadd(day, 1, convert(date, getdate()))
declare #CurrentDate date = convert(date, getdate())
-- transactions from both inserted and deleted tables
declare #tbl_Trans table (FirmId int, GLAccountId int,
AmountDebit money, AmountCredit money, "Status" char(1), TableType char(1))
declare #tbl_AccountCounters table (FirmId int, GLAccountId int, Balance money)
declare #IsChange bit = null
insert into #tbl_Trans (FirmId, GLAccountId, AmountDebit, AmountCredit, "Status", TableType)
select FirmId, GLAccountId, AmountDebit, AmountCredit, "Status", 'I'
from inserted
union
select FirmId, GLAccountId, AmountDebit, AmountCredit, "Status", 'D'
from deleted
if #IsDebug = 1
begin
select #TriggerMessage = (select * from #tbl_Trans for xml path ('tbl_Trans'))
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
insert into #tbl_AccountCounters (FirmId, GLAccountId, Balance)
select FirmId, GLAccountId, 0
from #tbl_Trans
group by FirmId, GLAccountId
if #Oper = 1 or #Oper = 2 -- insert/delete
begin
update #tbl_AccountCounters
set Balance = cnt.TransSum
from #tbl_AccountCounters as ac join
(
select trans.FirmId, trans.GLAccountId,
isnull(sum((trans.AmountDebit - trans.AmountCredit) * iif(trans.TableType = 'I', 1, -1)), 0) as TransSum
from #tbl_Trans as trans
where trans.Status = 'A'
group by trans.FirmId, trans.GLAccountId
) as cnt on ac.FirmId = cnt.FirmId and ac.GLAccountId = cnt.GLAccountId
select #IsChange = 1
end
else
begin
if update(AmountDebit) or update(AmountCredit) or update(Status) or update(GLAccountId)
begin
update #tbl_AccountCounters
set Balance = cnt.TransBalance
from #tbl_AccountCounters as ac join
(select trans.FirmId, trans.GLAccountId, isnull(sum(trans.AmountDebit - trans.AmountCredit), 0) as TransBalance
from dbo.tbl_GLTransaction as trans
where trans."Status" = 'A' and exists (select 1 from #tbl_AccountCounters as ac
where ac.GLAccountId = trans.GLAccountId and ac.FirmId = trans.FirmId)
group by trans.FirmId, trans.GLAccountId) as cnt on
ac.FirmId = cnt.FirmId and ac.GLAccountId = cnt.GLAccountId
select #IsChange = 0
end
end
if #IsDebug = 1
begin
select #TriggerMessage = '#IsChange = ' + isnull(convert(varchar, #IsChange), 'null')
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
select #TriggerMessage = (select * from #tbl_AccountCounters for xml path ('tbl_AccountCounters'))
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
if #IsChange is not null
begin
update tbl_GLAccount
set tbl_GLAccount.Balance = iif(#IsChange = 1, cnt.Balance + acc.Balance, cnt.Balance),
tbl_GLAccount.LastUpdate = getutcdate(),
tbl_GLAccount.LastUpdatedBy = 1
from #tbl_AccountCounters as cnt join dbo.tbl_GLAccount as acc on
cnt.FirmId = acc.FirmId and cnt.GLAccountId = acc.GLAccountId
end
if (16384 & #OldOptions) = 16384 set xact_abort on
end try
begin catch
declare #ErrorLine varchar(max)
select #ErrorLine = uManageDb.dbo.udf_GetErrorInfo()
insert into uManageDb.dbo.tbl_TriggerError ("Name", "Message", CreateDate)
values ('AccountingDB..trg_AccountBalance', #ErrorLine, GETUTCDATE())
end catch
I think I've found it. I have this line:
select .. from inserted
union
select .. from deleted
and they inserted 5 trans for $300 and 4 trans $100. I've got 2 records (300 and 100) in my #tbl_Trans (it was in the log). That's probably was the bug. So log hellps and trigger run as it had to.
I'll replace union with union all.

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

Resources