How to get value inside trigger - sql-server

i'm running my application using one DB but during some specific table insertion i'm calling trigger where i need to get some value from some another DB.Previously i did this by hard coding the specific DB name inside the trigger. i got it correctly but now i need to remove the dependency of that DB name.so i need to find get that particular value in some another way. Can anyone help me to find the solution please...
Previous trigger.
CREATE TRIGGER [dbo].[xC2C_ADT_Upd] on [dbo].[dmAppt] For Insert,UPDATE
AS
BEGIN
If Not Exists (Select top 1* from inserted)
Return
declare #ADT_ID UniqueIdentifier
Declare #personuid int
declare #ApptID int
select #personuid = PID, #ApptID = IDAppt from inserted
set #ADT_ID = NewID()
declare #MSG_Type varchar(1)
declare #AddDate datetime
declare #LastUpdateDate datetime
declare #ServerID [varchar](50)
declare #ClientDB [varchar](max)
declare #ClientID [varchar](50)
set #ServerID = Convert(varchar(50),CONNECTIONPROPERTY('local_net_address'))
set #ClientDB = db_name()
select #ClientID = ClientID from dm_asarum_Client_Master..Clients //here only my database is hardcoded
where DBName_Current = #ClientDB
Now i did some changes,
set #ClientID = (select top 1 CAST(CONTEXT_INFO as varchar(50)) from sys.dm_exec_sessions where context_info!=0x order by last_request_end_time desc) --ClientID from dm_asarum_Client_Master..Clients
i have set the Context_info value in some SP like the following.,
alter PROCEDURE [dbo].[dm_ApptResRecurrReasonRelViewSel]
(
#IDLocation int,
#IDApptResource int,
#StartDate datetime,
#IDApptResRecurr int,
#ClientID varchar(50)
)
AS
SET NOCOUNT ON;
Declare #context_info1 varbinary(128);
SET
#context_info1 = CAST(#ClientID as varbinary(128));
SET
CONTEXT_INFO #context_info1;
Now the problem is,Using the SQL Profiler i noticed, i can able the get the clintID inside this SP correctly. but it is not saving this Context_info inside sys.dm_exec_sessions correctly. when i ran this manually in sql like,
exec [dm_ApptResRecurrReasonRelViewSel] #IDLocation=101,#IDApptResource=59,#StartDate='2016-05-27 17:00:00',#IDApptResRecurr=NULL,#ClientID='cl_obgyn_reg'
i context_info value is updated correctly i don't know where i missied.

Related

Using SQL Server triggers, how to keep data in two identical tables the same without going in a infinite loop?

