SQL Service Broker - sql-server

I have created a service broker that stores id's in the queue table. But the problem is when i want to get the id back in the stored procedure it's now formatted as xml. Because the service broker message is XML.
How can i just get the last Id each time because the trigger is fired after an update of a row.
Below my code =>
/****** Object: Trigger [dba].[TriggerCall] Script Date: 6/16/2015 2:55:57 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dba].[TriggerCall] ON [dba].[CallID] FOR UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #MessageBody XML
DECLARE #ID varchar(50)
-- Insert statements for trigger here
--get relevant information from inserted/deleted and convert to xml message
SET #MessageBody = (SELECT Id FROM inserted
FOR XML AUTO)
If (#MessageBody IS NOT NULL)
BEGIN
DECLARE #Handle UNIQUEIDENTIFIER;
BEGIN DIALOG CONVERSATION #Handle
FROM SERVICE [TestServiceInitiator]
TO SERVICE 'TestServiceTarget'
ON CONTRACT [TestContract]
WITH ENCRYPTION = OFF;
SEND ON CONVERSATION #Handle
MESSAGE TYPE [TestMessage](#MessageBody);
END
END
/****** Object: StoredProcedure [dbo].[usp_GetCall] Script Date: 6/16/2015 2:44:27 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[usp_GetCall]
AS
BEGIN
DECLARE #message_type varchar(100)
DECLARE #dialog uniqueidentifier, #message_body XML;
Declare #Object as Int;
Declare #URL as varchar(255)
Declare #ResponseText as Varchar(8000);
Declare #ID as Varchar(38);
WHILE (1 = 1)
BEGIN -- Receive the next available message from the queue
WAITFOR (
RECEIVE TOP(1) #message_type = message_type_name,
#message_body = CAST(message_body AS XML),
#dialog = conversation_handle
FROM dbo.TestQueue ), TIMEOUT 500 if (##ROWCOUNT = 0 OR #message_body IS NULL)
BEGIN
BREAK
END
ELSE
BEGIN
INSERT INTO [dbo].[testtabel]
([id]
,[callid],
[test])
VALUES
('111', '111', #message_body)
END
END CONVERSATION #dialog
END
END

If I understand your question correctly, you're just looking to rehydrate the ID into an int. If that's the case, the following should do:
use tempdb;
create table inserted (id int);
insert into inserted values (1);
declare #message_body xml;
set #message_body= (select * from inserted for xml auto);
select #message_body.value('(/inserted/#id)[1]', 'int');
The magic is the last line (the rest was just setup for me to test).
I'll take this opportunity to clear up a misconception that you seem to have, though. Triggers in SQL server aren't fired per row, but per batch. So, if you run an update against your table and it updates 50 rows, the trigger gets fired once and the inserted (and deleted) table will have 50 rows in it. Just something to take into account.

Related

Fire SQL Trigger When Column Value Changes

I am adding the date to a column in SQL when the 'workstatus' is 'completed', but my problem is, when I open and save the same job again in the software, it runs the trigger and changes the date again to a new value which I don't want.
I want the trigger to run only if the 'workstatus' value is something else than 'completed'.
GO
/****** Object: Trigger [dbo].[TRJCD_JOBREQUEST] Script Date: 06/25/2021 15:49:04 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[TRJCD_JOBREQUEST] ON [dbo].[TBL_JOBREQUEST]
AFTER UPDATE,INSERT
AS
if (Update (workstatus))
begin
DECLARE #Jobcompletiondate datetime
DECLARE #workstatus VARCHAR(15)
DECLARE #jobid int
select #workstatus = workstatus from inserted
select #jobid = jobid from inserted
select #Jobcompletiondate = GETDATE()
begin
if #workstatus='Completed'
update TBL_JOBREQUEST set JobCompDate=#Jobcompletiondate where jobid = #jobid
end
end
The following is how you should construct your trigger.
There is no need to assign any values to variables, triggers fire once per batch and always operate on the set of updated rows.
If you update a status to Completed you need to check it's not currently Completed, also if you want to retain the first JobCompDate even if the status is amended afterwards simply use a case expression to only update the column where it's currently NULL.
create or alter trigger [dbo].[TRJCD_JOBREQUEST] on [dbo].[TBL_JOBREQUEST]
after update,insert
as
if ##RowCount=0 return
set nocount on
if Update (workstatus)
begin
update t set
t.JobCompDate=case when t.JobCompDate is null then GetDate() else t.JobCompDate end
from inserted i join TBL_JOBREQUEST t on t.jobid=i.jobid
where i.workstatus='Completed'
and not exists (
select * from deleted d
where d.jobid=i.jobid and d.workstatus=i.workstatus
)
end
Please note that I do not have your data set, so I'm unable to test the trigger, however, based on what you provided in your question, I believe this is the answer you are seeking:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[TRJCD_JOBREQUEST] ON [dbo].[TBL_JOBREQUEST]
AFTER UPDATE,INSERT
AS
if (Update (workstatus))
begin
DECLARE #Jobcompletiondate datetime
DECLARE #currentworkstatus VARCHAR(15)
DECLARE #oldworkstatus VARCHAR(15)
DECLARE #jobid int
select #oldworkstatus = workstatus from deleted
select #currentworkstatus = workstatus from inserted
select #jobid = jobid from inserted
select #Jobcompletiondate = GETDATE()
begin
if #currentworkstatus='Completed' and #oldworkstatus <> 'Completed'
update TBL_JOBREQUEST set JobCompDate=#Jobcompletiondate where jobid = #jobid
end
end
You needed to check if the deleted workstatus does not equal Completed and only then should the trigger fire.

Does SQL SERVER 2014 queues the incoming request in a locked table?

USE [ProjectDB]
GO
/****** Object: StoredProcedure [dbo].[generateTransRefNo] Script Date: 9/9/2017 7:19:55 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[generateTransRefNo]
(
#year varchar(max),
#txrefno varchar(max) output
)
AS
BEGIN
BEGIN TRANSACTION
SET NOCOUNT ON
DECLARE #seq varchar(max)
set #seq = '00000000'
select #txrefno = sequence from TBSEQUENCE WITH (TABLOCK, HOLDLOCK) where year =#year
if(#txrefno='' or #txrefno is null)
begin
insert into TBSEQUENCE values (substring(cast(DATEPART(year, GETDATE()) as varchar),3,4),1,'YR')
end
select #txrefno = concat(year,SUBSTRING('00000000',1,8- len(sequence)),sequence) from TBSEQUENCE where year =#year
update TBSEQUENCE set sequence = sequence+1 where year=#year
COMMIT TRANSACTION
END
select #txrefno
Say I have this stored procedure that gets the latest sequence from tbsequence then update it
The application is java using hibernate to access the database which is deployed already that I cannot access.
Will this hold the incoming requests while the current request is still up or cancel them and returns nothing?

Automated SQL Email bad results

I've created an automated email that tells my customers when they've ordered a part that's on back order. It uses one SQL view and 2 stored procedures as listd below. My issue is that when I execute it, the first backorder sends an email with the title 'sql server message' and has no sql results in it. All subsequent backorders run properly with the correct subject and data in the body. Is there something I've done wrong in my sp's?
This sp runs first, and builds a list of our customers that have Backordered parts.
USE [001]
GO
/****** Object: StoredProcedure [mfg].[sp_BackorderEmailListing] Script Date: 6/29/2015 4:09:31 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Nicholas Dininno>
-- Create date: <6/15/15>
-- Description: <Runs through a distinct list of Franchisees that have
-- orders on backorder, and passes their info to [mfg].[sp_SendBackorderEmail]>
-- =============================================
ALTER PROCEDURE [mfg].[sp_BackorderEmailListing]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
Declare #cmp_code nvarchar(50)
Declare #email nvarchar(Max)
Declare cur_BackOrdSP Cursor
For SELECT DISTINCT [Franchise ID] FROM [001].[mfg].[Backorder_Report]
Open cur_BackOrdSP
Fetch Next From cur_BackOrdSP
While ##FETCH_STATUS = 0
Begin
--find email address
SELECT #email = [cmp_e_mail] FROM [001].[mfg].[Backorder_Report] where [Franchise ID]=#cmp_code
execute [mfg].[sp_SendBackorderEmail] #cmp_code, #email
Fetch Next From cur_BackOrdSP Into #cmp_code
END
Close cur_BackOrdSP
Deallocate cur_BackOrdSP
END
The next part is where the email is built.
USE [001]
GO
/****** Object: StoredProcedure [mfg].[sp_SendBackorderEmail] Script Date: 6/29/2015 4:12:51 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Nick Dininno
-- Create date: 6/25/2015
-- Description: Sends email to Franchisees with backordered parts
-- =============================================
ALTER PROCEDURE [mfg].[sp_SendBackorderEmail]
-- Add the parameters for the stored procedure here
(#cmp_code nvarchar(5), #email nvarchar(50))
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE #profile nvarchar(50)
DECLARE #subject nvarchar(100)
DECLARE #querystr nvarchar (MAX)
set #profile = 'Reports'
set #subject = 'Backorder Report for ' + #cmp_code
set #querystr = 'SET NOCOUNT ON
SELECT [Order #],[Order Date],[Item #],[Description]
FROM [001].[mfg].[Backorder_Report]
WHERE [Franchise ID] = '''+#cmp_code+'''';
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #profile,
#recipients = 'me#company.com',
#subject = #subject,
#body = 'Note: This is an automatic e-mail message generated by the Parts Department.
Our records indicate that you ordered the parts shown below. Unfortunately these parts are currently on backorder. They will be shipped to you as soon as they become available. In the meantime, if you have any questions or concerns regarding this order please contact the Parts Department via email parts#lawndoctor.com or call 732-308-2300 x4.
Thank you!
Parts Department
parts#company.com
',
#query = #querystr
END
The recipient me#company.com will be replaced once i'm sure it works with the #email parameter
My issue here was a syntax issue. In the first procedure I didn't pass the Franchise ID into #cmp_code.
Fetch Next From cur_BackOrdSP
Should have been
Fetch Next From cur_BackOrdSP into #cmp_code

Need a stored procedure that inserts a row and returns the ID

I tried to write a stored procedure that first inserts a new record into table and then returned the id of this new record. I am not sure if it is the correct way and the best way to achieve this.
ALTER PROCEDURE dbo.spAddAsset
(
#Name VARCHAR(500),
#URL VARCHAR(2000)
)
AS
BEGIN
Set NOCOUNT on;
Insert Into Assets (Name, URL) Values (#Name, #URL)
Declare #new_identity int;
SELECT #new_identity = SCOPE_IDENTITY()
return #new_identity;
END
To return a single scalar value to the caller you should use an OUTPUT parameter, not RETURN. RETURN is for error/status codes. Also the prefix sp is redundant and unnecessary.
CREATE PROCEDURE dbo.AddAsset
#Name VARCHAR(500),
#URL VARCHAR(2000),
#new_identity INT = NULL OUTPUT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.Assets(Name, URL) SELECT #Name, #URL;
SET #new_identity = SCOPE_IDENTITY();
END
GO
Then to call it:
DECLARE #new_identity INT;
EXEC dbo.AddAsset #Name = 'a', #URL = 'b', #new_identity = #new_identity OUTPUT;
PRINT #new_identity;
EDIT just adding a disclaimer that won't affect the asker in this specific scenario, but may help in other scenarios or for future readers. In SQL Server 2008 R2 and earlier, there is a potentially nasty bug with built-in functions such as SCOPE_IDENTITY when parallelism is used to derive the results to be inserted (think INSERT FROM othertable). This bug (here is the Connect item) is fixed in Cumulative Update #5 for SQL Server 2008 R2 SP1, but so far a fix has not appeared for 2008 R2 RTM, 2008 or 2005.
I can't see why it is necessary to place the returned row value into a new variable #new_identity. I would simply include SELECT SCOPE_IDENTITY(); at the end of the stored procedure like this:
CREATE PROCEDURE dbo.AddAsset
#Name VARCHAR(500),
#URL VARCHAR(2000)
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.Assets(Name, URL) SELECT #Name, #URL;
SELECT SCOPE_IDENTITY();
END
GO

SQL Server row level triggers

I tried to achieve row level delete trigger by using cursor but when in trying yo delete the any row from table it tooks so long time.
I could not understand where exactly it stuck.
/****** Object: Trigger [delStudent] Script Date: 06/24/2010 12:33:33 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [delStudent]
ON [dbo].[Student]
FOR DELETE
AS
DECLARE #Roll as varChar(50);
DECLARE #Name as varChar(50);
DECLARE #Age as int;
DECLARE #UserName as varChar(50);
SELECT #UserName=SYSTEM_USER;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
declare CurD cursor for select roll, Sname, age from deleted
open CurD
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO [dbo].[Audit]
(roll,sname,age,userId)
VALUES
(#Roll,#Name,#Age,#UserName)
END
COMMIT TRANSACTION;
Close CurD
DEALLOCATE CurD
I think you should transform your cursor in an insert-select sentence. I'm not sure this will solve your problem, but it's a good best practice anyway.
INSERT [dbo].[Audit] (roll,sname,age,userId)
SELECT 'FIELDS FROM DELETED', SYSTEM_USER
FROM deleted
Try to avoid cursors, and this will result in better performance.

Resources