Locking From Temp Table - SQL 2008 R2 - sql

I have just implemented a new database on our live environment, this database is fed information by a service brokers from our two transactional databases. When the messages come into the new database we have a series of stored procedures which manipulate the feed for each specific user.
I also have another set of stored procedures which re-manipulate the data by user instead of by vehicle, these are used by our maintenance screens to ensure we can change users visibility of the data.
The below stored procedure keeps locking up I have now amended this sp to update a temp table first and then just undertake one update against the main database, however this has not aided the situation, I have also amended so I can update in smaller batches and if this fails keep retrying until it is successful. This works 100% on our dev environment but on live it keeps locking. The throughput on the servicebrokers is not great enough to register the number of failures, I therefore believe I am locking myself within this sp?
I have included ---'THIS UPDATE KEEPS LOCKING' at the point of failure.
What could be causing this locking behavior?
USE [Isight]
GO
/** Object: StoredProcedure [dbo].[UserVisibilityForVehicles] Script Date: 07/18/2014 14:43:04 **/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[UserVisibilityForVehicles]
-- Add the parameters for the stored procedure here
#Username VARCHAR(50)
WITH RECOMPILE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
PRINT 'UserVisibilityForVehicles Started'
-- Now Start to check security for user.
IF EXISTS ( SELECT ID FROM dbo.SecurityTable WHERE userid = #Username AND Deleted = 0)
BEGIN
CREATE TABLE #VehicleToUsers(ID BIGINT, NewRecord BIT DEFAULT(0))
CREATE CLUSTERED INDEX IDX_VehicleToUsers ON #VehicleToUsers(ID)
CREATE NonCLUSTERED INDEX IDX_NewRecord ON #VehicleToUsers(ID)
INSERT INTO #VehicleToUsers
( ID )
(
SELECT Distinct Veh.[ID]
FROM [iSight].[dbo].[Vehicle] Veh WITH (NOLOCK)
INNER JOIN SecurityTable WITH (NOLOCK) ON Veh.[System] = SecurityTable.[System]
WHERE SecurityType = 1 AND UserID = #Username AND SecurityTable.deleted = 0
)
INSERT INTO #VehicleToUsers
( ID )
(
SELECT DISTINCT Veh.[ID]
FROM [iSight].[dbo].[Vehicle] Veh WITH (NOLOCK)
INNER JOIN SecurityTable WITH (NOLOCK) ON Veh.[System] = SecurityTable.[System] AND Veh.CurrentSite = SecurityTable.[Site]
WHERE SecurityType = 2 AND UserID = #Username AND SecurityTable.deleted = 0
)
BEGIN
PRINT 'UserVisibilityForVehicles: ' + #Username
INSERT INTO #VehicleToUsers
( ID )
(
SELECT DISTINCT Vehicle.ID
FROM Manufacturer WITH (NOLOCK) INNER JOIN
ManufacturerAgreementSubcustomer WITH (NOLOCK) ON Manufacturer.ID = ManufacturerAgreementSubcustomer.ManufacturerID INNER JOIN
ManufacturerMake WITH (NOLOCK) ON Manufacturer.ID = ManufacturerMake.ManufacturerID INNER JOIN
Vehicle WITH (NOLOCK) ON ManufacturerMake.Make = Vehicle.Make AND ManufacturerAgreementSubcustomer.Agreement = Vehicle.CurrentAgreement INNER JOIN
SecurityTable WITH (NOLOCK) ON Manufacturer.ManufacturerGroupID = SecurityTable.ManufacturerGroupID AND Vehicle.System = SecurityTable.System
WHERE (SecurityTable.SecurityType = 3) AND (SecurityTable.UserID = #Username) AND ManufacturerMake.Deleted = 0 AND ManufacturerAgreementSubcustomer.Deleted = 0 AND SecurityTable.deleted = 0
)
INSERT INTO #VehicleToUsers
( ID )
(
SELECT DISTINCT Vehicle.ID
FROM Manufacturer WITH (NOLOCK) INNER JOIN
ManufacturerAgreementSubcustomer WITH (NOLOCK) ON Manufacturer.ID = ManufacturerAgreementSubcustomer.ManufacturerID INNER JOIN
ManufacturerMake WITH (NOLOCK) ON Manufacturer.ID = ManufacturerMake.ManufacturerID INNER JOIN
Vehicle WITH (NOLOCK) ON ManufacturerMake.Make = Vehicle.Make AND ManufacturerAgreementSubcustomer.Agreement = Vehicle.CurrentAgreement INNER JOIN
SecurityTable WITH (NOLOCK) ON Vehicle.System = SecurityTable.System AND Manufacturer.ID = SecurityTable.ManufacturerID
WHERE (SecurityTable.SecurityType = 4) AND (SecurityTable.UserID = #Username) AND ManufacturerMake.Deleted = 0 AND ManufacturerAgreementSubcustomer.Deleted = 0 AND SecurityTable.deleted = 0
)
INSERT INTO #VehicleToUsers
( ID )
(
SELECT DISTINCT Vehicle.ID
FROM Manufacturer WITH (NOLOCK) INNER JOIN
ManufacturerAgreementSubcustomer WITH (NOLOCK) ON Manufacturer.ID = ManufacturerAgreementSubcustomer.ManufacturerID INNER JOIN
ManufacturerMake WITH (NOLOCK) ON Manufacturer.ID = ManufacturerMake.ManufacturerID INNER JOIN
Vehicle WITH (NOLOCK) ON ManufacturerMake.Make = Vehicle.Make AND ManufacturerAgreementSubcustomer.Agreement = Vehicle.CurrentAgreement INNER JOIN
SecurityTable WITH (NOLOCK) ON Vehicle.System = SecurityTable.System AND Manufacturer.ID = SecurityTable.ManufacturerID AND
ManufacturerAgreementSubcustomer.ID = SecurityTable.ManufacturerAgreementSub INNER JOIN
ManufacturerUserAgreementSubcustomer WITH (NOLOCK) ON
ManufacturerAgreementSubcustomer.ID = ManufacturerUserAgreementSubcustomer.ManufacturerAgreementSubcustomerID AND
SecurityTable.ManufacturerID = ManufacturerUserAgreementSubcustomer.ManufacturerID AND
SecurityTable.UserID = ManufacturerUserAgreementSubcustomer.UserName
WHERE (SecurityTable.SecurityType = 5) AND (SecurityTable.UserID = #Username) AND (ManufacturerMake.Deleted = 0) AND
(ManufacturerAgreementSubcustomer.Deleted = 0) AND (ManufacturerUserAgreementSubcustomer.Deleted = 0) AND SecurityTable.deleted = 0
)
INSERT INTO #VehicleToUsers
( ID )
(
SELECT DISTINCT Vehicle.ID
FROM Manufacturer WITH (NOLOCK) INNER JOIN
ManufacturerAgreementSubcustomer WITH (NOLOCK) ON Manufacturer.ID = ManufacturerAgreementSubcustomer.ManufacturerID INNER JOIN
ManufacturerMake WITH (NOLOCK) ON Manufacturer.ID = ManufacturerMake.ManufacturerID INNER JOIN
Vehicle WITH (NOLOCK) ON ManufacturerMake.Make = Vehicle.Make AND ManufacturerAgreementSubcustomer.Agreement = Vehicle.CurrentAgreement AND
ManufacturerAgreementSubcustomer.Subcustomer = Vehicle.CurrentSubCustomer INNER JOIN
SecurityTable WITH (NOLOCK) ON Vehicle.System = SecurityTable.System AND Manufacturer.ID = SecurityTable.ManufacturerID AND
ManufacturerAgreementSubcustomer.ID = SecurityTable.ManufacturerAgreementSub INNER JOIN
ManufacturerUserAgreementSubcustomer WITH (NOLOCK) ON
ManufacturerAgreementSubcustomer.ID = ManufacturerUserAgreementSubcustomer.ManufacturerAgreementSubcustomerID AND
SecurityTable.UserID = ManufacturerUserAgreementSubcustomer.UserName AND
SecurityTable.ManufacturerID = ManufacturerUserAgreementSubcustomer.ManufacturerID
WHERE (SecurityTable.SecurityType = 6) AND (SecurityTable.UserID = #Username) AND (ManufacturerMake.Deleted = 0) AND
(ManufacturerAgreementSubcustomer.Deleted = 0) AND (ManufacturerUserAgreementSubcustomer.Deleted = 0) AND SecurityTable.deleted = 0
)
END
CREATE TABLE #VehicleToUserCopy(ID BIGINT, vehicleTableID BIGINT, Deleted BIT DEFAULT(1), UpdatedAt DATETIME DEFAULT (GETDATE()), UpdatedBy VARCHAR(50) DEFAULT('UserVisibilityForVehicles-Update'), NextToUpdate BIT DEFAULT(0))
CREATE CLUSTERED INDEX idx_ID ON #VehicleToUserCopy(ID)
CREATE NONCLUSTERED INDEX idx_VehicleTableID ON #VehicleToUserCopy(vehicleTableID)
CREATE NONCLUSTERED INDEX idx_NextToUpdate ON #VehicleToUserCopy(NextToUpdate)
INSERT INTO #VehicleToUserCopy
( ID ,
vehicleTableID ,
Deleted
)
(
SELECT ID, vehicleTableID, Deleted
FROM dbo.VehicleToUser WITH (nolock)
WHERE Username = #Username
)
PRINT 'Starting to do updates'
--Not required as default set to 1
----UPDATE VehicleToUser
----SET DELETED = 1
----,UpdatedAt = GETDATE()
----,UpdatedBy = 'UserVisibilityForVehicles'
----FROM dbo.VehicleToUser WITH (NOLOCK)
----LEFT JOIN #VehicleToUsers AS UsersVehicles ON VehicleToUser.VehicleTableID = UsersVehicles.ID
----WHERE UserName = #Username AND UsersVehicles.ID IS null
PRINT 'Starting to do updates - Set Deleted = 0'
SET LOCK_TIMEOUT 1000 -- set to second
DECLARE #Tries tinyint
UPDATE #VehicleToUserCopy
SET Deleted = 0
FROM #VehicleToUserCopy AS VehicleToUserCopy
inner JOIN #VehicleToUsers AS UsersVehicles ON VehicleToUserCopy.VehicleTableID = UsersVehicles.ID
INSERT INTO VehicleToUser(UserName, VehicleTableID, DELETED, UpdatedAt, UpdatedBy)
(
SELECT DISTINCT #Username, TempVehicle.ID, 0 , GETDATE(), 'UserVisibilityForVehicles-Insert'
FROM #VehicleToUsers AS TempVehicle
LEFT JOIN (
SELECT VehicleTableID
FROM #VehicleToUserCopy WITH (NOLOCK)
) AS [VehicleToUser] ON TempVehicle.ID = [VehicleToUser].VehicleTableID
WHERE [VehicleToUser].VehicleTableID IS null
)
DECLARE #ID bigint
SELECT #ID = ID FROM #VehicleToUserCopy
WHILE ##rowcount > 0
BEGIN
SET ROWCOUNT 1000
SELECT #Tries = 1
WHILE #Tries <= 3
BEGIN
BEGIN TRANSACTION
BEGIN TRY
UPDATE #VehicleToUserCopy SET NextToUpdate = 1
---'THIS UPDATE KEEPS LOCKING'
UPDATE dbo.VehicleToUser
SET Deleted = VehicleToUserCopy.Deleted
, UpdatedAt = GETDATE()
, UpdatedBy = VehicleToUserCopy.UpdatedBy
FROM VehicleToUser
inner JOIN #VehicleToUserCopy AS VehicleToUserCopy ON VehicleToUser.ID = VehicleToUserCopy.ID
WHERE VehicleToUserCopy.NextToUpdate = 1
PRINT 'WORKED'
DELETE FROM #VehicleToUserCopy WHERE NextToUpdate = 1
COMMIT
-- therefore we can leave our loop
BREAK
END TRY
BEGIN CATCH
ROLLBACK --always rollback
PRINT 'Rolled Back '
-- Now check for Blocking errors 1222 or Deadlocks 1205 and if its a deadlock wait for a while to see if that helps
SELECT ERROR_MESSAGE()
IF ERROR_NUMBER() = 1205 OR ERROR_NUMBER() = 1222
BEGIN
-- if its a deadlock wait 2 seconds then try again
IF ERROR_NUMBER() = 1205
BEGIN -- wait 2 seconds to see if that helps the deadlock
WAITFOR DELAY '00:00:02'
END
-- no need to wait for anything for BLOCKING ERRORS as our LOCK_TIMEOUT is going to wait for half a second anyway
-- and if it hasn't finished by then (500ms x 3 attempts = 1.5 seconds) there is no point waiting any longer
END
SELECT #Tries = #Tries + 1 -- increment and try again for 3 goes
-- we carry on until we reach our limit i.e 3 attempts
CONTINUE
END CATCH
END
SELECT #ID = ID FROM #VehicleToUserCopy
End
SET ROWCOUNT 0
DROP TABLE #VehicleToUsers
END
ELSE
BEGIN
DELETE FROM dbo.VehicleToUser WHERE username = #Username
DELETE FROM dbo.VehicleToUser_UserCurrentImageCount WHERE username = #Username
DELETE FROM dbo.VehicleToUser_UsersCurrentVehicles WHERE username = #Username
End

Try changing your update slightly. I have seen some inconsistent results when using an update like this and you update the base table. You should instead update the alias.
UPDATE v
SET Deleted = VCopy.Deleted
, UpdatedAt = GETDATE()
, UpdatedBy = VCopy.UpdatedBy
FROM VehicleToUser v
inner JOIN #VehicleToUserCopy AS VCopy ON v.ID = VCopy.ID
WHERE VCopy.NextToUpdate = 1

Related

SQL Update trigger requirement

I want to write a trigger which updates some fields in another master table record in TRN_PT_TESTS_DET table gets updated for a specific test.
EG. when driving license in PTD_READING_TEXT column is updated in TRN_PT_TESTS_DET table, we want to update PT_DRIVER_LIC column in MST_PATIENT where PT_ID = ENC_PT_ID, which will also look in TST_CODE = 'SC32105' in MST_TESTS table (code 'SC32105' stands for driving license).
Reference query:
select top 10 ENC_PT_ID, PTD_READING_TEXT
from trn_pt_tests_Det
inner join trn_pt_tests_head on ptd_pth_id = pth_id
inner join trn_encounters on pth_enc_id = enc_id
where ptd_test_id = (select TST_ID from MST_TESTS where TST_CODE = 'SC32105');
While doing that we also want to add a record in TRN_NOTES with TN_TYPE = 'PC', TN_OBJECT_ID = PT_ID and TN_NOTE =
"Updated Patient <PT_LAST_NAME>, <PT_FIRST_NAME> Driver License Old:XXX New:YYYY"
I have created a below trigger which is not updating the PT_DRIVER_LIC column in MST_PATIENT and also inserting incorrect record into TRN_NOTES table.
Can someone please tell me what I am doing wrong?
When I execute below update query.
update TRN_PT_TESTS_DET
set PTD_READING_TEXT = 'aaaaa'
where PTD_TEST_ID = 11025
and PTD_PTH_ID = 134
the value is updated and trigger is called but #pt_id, #enc_id, #pt_last_name and #pt_first_name declared variable in trigger fetch different ID's and insert different values in TRN_NOTES table and the update MST_PATIENT table part don't work at all.
Can some please review below trigger and help me to fin out what I am doing wrong.
Trigger:
CREATE TRIGGER update_driver_lic_and_notes
ON TRN_PT_TESTS_DET
AFTER UPDATE
AS
IF (UPDATE (PTD_READING_TEXT))
BEGIN
IF EXISTS (SELECT 1
FROM trn_pt_tests_Det
INNER JOIN trn_pt_tests_head ON ptd_pth_id = pth_id
INNER JOIN trn_encounters ON pth_enc_id = enc_id
WHERE PTH_ID = PTD_PTH_ID
AND EXISTS (SELECT 1
FROM MST_TESTS
WHERE TST_CODE = 'VTWTLBS'))
BEGIN
DECLARE #old_dl varchar(255);
DECLARE #new_dl varchar(255);
DECLARE #pt_id int;
DECLARE #enc_id int;
DECLARE #pt_last_name varchar(255);
DECLARE #pt_first_name varchar(255);
SELECT #old_dl = PT_DRIVER_LIC
FROM MST_PATIENT
INNER JOIN trn_encounters ON PT_ID = ENC_PT_ID
INNER JOIN trn_pt_tests_head ON pth_enc_id = enc_id
INNER JOIN TRN_PT_TESTS_DET ON ptd_pth_id = pth_id
WHERE ptd_test_id = (SELECT TST_ID FROM MST_TESTS
WHERE TST_CODE = 'VTWTLBS');;
SELECT #new_dl = PTD_READING_TEXT,
#pt_id = ENC_PT_ID,
#enc_id = enc_id,
#pt_last_name = PT_LNAME,
#pt_first_name = PT_FNAME
FROM MST_PATIENT
INNER JOIN trn_encounters ON PT_ID = ENC_PT_ID
INNER JOIN trn_pt_tests_head ON pth_enc_id = enc_id
INNER JOIN TRN_PT_TESTS_DET ON ptd_pth_id = pth_id
WHERE ptd_test_id = (SELECT TST_ID FROM MST_TESTS
WHERE TST_CODE = 'VTWTLBS');
-- IF (#old_dl != #new_dl)
UPDATE MST_PATIENT
SET PT_DRIVER_LIC = #new_dl
WHERE PT_ID = #pt_id;
INSERT INTO TRN_NOTES (TN_TYPE, TN_OBJECT_ID, TN_ENC_ID, TN_MOD_USER, TN_NOTES, TN_MOD_TIMESTAMP)
VALUES ('PC', #pt_id, #enc_id,0, 'Updated Patient ' + #pt_last_name + ', ' + #pt_first_name + ' Driver License Old:' + #old_dl + ' New:' + #new_dl, GETDATE());
END
END
Thank you.

Updating table based on derived column as well as returning the resultset of the stored procedure

I have written a SQL Server stored procedure that returns a derived column in its result set. The derived column returns the difference in days and if the difference is less than 24 hours, then it returns hours.
I need to call an update statement based on value that comes from the derived column (NoOfDays) is -1: The storedprocedure would however return the entire resultset that includes the derived column and perform the update if necessary
update [org].[User]
set [Disabled] = 0
where id = #UserID AND ....
How do include this update statement to do that in this stored procedure
Stored procedure:
CREATE PROCEDURE declaration.UserAgreementsOutstanding
(#UserID INT)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
UPDATE ua
SET ua.AcceptanceWindowExpiry = GETUTCDATE() + a.ReviewPeriodInDays
FROM declaration.UserAgreement ua
INNER JOIN declaration.Agreement a ON ua.AgreementID = a.ID
WHERE ua.USERID = #UserID
AND ua.AcceptanceWindowExpiry IS NULL;
SELECT
ua.ID AS UserAgreementID,
A.ID AS AgreementID,
A.Code,
A.ComplianceCode,
A.Name, A.Description,
A.Version,
ua.UserAgreementStateID,
uas.Name AS UserAgreementStateName,
ua.AcceptanceWindowExpiry,
declaration.GetDifferenceInDaysOrHours(ua.AcceptanceWindowExpiry) AS NoOfDays,
A.Data,
pa.ID AS AuthoredByID,
pa.FirstName + ' ' + pa.LastName AS AuthoredByName,
A.Authored,
ia.ID AS IssuedByID,
ia.FirstName + ' ' + pa.LastName AS IssuedByName,
A.Issued
FROM
declaration.Agreement AS A
INNER JOIN
declaration.UserAgreement AS ua ON A.ID = ua.AgreementID
INNER JOIN
declaration.UserAgreementState AS uas ON ua.UserAgreementStateID = uas.ID
LEFT JOIN
common.Person AS pa ON A.AuthoredBy = pa.ID
LEFT JOIN
common.Person AS ia ON A.IssuedBy = ia.ID
WHERE
ua.UserID = 607
AND uas.Code IN ('ISS', 'DEF') -- Issued, Deferred
AND A.Draft = CONVERT(BIT, 0) -- Not a draft.
AND A.Deleted = CONVERT(BIT, 0) -- Not deleted.
AND (A.Issued <= GETUTCDATE() OR A.Issued IS NULL)
AND (A.Expires > GETUTCDATE() OR A.Expires IS NULL)
END TRY
BEGIN CATCH
-- do some pseudo logging
PRINT ERROR_MESSAGE();
THROW;
END CATCH;
END;
Function
CREATE FUNCTION declaration.GetDifferenceInDaysOrHours(#AcceptanceWindowExpiry datetime)
RETURNS int
AS
BEGIN
DECLARE #timeDifferenceInDays INT;
DECLARE #timeDifferenceInHours INT;
DECLARE #timeDifference INT;
SELECT #timeDifferenceInDays = DATEDIFF(d, GETUTCDATE(), #AcceptanceWindowExpiry)
IF #timeDifferenceInDays > 1
BEGIN
SELECT #timeDifference = #timeDifferenceInDays
END
ELSE
BEGIN
SELECT #timeDifferenceInHours = DATEDIFF(HOUR, GETUTCDATE(), #AcceptanceWindowExpiry)
IF #timeDifferenceInHours >= 0 AND #timeDifferenceInHours <= 24
BEGIN
SELECT #timeDifference = #timeDifferenceInHours
END
ELSE
BEGIN
SELECT #timeDifference = -1
END
END
RETURN #timeDifference;
END;
The current resultset
You can add UPDATE statement to disable the user before updating the user agreement. Also, have the transaction scope.
BEGIN TRY
BEGIN TRANSACTION;
DECLARE #OutStandingUserAgreements TABLE(UserID INT, AgreementID INT, TimeDifference INT)
INSERT INTO #OutStandingUserAgreements(UserID, AgreementID , TimeDifference)
SELECT U.Id
, UA.AgreementID
, declaration.UserAgreementsOutstanding(UA.AcceptanceWindowExpiry) AS TimeDifference
FROM [org].[User] AS U
INNER JOIN declaration.UserAgreement AS UA
ON U.UserID = UA.UserID
where U.id = #UserID;
-- We are disabling user here, based on the UserAgreement Status
UPDATE U
SET U.[Disabled] = 0
FROM [org].[User] AS U
WHERE EXISTS (SELECT 1 FROM
#OutStandingUserAgreements AS oua
WHERE oua.UserID = U.ID
AND oua.TimeDifference = -1);
UPDATE ua
SET ua.AcceptanceWindowExpiry = GETUTCDATE() + a.ReviewPeriodInDays
FROM declaration.UserAgreement ua
INNER JOIN declaration.Agreement a ON ua.AgreementID = a.ID
WHERE ua.USERID = #UserID
AND ua.AcceptanceWindowExpiry IS NULL;
IF EXISTS(SELECT 1 FROM Org.User WHERE Id = #UserID AND Disabled = 0)
BEGIN
SELECT -1;
END
ELSE
BEGIN
SELECT
ua.ID AS UserAgreementID,
A.ID AS AgreementID,
A.Code,
A.ComplianceCode,
A.Name, A.Description,
A.Version,
ua.UserAgreementStateID,
uas.Name AS UserAgreementStateName,
ua.AcceptanceWindowExpiry,
oua.TimeDifference AS NoOfDays,
A.Data,
pa.ID AS AuthoredByID,
pa.FirstName + ' ' + pa.LastName AS AuthoredByName,
A.Authored,
ia.ID AS IssuedByID,
ia.FirstName + ' ' + pa.LastName AS IssuedByName,
A.Issued
FROM
declaration.Agreement AS A
INNER JOIN
declaration.UserAgreement AS ua ON A.ID = ua.AgreementID
INNER JOIN
declaration.UserAgreementState AS uas ON ua.UserAgreementStateID = uas.ID
INNER JOIN #OutStandingUserAgreements AS oua
ON oua.AgreementID = ua.AgreementID
LEFT JOIN
common.Person AS pa ON A.AuthoredBy = pa.ID
LEFT JOIN
common.Person AS ia ON A.IssuedBy = ia.ID
WHERE
ua.UserID = 607
AND uas.Code IN ('ISS', 'DEF') -- Issued, Deferred
AND A.Draft = CONVERT(BIT, 0) -- Not a draft.
AND A.Deleted = CONVERT(BIT, 0) -- Not deleted.
AND (A.Issued <= GETUTCDATE() OR A.Issued IS NULL)
AND (A.Expires > GETUTCDATE() OR A.Expires IS NULL)
END
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH

Splitting SQL query with many joins into smaller ones helps?

We need to do some reporting every night on our SQL server 2008R2. Calculating the reports takes several hours. In order to shorten the time we precalculate a table. This table is created based on JOINining 12 quite big (tens of milions row) tables.
The calculation of this aggregation table took until few days ago cca 4 hours. Our DBA than split this big join into 3 smaller joins (each joining 4 tables). The temporar result is everytime saved into a temporary table, which is used in the next join.
The result of the DBA enhancment is, that the aggregation table is calculated in 15 minutes. I wondered how is that possible. DBA told me that it is because the number of data the server must process is smaller. In other words, that in the big original join the server has to work with more data than in summed smaller joins. However, I would presume that optimizer would take care of doing it effeciently with the original big join, splitting the joins on its own and sending only the number of columns needed to next joins.
The other thing he has done is that he created an index on one of the tmeporary tables. However, once again I would think that the optimizer will create the appropriate hash tables if needed and alltogether better optimize the computation.
I talked about this with our DBA, but he was himself uncertain about what cased the improvement in processing time. He just mentioned, that he would not blame the server as it can be overwhelming to compute such big data and that it is possible that the optimizer has hard time to predict the best execution plan ... . This I understand, but I would like to have more defining answer as to exactly why.
So, the questions are:
1. "What could possibly cause the big improvement?"
2. "Is it a standard procedure to split big joins into smaller?"
3. "Is the amount of data which srever has to process really smaller in case of multiple smaller joins?"
Here is the original query:
Insert Into FinalResult_Base
SELECT
TC.TestCampaignContainerId,
TC.CategoryId As TestCampaignCategoryId,
TC.Grade,
TC.TestCampaignId,
T.TestSetId
,TL.TestId
,TSK.CategoryId
,TT.[TestletId]
,TL.SectionNo
,TL.Difficulty
,TestletName = Char(65+TL.SectionNo) + CONVERT(varchar(4),6 - TL.Difficulty)
,TQ.[QuestionId]
,TS.StudentId
,TS.ClassId
,RA.SubjectId
,TQ.[QuestionPoints]
,GoodAnswer = Case When TQ.[QuestionPoints] Is null Then 0
When TQ.[QuestionPoints] > 0 Then 1
Else 0 End
,WrongAnswer = Case When TQ.[QuestionPoints] = 0 Then 1
When TQ.[QuestionPoints] Is null Then 1
Else 0 End
,NoAnswer = Case When TQ.[QuestionPoints] Is null Then 1 Else 0 End
,TS.Redizo
,TT.ViewCount
,TT.SpentTime
,TQ.[Position]
,RA.SpecialNeeds
,[Version] = 1
,TestAdaptationId = TA.Id
,TaskId = TSK.TaskId
,TaskPosition = TT.Position
,QuestionRate = Q.Rate
,TestQuestionId = TQ.Guid
,AnswerType = TT.TestletAnswerTypeId
FROM
[TestQuestion] TQ WITH (NOLOCK)
Join [TestTask] TT WITH (NOLOCK) On TT.Guid = TQ.TestTaskId
Join [Question] Q WITH (NOLOCK) On TQ.QuestionId = Q.QuestionId
Join [Testlet] TL WITH (NOLOCK) On TT.TestletId = TL.Guid
Join [Test] T WITH (NOLOCK) On TL.TestId = T.Guid
Join [TestSet] TS WITH (NOLOCK) On T.TestSetId = TS.Guid
Join [RoleAssignment] RA WITH (NOLOCK) On TS.StudentId = RA.PersonId And RA.RoleId = 1
Join [Task] TSK WITH (NOLOCK) On TSK.TaskId = TT.TaskId
Join [Category] C WITH (NOLOCK) On C.CategoryId = TSK.CategoryId
Join [TimeWindow] TW WITH (NOLOCK) On TW.Id = TS.TimeWindowId
Join [TestAdaptation] TA WITH (NOLOCK) On TA.Id = TW.TestAdaptationId
Join [TestCampaign] TC WITH (NOLOCK) On TC.TestCampaignId = TA.TestCampaignId
WHERE
T.TestTypeId = 1 -- eliminuji ankety
And t.ProcessedOn is not null -- ne vsechny, jen dokoncene
And TL.ShownOn is not null
And TS.Redizo not in (999999999, 111111119)
END;
The new splitted joins after DBA great work:
SELECT
TC.TestCampaignContainerId,
TC.CategoryId As TestCampaignCategoryId,
TC.Grade,
TC.TestCampaignId,
T.TestSetId
,TL.TestId
,TL.SectionNo
,TL.Difficulty
,TestletName = Char(65+TL.SectionNo) + CONVERT(varchar(4),6 - TL.Difficulty) -- prevod na A5, B4, B5 ...
,TS.StudentId
,TS.ClassId
,TS.Redizo
,[Version] = 1 -- ?
,TestAdaptationId = TA.Id
,TL.Guid AS TLGuid
,TS.TimeWindowId
INTO
[#FinalResult_Base_1]
FROM
[TestSet] [TS] WITH (NOLOCK)
JOIN [Test] [T] WITH (NOLOCK)
ON [T].[TestSetId] = [TS].[Guid] AND [TS].[Redizo] NOT IN (999999999, 111111119) AND [T].[TestTypeId] = 1 AND [T].[ProcessedOn] IS NOT NULL
JOIN [Testlet] [TL] WITH (NOLOCK)
ON [TL].[TestId] = [T].[Guid] AND [TL].[ShownOn] IS NOT NULL
JOIN [TimeWindow] [TW] WITH (NOLOCK)
ON [TW].[Id] = [TS].[TimeWindowId] AND [TW].[IsActive] = 1
JOIN [TestAdaptation] [TA] WITH (NOLOCK)
ON [TA].[Id] = [TW].[TestAdaptationId] AND [TA].[IsActive] = 1
JOIN [TestCampaign] [TC] WITH (NOLOCK)
ON [TC].[TestCampaignId] = [TA].[TestCampaignId] AND [TC].[IsActive] = 1
JOIN [TestCampaignContainer] [TCC] WITH (NOLOCK)
ON [TCC].[TestCampaignContainerId] = [TC].[TestCampaignContainerId] AND [TCC].[IsActive] = 1
;
SELECT
FR1.TestCampaignContainerId,
FR1.TestCampaignCategoryId,
FR1.Grade,
FR1.TestCampaignId,
FR1.TestSetId
,FR1.TestId
,TSK.CategoryId AS [TaskCategoryId]
,TT.[TestletId]
,FR1.SectionNo
,FR1.Difficulty
,TestletName = Char(65+FR1.SectionNo) + CONVERT(varchar(4),6 - FR1.Difficulty) -- prevod na A5, B4, B5 ...
,FR1.StudentId
,FR1.ClassId
,FR1.Redizo
,TT.ViewCount
,TT.SpentTime
,[Version] = 1 -- ?
,FR1.TestAdaptationId
,TaskId = TSK.TaskId
,TaskPosition = TT.Position
,AnswerType = TT.TestletAnswerTypeId
,TT.Guid AS TTGuid
INTO
[#FinalResult_Base_2]
FROM
#FinalResult_Base_1 FR1
JOIN [TestTask] [TT] WITH (NOLOCK)
ON [TT].[TestletId] = [FR1].[TLGuid]
JOIN [Task] [TSK] WITH (NOLOCK)
ON [TSK].[TaskId] = [TT].[TaskId] AND [TSK].[IsActive] = 1
JOIN [Category] [C] WITH (NOLOCK)
ON [C].[CategoryId] = [TSK].[CategoryId]AND [C].[IsActive] = 1
;
DROP TABLE [#FinalResult_Base_1]
CREATE NONCLUSTERED INDEX [#IX_FR_Student_Class]
ON [dbo].[#FinalResult_Base_2] ([StudentId],[ClassId])
INCLUDE ([TTGuid])
SELECT
FR2.TestCampaignContainerId,
FR2.TestCampaignCategoryId,
FR2.Grade,
FR2.TestCampaignId,
FR2.TestSetId
,FR2.TestId
,FR2.[TaskCategoryId]
,FR2.[TestletId]
,FR2.SectionNo
,FR2.Difficulty
,FR2.TestletName
,TQ.[QuestionId]
,FR2.StudentId
,FR2.ClassId
,RA.SubjectId
,TQ.[QuestionPoints] -- 1+ good, 0 wrong, null no answer
,GoodAnswer = Case When TQ.[QuestionPoints] Is null Then 0
When TQ.[QuestionPoints] > 0 Then 1 -- cookie
Else 0 End
,WrongAnswer = Case When TQ.[QuestionPoints] = 0 Then 1
When TQ.[QuestionPoints] Is null Then 1
Else 0 End
,NoAnswer = Case When TQ.[QuestionPoints] Is null Then 1 Else 0 End
,FR2.Redizo
,FR2.ViewCount
,FR2.SpentTime
,TQ.[Position] AS [QuestionPosition]
,RA.SpecialNeeds -- identifikace SVP
,[Version] = 1 -- ?
,FR2.TestAdaptationId
,FR2.TaskId
,FR2.TaskPosition
,QuestionRate = Q.Rate
,TestQuestionId = TQ.Guid
,FR2.AnswerType
INTO
[#FinalResult_Base]
FROM
[#FinalResult_Base_2] FR2
JOIN [TestQuestion] [TQ] WITH (NOLOCK)
ON [TQ].[TestTaskId] = [FR2].[TTGuid]
JOIN [Question] [Q] WITH (NOLOCK)
ON [Q].[QuestionId] = [TQ].[QuestionId] AND [Q].[IsActive] = 1
JOIN [RoleAssignment] [RA] WITH (NOLOCK)
ON [RA].[PersonId] = [FR2].[StudentId]
AND [RA].[ClassId] = [FR2].[ClassId] AND [RA].[IsActive] = 1 AND [RA].[RoleId] = 1
drop table #FinalResult_Base_2;
truncate table [dbo].[FinalResult_Base];
insert into [dbo].[FinalResult_Base] select * from #FinalResult_Base;
drop table #FinalResult_Base;
For the first, please, rebuilding indexes on your tables by this script (this may take a long time) -
SET NOCOUNT ON;
DECLARE
#SQL NVARCHAR(MAX)
, #IndexName SYSNAME
, #Output VARCHAR(200)
, #ServerVersion VARCHAR(100)
SELECT #ServerVersion = CAST(SERVERPROPERTY('Edition') AS VARCHAR(100))
DECLARE cur CURSOR LOCAL READ_ONLY FORWARD_ONLY FOR
SELECT
'ALTER INDEX [' + ix.name + N'] ON [' + SCHEMA_NAME(t.[schema_id]) + '].[' + t.name + '] ' +
CASE
WHEN ps.avg_fragmentation_in_percent > 50 THEN
CASE WHEN #ServerVersion LIKE 'Enterprise%' OR #ServerVersion LIKE 'Developer%' THEN
'REBUILD WITH (SORT_IN_TEMPDB = ON, ONLINE = ON ' + CASE WHEN ix.fill_factor > 0 THEN ', FILLFACTOR = ' + CAST(ix.fill_factor AS VARCHAR(3)) ELSE '' END + ') '
ELSE
'REBUILD WITH (SORT_IN_TEMPDB = ON' + CASE WHEN ix.fill_factor > 0 THEN ', FILLFACTOR = ' + CAST(ix.fill_factor AS VARCHAR(3)) ELSE '' END + ') '
END
ELSE 'REORGANIZE '
END +
CASE
WHEN ps.partition_number > 1 THEN N' PARTITION = ' + CAST(ps.partition_number AS NVARCHAR(MAX))
ELSE N''
END + ';', ix.name
FROM sys.indexes ix
JOIN sys.objects t ON t.[object_id] = ix.[object_id]
JOIN (
SELECT
[object_id]
, index_id
, avg_fragmentation_in_percent
, partition_number
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, N'LIMITED')
WHERE page_count > 100
AND avg_fragmentation_in_percent > 10
) ps ON t.[object_id] = ps.[object_id] AND ix.index_id = ps.index_id
WHERE t.[type] = 'U'
AND t.name IN (
'TestQuestion', 'TestTask', 'Question', 'Testlet',
'Test', 'TestSet', 'RoleAssignment', 'Task',
'category', 'TimeWindow', 'TestAdaptation', 'TestCampaign')
OPEN cur
FETCH NEXT FROM cur INTO #SQL, #IndexName
WHILE ##FETCH_STATUS = 0 BEGIN
SELECT #Output = CONVERT(NVARCHAR(15), GETDATE(), 114) + ': ' + #IndexName
RAISERROR(#Output, 0, 1) WITH NOWAIT
EXEC sys.sp_executesql #SQL
FETCH NEXT FROM cur INTO #SQL, #IndexName
END
CLOSE cur
DEALLOCATE cur
And after it try this query -
INSERT INTO dbo.FinalResult_Base
SELECT
TC.TestCampaignContainerId
, TestCampaignCategoryId = TC.CategoryID
, TC.Grade
, TC.TestCampaignId
, T.TestSetId
, TL.TestId
, TSK.CategoryID
, TT.[TestletId]
, TL.SectionNo
, TL.Difficulty
, TestletName = CHAR(65 + TL.SectionNo) + CONVERT(VARCHAR(4), 6 - TL.Difficulty)
, TQ.[QuestionId]
, TS.StudentId
, TS.ClassId
, RA.SubjectId
, TQ.[QuestionPoints]
, GoodAnswer =
CASE WHEN ISNULL(TQ.[QuestionPoints], 0) > 0
THEN 1
ELSE 0
END
, WrongAnswer =
CASE
WHEN ISNULL(TQ.[QuestionPoints], 0) = 0
THEN 1
ELSE 0
END
, NoAnswer =
CASE WHEN TQ.[QuestionPoints] IS NULL
THEN 1
ELSE 0
END
, TS.Redizo
, TT.ViewCount
, TT.SpentTime
, TQ.[Position]
, RA.SpecialNeeds
, [Version] = 1
, TestAdaptationId = TA.id
, TaskId = TSK.TaskId
, TaskPosition = TT.Position
, QuestionRate = Q.Rate
, TestQuestionId = TQ.guid
, AnswerType = TT.TestletAnswerTypeId
FROM dbo.TestQuestion TQ WITH (NOLOCK)
JOIN dbo.TestTask TT WITH (NOLOCK) ON TT.[guid] = TQ.TestTaskId
JOIN dbo.Question Q WITH (NOLOCK) ON TQ.QuestionId = Q.QuestionId
JOIN (
SELECT *
FROM dbo.Testlet TL WITH (NOLOCK)
WHERE TL.ShownOn IS NOT NULL
) TL ON TT.TestletId = TL.[guid]
JOIN (
SELECT *
FROM dbo.Test T WITH (NOLOCK)
WHERE T.TestTypeId = 1
AND T.ProcessedOn IS NOT NULL
) T ON TL.TestId = T.[guid]
JOIN (
SELECT *
FROM dbo.TestSet TS WITH (NOLOCK)
WHERE TS.Redizo NOT IN (999999999, 111111119)
) TS ON T.TestSetId = TS.[guid]
JOIN dbo.RoleAssignment RA WITH (NOLOCK) ON TS.StudentId = RA.PersonID AND RA.RoleId = 1
JOIN dbo.Task TSK WITH (NOLOCK) ON TSK.TaskId = TT.TaskId
JOIN dbo.category C WITH (NOLOCK) ON C.CategoryID = TSK.CategoryID
JOIN dbo.TimeWindow TW WITH (NOLOCK) ON TW.id = TS.TimeWindowId
JOIN dbo.TestAdaptation TA WITH (NOLOCK) ON TA.id = TW.TestAdaptationId
JOIN dbo.TestCampaign TC WITH (NOLOCK) ON TC.TestCampaignId = TA.TestCampaignId
And try this query -
SELECT TC.TestCampaignContainerId
,TC.CategoryID AS TestCampaignCategoryId
,TC.Grade
,TC.TestCampaignId
,T.TestSetId
,TL.TestId
,TL.SectionNo
,TL.Difficulty
,TestletName = CHAR(65 + TL.SectionNo) + CONVERT(VARCHAR(4), 6 - TL.Difficulty) -- prevod na A5, B4, B5 ...
,TS.StudentId
,TS.ClassId
,TS.Redizo
,[Version] = 1 -- ?
,TestAdaptationId = TA.id
,TL.guid AS TLGuid
,TS.TimeWindowId
INTO [#FinalResult_Base_1]
FROM (
SELECT *
FROM dbo.[TestSet] [TS] WITH (NOLOCK)
WHERE [TS].[Redizo] NOT IN (999999999, 111111119)
) TS
JOIN (
SELECT *
FROM dbo.[Test] [T] WITH (NOLOCK)
WHERE [T].[TestTypeId] = 1
AND [T].[ProcessedOn] IS NOT NULL
) T ON [T].[TestSetId] = [TS].[guid]
JOIN (
SELECT *
FROM dbo.[Testlet] [TL] WITH (NOLOCK)
WHERE [TL].[ShownOn] IS NOT NULL
) TL ON [TL].[TestId] = [T].[guid]
JOIN (
SELECT *
FROM dbo.[TimeWindow] [TW] WITH (NOLOCK)
WHERE [TW].[IsActive] = 1
) TW ON [TW].[id] = [TS].[TimeWindowId]
JOIN (
SELECT *
FROM dbo.[TestAdaptation] [TA] WITH (NOLOCK)
WHERE [TA].[IsActive] = 1
) TA ON [TA].[id] = [TW].[TestAdaptationId]
JOIN (
SELECT *
FROM dbo.[TestCampaign] [TC] WITH (NOLOCK)
WHERE [TC].[IsActive] = 1
) TC ON [TC].[TestCampaignId] = [TA].[TestCampaignId]
--possible unused join in this query
--JOIN dbo.[TestCampaignContainer] [TCC] WITH (NOLOCK) ON [TCC].[TestCampaignContainerId] = [TC].[TestCampaignContainerId] AND [TCC].[IsActive] = 1
SELECT FR1.TestCampaignContainerId
,FR1.TestCampaignCategoryId
,FR1.Grade
,FR1.TestCampaignId
,FR1.TestSetId
,FR1.TestId
,TSK.CategoryID AS [TaskCategoryId]
,TT.[TestletId]
,FR1.SectionNo
,FR1.Difficulty
,TestletName = CHAR(65 + FR1.SectionNo) + CONVERT(VARCHAR(4), 6 - FR1.Difficulty) -- prevod na A5, B4, B5 ...
,FR1.StudentId
,FR1.ClassId
,FR1.Redizo
,TT.ViewCount
,TT.SpentTime
,[Version] = 1 -- ?
,FR1.TestAdaptationId
,TaskId = TSK.TaskId
,TaskPosition = TT.Position
,AnswerType = TT.TestletAnswerTypeId
,TT.guid AS TTGuid
INTO [#FinalResult_Base_2]
FROM #FinalResult_Base_1 FR1
JOIN [TestTask] [TT] WITH (NOLOCK) ON [TT].[TestletId] = [FR1].[TLGuid]
JOIN [Task] [TSK] WITH (NOLOCK) ON [TSK].[TaskId] = [TT].[TaskId]
--possible unused join
--JOIN [category] [C] WITH (NOLOCK) ON [C].[CategoryID] = [TSK].[CategoryID]
WHERE [TSK].[IsActive] = 1
--AND [C].[IsActive] = 1
DROP TABLE [#FinalResult_Base_1]
CREATE NONCLUSTERED INDEX [#IX_FR_Student_Class]
ON [dbo].[#FinalResult_Base_2] ([StudentId], [ClassId])
INCLUDE ([TTGuid])
TRUNCATE TABLE [dbo].[FinalResult_Base];
INSERT INTO [dbo].[FinalResult_Base]
SELECT FR2.TestCampaignContainerId
,FR2.TestCampaignCategoryId
,FR2.Grade
,FR2.TestCampaignId
,FR2.TestSetId
,FR2.TestId
,FR2.[TaskCategoryId]
,FR2.[TestletId]
,FR2.SectionNo
,FR2.Difficulty
,FR2.TestletName
,TQ.[QuestionId]
,FR2.StudentId
,FR2.ClassId
,RA.SubjectId
,TQ.[QuestionPoints] -- 1+ good, 0 wrong, null no answer
, GoodAnswer =
CASE WHEN ISNULL(TQ.[QuestionPoints], 0) > 0
THEN 1
ELSE 0
END
, WrongAnswer =
CASE
WHEN ISNULL(TQ.[QuestionPoints], 0) = 0
THEN 1
ELSE 0
END
, NoAnswer =
CASE WHEN TQ.[QuestionPoints] IS NULL
THEN 1
ELSE 0
END
,FR2.Redizo
,FR2.ViewCount
,FR2.SpentTime
,TQ.[Position] AS [QuestionPosition]
,RA.SpecialNeeds -- identifikace SVP
,[Version] = 1 -- ?
,FR2.TestAdaptationId
,FR2.TaskId
,FR2.TaskPosition
,QuestionRate = Q.Rate
,TestQuestionId = TQ.guid
,FR2.AnswerType
FROM [#FinalResult_Base_2] FR2
JOIN [TestQuestion] [TQ] WITH (NOLOCK) ON [TQ].[TestTaskId] = [FR2].[TTGuid]
JOIN [Question] [Q] WITH (NOLOCK) ON [Q].[QuestionId] = [TQ].[QuestionId] AND [Q].[IsActive] = 1
JOIN [RoleAssignment] [RA] WITH (NOLOCK) ON [RA].[PersonID] = [FR2].[StudentId]
AND [RA].[ClassId] = [FR2].[ClassId]
AND [RA].[IsActive] = 1
AND [RA].[RoleId] = 1
DROP TABLE #FinalResult_Base_2;
IMHO It should not be the case, I faced similar problem and took the following steps to optimize it.
Try putting Indexes on the columns which are used in filtering (The
columns deciding the joins).
Try to put indexes on Views, It could be done but It needs some
special requirements.
Have some jobs for Index Rebuilding.
Use another mirror instance of DB for reporting leave the live DB
Don't use functions while joining if you want to transform data do it
after joining.
Use Query optimizer to view what part of join is taking most of the
time/resources.
Use an Archive table to purge data into it from live DB.
Hope it helps :)

SQL Server trigger on a field that's updated

I have the following trigger:
BEGIN
DECLARE #email varchar(200)
DECLARE #jobcode int
DECLARE #status char(1)
DECLARE #emaild varchar(200)
DECLARE #jobcoded int
DECLARE #statusd char(1)
SET #statusd = NULL
SELECT #status = z.status, #email = p.EMail, #jobcode = z.jobID
FROM zipoutfiles z
INNER JOIN inserted AS i ON z.jobID = i.jobID
INNER JOIN PS_LoginUser AS p ON z.UserID = p.UserID
SELECT #statusd = z.status, #emaild = p.EMail, #jobcoded = z.jobID
FROM zipoutfiles z
INNER JOIN deleted AS d ON z.jobID = d.jobID
INNER JOIN PS_LoginUser AS p ON z.UserID = p.UserID
WHERE d.jobID = #jobcode
IF ((#status = 'D' AND #statusd = 'R') OR (#status = 'D' AND #statusd = 'E'))
BEGIN
EXEC SendMail #email, #jobcode
END
END
I want to be able to run SendMail when status goes from E to D or R to D, but not D to D (if it gets updated again) and also when it gets inserted as D. What am I doing wrong here:
Not sure what your table schemas are, but this may get you all of the appropriate emails:
select p.EMail as Email, z.JobId as JobCode
from ZipOutFiles as ZOF inner join
inserted as i on i.JobId = ZOF.JobId inner join
PS_LoginUser as PLU on PLU.UserId = ZOF.UserId left outer join
deleted as d on d.JobId = ZOF.JobId
where
( d.Status = 'E' and i.Status = 'D' ) or -- E -> D.
( d.Status = 'R' and i.Status = 'D' ) or -- R -> D.
( d.Status is NULL and i.Status = 'D' ) -- Inserted D.
I've assumed that you aren't really updating JobId. If so, how do you match the before and after rows?
Also assumed is that Status cannot be NULL. If so, the last condition needs to be modified to properly detect no corresponding row was found in the deleted table.

Writing a complex trigger

This posting is an update (with trigger code) from my earlier posting yesterday
I am using SQL Server 2000. I am writing a trigger that is executed when a field Applicant.AppStatusRowID
Table Applicant is linked to table Location, table Company & table AppStatus.
My issue is creating the joins in my query.
When Applicant.AppStatusRowID is updated, I want to get the values from Applicant.AppStatusRowID, Applicant.FirstName, Applicant.Lastname, Location.LocNumber, Location.LocationName, Company.CompanyCode, AppStatus.DisplayText
The joins would be :
Select * from Applicant A
Inner Join AppStatus ast on ast.RowID = a.AppStatusRowID
Inner Join Location l on l.RowID = a.LocationRowID
Inner Join Company c on c.RowID = l.CompanyRowID
This is to be inserted into an Audit table (fields are ApplicantID, LastName, FirstName, Date, Time, Company, Location Number, Location Name, StatusDisposition, User)
The trigger query is:
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER tri_UpdateAppDispo ON dbo.Test_App
For Update
AS
declare #Approwid int
Declare #triggername sysname
Set #rowCnt = ##rowcount
Set #triggername = object_name(##procid)
If #rowCnt = 0
RETURN
If Update(appstatusrowid)
BEGIN
-----------------------------------------------------------------------------
-- insert a record to the AppDispo table, if AppstatusRowid
-- is being Updated
-----------------------------------------------------------------------------
Insert AppDispo(AppID, LastName, FirstName, [DateTime],Company,Location,LocationName,
StatusDispo,[Username])
Select d.Rowid,d.LastName, d.FirstName, getDate(),C.CompanyCode,l.locnum,l.locname, ast.Displaytext,
SUSER_SNAME()+' '+User
From deleted d with(nolock)
Inner join Test_App a with (nolock) on a.RowID = d.rowid
inner join location l with (nolock) on l.rowid = d.Locationrowid
inner join appstatus ast with (nolock) on ast.rowid = d.appstatusrowid
inner join company c with (nolock) on c.rowid = l.CompanyRowid
--Inner Join Deleted d ON a.RowID = d.RowID
--Where (a.rowid = #Approwid)
END
GO
Any ideas?
Try with this code
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER tri_UpdateAppDispo ON dbo.Test_App
For Update
AS
declare #Approwid int
Declare #triggername sysname
Set #rowCnt = ##rowcount
Set #triggername = object_name(##procid)
If #rowCnt = 0
RETURN
If Update(appstatusrowid)
BEGIN
-----------------------------------------------------------------------------
-- insert a record to the AppDispo table, if AppstatusRowid
-- is being Updated
-----------------------------------------------------------------------------
Insert AppDispo(AppID, LastName, FirstName, [DateTime],Company,Location,LocationName,
StatusDispo,[Username])
Select d.Rowid,d.LastName, d.FirstName, getDate(),C.CompanyCode,l.locnum,l.locname, ast.Displaytext,
SUSER_SNAME()+' '+User
From deleted d with(nolock),location l with (nolock),appstatus ast with (nolock),company c with (nolock)
where d.Locationrowid =l.rowid and
d.appstatusrowid = ast.rowid and
c.rowid = l.CompanyRowid
END GO
whith this code you get the update-deleted row(the old_value)
see you.