I know that probably the best way to acomplish this would be to make some changes in the application code to save all changes in both tables, but the company ordered to make this happens with database logic, using triggers. So, there are two different databases, both with a table named User, and they both have the same model already. What I insert/update in User on database X have to be inserted/updated in table User on database Y, and vice-versa.
I managed to make the insert and update trigger going in one direction (database X -> database Y), but now i'm thinking that when I create the trigger on database Y, a loop would happen. What is missing or what can I do to make the trigger loop not happen?
This is what I have created for now on one of the databases:
---insert trigger
USE [DATABASE_Y]
CREATE TRIGGER [dbo].[TR_USER_OnInsert] ON [dbo].[USER]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [DATABASE_X].[dbo].[USER] (
usu_id,
usu_name,
usu_block,
usu_login,
usu_password
)
SELECT
usu_id,
usu_name,
usu_block,
usu_login,
usu_password
FROM INSERTED
SET NOCOUNT OFF
END
---update trigger
USE [DATABASE_Y]
ALTER TRIGGER [dbo].[TR_USER_OnUpdate] ON [dbo].[USER]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE X
SET
X.usu_id = INSERTED.usu_id,
X.usu_name = INSERTED.usu_name,
X.usu_block = INSERTED.usu_block,
X.usu_login = INSERTED.usu_login,
X.usu_password = INSERTED.usu_password
FROM [DATABASE_X].[dbo].[USER] X
INNER JOIN inserted ON X.usu_login = inserted.usu_login
SET NOCOUNT OFF
END
Interesting approach. There are a number of different ways to solve this problem, most of them are better than using triggers.
That said...
As part of your triggered code, create some variables in memory then run a select on the target database to fill them before running your update / insert. Use some simple logic to check that the values aren't already set to those same values, and avoid getting into a loop.
something like this for your update trigger:
USE [DATABASE_Y]
ALTER TRIGGER [dbo].[TR_USER_OnUpdate] ON [dbo].[USER]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE #id as varchar(100)
DECLARE #name as varchar(100)
DECLARE #block as varchar(100)
DECLARE #login as varchar(100)
DECLARE #password as varchar(100)
SELECT #id = X.usu_id,
#name = X.usu_name,
#block = X.usu_block,
#login = X.usu_login,
#password = X.usu_password
FROM [DATABASE_X].[dbo].[USER] X
WHERE X.usu_login = inserted.usu_login
IF #id <> INSERTED.usu_id
OR #name <> INSERTED.usu_name
OR #block <> INSERTED.usu_block
OR #login <> INSERTED.usu_login
OR #password <> INSERTED.usu_password
BEGIN
UPDATE X
SET
X.usu_id = INSERTED.usu_id,
X.usu_name = INSERTED.usu_name,
X.usu_block = INSERTED.usu_block,
X.usu_login = INSERTED.usu_login,
X.usu_password = INSERTED.usu_password
FROM [DATABASE_X].[dbo].[USER] X
INNER JOIN inserted ON X.usu_login = inserted.usu_login
END
SET NOCOUNT OFF
END
I don't know what your datatypes are so I just assumed varchar(100) for everything.
Make sure that all of the columns are NOT NULL. If you have any Nullable columns, make sure you add ISNULL logic to the variable comparisons.
The insert trigger is a little easier, since you just need to check that the userId you're trying to insert doesn't already exist:
USE [DATABASE_Y]
CREATE TRIGGER [dbo].[TR_USER_OnInsert] ON [dbo].[USER]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
IF NOT EXISTS(SELECT 1 FROM [DATABASE_X].[dbo].[USER] WHERE usu_login = INSERTED.usu_login)
BEGIN
INSERT INTO [DATABASE_X].[dbo].[USER] (
usu_id,
usu_name,
usu_block,
usu_login,
usu_password
)
SELECT
usu_id,
usu_name,
usu_block,
usu_login,
usu_password
FROM INSERTED
END
SET NOCOUNT OFF
END

How to convert a query to a a function

I'm playing around with a query from a software partner that i did not create. I'm trying to use it as a function that i can insert into a table, since i'm no sql expert i'm having difficulties with the sintax.
Thanks to your suggestions i'm making progress, now i'm stuck on how to add an additional select to include some join tables, i have disabled that part converting it to a comment. This actually works without the final select but i need the final select, any ideas?
alter function Renetest
(
#companytest nvarchar(8),
#fiscalyeartest int
)
returns #PERIODBALANCE TABLE
(
[Company] NVARCHAR(8)
,[BookID] NVARCHAR(12)
,[BalanceAcct] NVARCHAR(200)
,[FiscalYear] INT
,[FiscalPeriod] INT
,[BalanceType] NVARCHAR(1)
,[SegValue1] NVARCHAR(50)
,[SegValue2] NVARCHAR(50)
,[FiscalYearSuffix] NVARCHAR(8)
,[FiscalCalendarID] NVARCHAR(12)
)
as
begin
Declare #company nvarchar(8);
Declare #fiscalyear INT;
DECLARE #FiscalPeriod INT;
Set #company = 'epic03'
set #Fiscalyear = '2013'
SET #FiscalPeriod=0;
DECLARE #MaxPeriod AS NVARCHAR(20);
SET #MaxPeriod=(
SELECT
MAX([Erp].[GLBookPer].[FiscalPeriod])
FROM
[Erp].[GLBookPer] WITH (NOLOCK)
WHERE
[Erp].[GLBookPer].[Company] IN (#company)
AND [Erp].[GLBookPer].[FiscalYear]=#FiscalYear
);
WHILE #FiscalPeriod<=(#MaxPeriod)
BEGIN
INSERT INTO #PERIODBALANCE
(
[Company]
,[BookID]
,[BalanceAcct]
,[FiscalYear]
,[FiscalPeriod]
,[BalanceType]
,[SegValue1]
,[SegValue2]
,[FiscalYearSuffix]
,[FiscalCalendarID]
)
SELECT
[Erp].[GLPeriodBal].[Company]
,[Erp].[GLPeriodBal].[BookID]
,[Erp].[GLPeriodBal].[BalanceAcct]
,[Erp].[GLPeriodBal].[FiscalYear]
,#FiscalPeriod AS [FiscalPeriod]
,[Erp].[GLPeriodBal].[BalanceType]
,[Erp].[GLPeriodBal].[SegValue1]
,[Erp].[GLPeriodBal].[SegValue2]
,[Erp].[GLPeriodBal].[FiscalYearSuffix]
,[Erp].[GLPeriodBal].[FiscalCalendarID]
FROM
[Erp].[GLPeriodBal] WITH (NOLOCK)
WHERE
[Erp].[GLPeriodBal].[Company] IN (#company)
AND [Erp].[GLPeriodBal].[FiscalYear]=#FiscalYear
AND [Erp].[GLPeriodBal].[FiscalPeriod]<=#FiscalPeriod
AND [Erp].[GLPeriodBal].[BalanceType] IN ('D','B')
SET #FiscalPeriod=#FiscalPeriod+1;
end;
/*
SELECT
LTRIM(RTRIM([PERIODBALANCE].[Company])) AS [Company]
,LTRIM(RTRIM([PERIODBALANCE].[BookID])) AS [BookID]
,LTRIM(RTRIM(REPLACE([PERIODBALANCE].[BalanceAcct],'|','-'))) AS [BalanceAcct]
,LTRIM(RTRIM(ISNULL(NULLIF([PERIODBALANCE].[BalanceType],''),'--'))) AS [BalanceType]
,LTRIM(RTRIM(ISNULL(NULLIF([PERIODBALANCE].[FiscalYearSuffix],''),'--'))) AS [FiscalYearSuffix]
,LTRIM(RTRIM(ISNULL(NULLIF([PERIODBALANCE].[FiscalCalendarID],''),'--'))) AS [FiscalCalendarID]
FROM
#PERIODBALANCE AS [PERIODBALANCE]*/
return
end
go
The function returns a table. Just select from the function with an INSERT statement.
INSERT INTO dbo.MyTable
SELECT Company, BookID
FROM Renetest();
This is strictly what you asked. But this could be converted into a stored procedure and avoid using a function all together.

How to get and use the value returned by a stored procedure to a INSERT INTO... SELECT... statement

I am just new in SQL language and still studying it. I'm having hard time looking for answer on how can I use the stored procedure and insert value into a table.
I have this stored procedure:
CREATE PROCEDURE TestID
AS
SET NOCOUNT ON;
BEGIN
DECLARE #NewID VARCHAR(30),
#GenID INT,
#BrgyCode VARCHAR(5) = '23548'
SET #GenID = (SELECT TOP (1) NextID
FROM dbo.RandomIDs
WHERE IsUsed = 0
ORDER BY RowNumber)
SET #NewID = #BrgyCode + '-' + CAST(#GenID AS VARCHAR (30))
UPDATE dbo.RandomIDs
SET dbo.RandomIDs.IsUsed = 1
WHERE dbo.RandomIDs.NextID = #GenID
SELECT #NewID
END;
and what I'm trying to do is this:
INSERT INTO dbo.Residents([ResidentID], NewResidentID, [ResLogdate],
...
SELECT
[ResidentID],
EXEC TestID ,
[ResLogdate],
....
FROM
source.dbo.Resident;
There is a table dbo.RandomIDs containing random 6 digit non repeating numbers where I'm pulling out the value via the stored procedure and updating the IsUsed column of the table to 1. I'm transferring data from one database to another database and doing some processing on the data while transferring. Part of the processing is generating a new ID with the required format.
But I can't get it to work Sad I've been searching the net for hours now but I'm not getting the information that I need and that the reason for my writing. I hope someone could help me with this.
Thanks,
Darren
your question is little bit confusing, because you have not explained what you want to do. As i got your question, you want to fetch random id from randomids table and after performed some processing on nextid you want to insert it into resident table [newresidentid] and end of the procedure you fetch data from resident table. if i get anything wrong feel free to ask me.
your procedure solution is following.
CREATE PROCEDURE [TestId]
AS
SET NOCOUNT ON;
BEGIN
DECLARE #NEWID NVARCHAR(30)
DECLARE #GENID BIGINT
DECLARE #BRGYCODE VARCHAR(5) = '23548'
DECLARE #COUNT INTEGER
DECLARE #ERR NVARCHAR(20) = 'NO IDS IN RANDOM ID'
SET #COUNT = (SELECT COUNT(NEXTID) FROM RandomIds WHERE [IsUsed] = 0)
SET #GENID = (SELECT TOP(1) [NEXTID] FROM RandomIds WHERE [IsUsed] = 0 ORDER BY [ID] ASC)
--SELECT #GENID AS ID
IF #COUNT = 0
BEGIN
SELECT #ERR AS ERROR
END
ELSE
BEGIN
SET #NEWID = #BRGYCODE + '-' + CAST(#GENID AS varchar(30))
UPDATE RandomIds SET [IsUsed] = 1 WHERE [NextId] = #GENID
INSERT INTO Residents ([NewResidentId] , [ResLogDate] ) VALUES (#NEWID , GETDATE())
SELECT * FROM Residents
END
END
this procedure will fetch data from your randomids table and perform some processing on nextid than after it directs insert it into resident table and if you want to insert some data through user you can use parameter after declaring procedure name
E.G
CREATE PROCEDURE [TESTID]
#PARAM1 DATATYPE,
#PARAM2 DATATYPE
AS
BEGIN
END
I'm not convinced that your requirement is a good one but here is a way to do it.
Bear in mind that concurrent sessions will not be able to read your update until it is committed so you have to kind of "lock" the update so you will get a block until you're going to commit or rollback. This is rubbish for concurrency, but that's a side effect of this requirement.
declare #cap table ( capturedValue int);
declare #GENID int;
update top (1) RandomIds set IsUsed=1
output inserted.NextID into #cap
where IsUsed=0;
set #GENID =(select max( capturedValue) from #cap )
A better way would be to use an IDENTITY or SEQUENCE to solve your problem. This would leave gaps but help concurrency.

Table-Valued Parameters not visible

For a reason unknown to me, I cannot access the contents of my TVP. The debugger says #_AccountList and #AccountTVP are tables, but I cannot view the contents, print them, nor will intellisense offer me auto completion for a column. I feel like probably declared something wrong or have an ambiguous definition. Any suggestions?
CREATE TYPE AccountList
AS TABLE
(
AccountNumber varchar(50),
AccountType varchar(50)
)
GO
CREATE PROCEDURE [dbo].[updateNumbers_ArchiveDB]
#_AccountList AccountList READONLY,
#padding int,
#proc_dateStart datetime,
#proc_dateEnd datetime
AS
DECLARE #AccountTVP AS AccountList;
BEGIN
SET NOCOUNT ON;
SELECT * FROM #AccountTVP;
UPDATE dbo.Archive2007001
SET LogicalAccount = #padding + AccountNumber
FROM dbo.Archive2007001 INNER JOIN #AccountTVP AS tvp
ON dbo.Archive2007001.LogicalAccount = tvp.AccountNumber
WHERE ProcessDate BETWEEN #proc_dateStart AND #proc_dateEnd
UPDATE dbo.DailyArchive
SET LogicalAccount = #padding + AccountNumber
FROM dbo.DailyArchive INNER JOIN #AccountTVP AS tvp
ON dbo.DailyArchive.LogicalAccount = tvp.AccountNumber
WHERE ProcessDate BETWEEN #proc_dateStart AND #proc_dateEnd
-- does not work PRINT N'tvp.AccountNumber is ' + #AccountTVP.AccountNumber
END
Here is how I am executing the procedure.
declare #p1 dbo.AccountList
insert into #p1 values(N'Account Number',N'Account Type')
insert into #p1 values(N'7463689',N'Basic')
insert into #p1 values(N'1317893',N'Premium')
insert into #p1 values(N'2806127',N'Basic')
exec updateNumbers_ArchiveDB
#_AccountList=#p1,
#padding=N'111',
#proc_dateStart='2008-01-04 11:24:46',
#proc_dateEnd='2008-01-04 11:24:46'
Answer: The data I was looking for was loaded in #_AccountList, not #AccountTVP.
#AccountTVP.AccountNumber represents a row of data, not a single value, so trying to print it will not work.
You should be able to see the incoming values by using a SELECT statement:
SELECT * FROM #_AccountList;
It seems like your SELECT statement from the top of the stored procedure should let you see the values, however, you have not actually set any values in #AccountTVP so it would be empty.
I am not sure but I suspect the debugger may not work because AccountList is a custom type.

Can't insert a second row into a table though it insert first row by stored procedure

It inserted a first row successfully but it's not inserting any other row, though second row has no conflict of primary key
Code in my aspx.cs file:
outputParVal = sqlCmd.Parameters[outputParName].Value;
outparameter in stored procedure is--- "Result"
CREATE PROCEDURE [dbo].[RecruiterProfileInsert]
#CompanyId int,
#CompanyName varchar(200),
#EmailId varchar(50) ,
#Password varchar(20) ,
#ContactNumber varchar(15),
#Website varchar(50),
#CompanyProfile varchar(2000),
#IsVerified bit,
#Result Tinyint OutPut
--#CreatedDate datetime ,
--UpdatedDate datetime
AS
BEGIN
-- Insert statements for procedure here
--check whether #CompanyName already exist or not if exist then return
IF EXISTS(SELECT Top 1 * FROM RecruiterProfile WHERE #CompanyId = LTRIM(RTRIM(#CompanyId)))
BEGIN
SET #Result = 0-- Already Exists
END
ELSE
BEGIN
INSERT INTO RecruiterProfile
(
CompanyId,
CompanyName,
EmailId ,
Password ,
ContactNumber,
Website ,
CompanyProfile ,
IsVerified,
CreatedDate
)
VALUES
(
#CompanyId,
#CompanyName,
#EmailId ,
#Password,
#ContactNumber,
#Website,
#CompanyProfile,
#IsVerified,
GetDate()
)
set #Result =1
return
END
END
This is the problem:
SELECT Top 1 * FROM RecruiterProfile WHERE #CompanyId = LTRIM(RTRIM(#CompanyId))
This inherently makes no sense. You're comparing the variable to itself. Take the # sign out of one of the CompanyId references. The RTrim is unnecessary in SQL Server, and the LTrim doesn't make sense either because the later insert doesn't also LTrim so something is going to go wrong eventually.
Furthermore, inside of an EXISTS clause, TOP makes no sense unless you are using ORDER BY and doing something with the final result. Just do SELECT * inside of EXISTS clauses.
One more thing: if there is high concurrency and users could possibly try to insert the same thing at the same time, your query could still fail on a duplicate key violation.

Resources