Update data in SQL Table row by row - sql

I'm using SQL Server 2008 R2. I have a View [EmployeeMaster] from a Linked Server and a table [EmployeeDetails] in database. i have a service which run at 12 AM everyday calling a stored procedure which sync the table with view. ie, stored procedure check and update each row in [EmployeeDetails] with the matching row from view. My stored procedure goes like this.
CREATE PROCEDURE usp_OracleSyncUpdate
AS
CREATE TABLE #ActiveEmployees (
RowID int IDENTITY(1, 1),
[EmployeeId] nvarchar(50),
)
DECLARE #NumberRecords int, #RowCount int
DECLARE #EmployeeId nvarchar(50)
,#EmployeeName nvarchar(50)
,#EmployeeLastName nvarchar(50)
,#EmployeeCategory nvarchar(50)
,#ContactNo nvarchar(50)
,#Email nvarchar(50)
,#Gender nvarchar(50)
,#JoiningDate DATETIME
-- into the temporary table
INSERT INTO #ActiveEmployees ([EmployeeId])
SELECT [EMPLOYEE_NUMBER]
FROM [dbo].[EmployeeMaster]
WHERE [EMPLOYEE_NUMBER] IN (
SELECT EmployeeId
FROM [dbo].[EmployeeDetails]
)
-- Get the number of records in the temporary table
SET #NumberRecords = ##ROWCOUNT
SET #RowCount = 1
-- loop through all records in the temporary table
-- using the WHILE loop construct
WHILE #RowCount <= #NumberRecords
BEGIN
SELECT #EmployeeId = EmployeeId
FROM #ActiveEmployees
WHERE RowID = #RowCount
SELECT #EmployeeName = EmployeeName FROM [dbo].[EmployeeDetails] WHERE [EmployeeId] = #EmployeeId
IF(LTRIM(RTRIM(#EmployeeName)) <> (SELECT LTRIM(RTRIM([FIRST_NAME])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId))
BEGIN
UPDATE [dbo].[EmployeeDetails] SET EmployeeName = (SELECT LTRIM(RTRIM([FIRST_NAME])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId)
WHERE EmployeeId = #EmployeeId
END
SELECT #EmployeeLastName = EmployeeLastName FROM [dbo].[EmployeeDetails] WHERE [EmployeeId] = #EmployeeId
IF(LTRIM(RTRIM(#EmployeeLastName)) <> (SELECT LTRIM(RTRIM([LAST_NAME])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId))
BEGIN
UPDATE [dbo].[EmployeeDetails] SET EmployeeLastName = (SELECT LTRIM(RTRIM([LAST_NAME])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId)
WHERE EmployeeId = #EmployeeId
END
SELECT #EmployeeCategory = [Category] FROM [dbo].[EmployeeCategory] WHERE [EmployeeCategoryId] = (SELECT [EmployeeCategoryId] FROM [dbo].[EmployeeDetails] WHERE [EmployeeId] = #EmployeeId)
IF(LTRIM(RTRIM(#EmployeeCategory)) <> (SELECT LTRIM(RTRIM([JOB_NAME])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId))
BEGIN
UPDATE [dbo].[EmployeeDetails] SET [EmployeeCategoryId] = (SELECT [EmployeeCategoryId] FROM [dbo].[EmployeeCategory] WHERE [Category] = (SELECT LTRIM(RTRIM([JOB_NAME])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId))
WHERE EmployeeId = #EmployeeId
END
SELECT #Email = Email FROM [dbo].[EmployeeDetails] WHERE [EmployeeId] = #EmployeeId
IF(LTRIM(RTRIM(#Email)) <> (SELECT LTRIM(RTRIM([EMAIL_ADDRESS])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId))
BEGIN
UPDATE [dbo].[EmployeeDetails] SET Email = (SELECT LTRIM(RTRIM([EMAIL_ADDRESS])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId)
WHERE EmployeeId = #EmployeeId
END
SELECT #Gender = Gender FROM [dbo].[EmployeeDetails] WHERE [EmployeeId] = #EmployeeId
IF(LTRIM(RTRIM(#Gender)) <> (SELECT LTRIM(RTRIM([GENDER])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId))
BEGIN
UPDATE [dbo].[EmployeeDetails] SET Gender = (SELECT LTRIM(RTRIM([GENDER])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId)
WHERE EmployeeId = #EmployeeId
END
SELECT #JoiningDate = JoiningDate FROM [dbo].[EmployeeDetails] WHERE [EmployeeId] = #EmployeeId
IF(LTRIM(RTRIM(#JoiningDate)) <> (SELECT LTRIM(RTRIM([DATE_OF_JOINING])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId))
BEGIN
UPDATE [dbo].[EmployeeDetails] SET JoiningDate = (SELECT LTRIM(RTRIM([DATE_OF_JOINING])) FROM [dbo].[EmployeeMaster] WHERE [EMPLOYEE_NUMBER] = #EmployeeId)
WHERE EmployeeId = #EmployeeId
END
SET #RowCount = #RowCount + 1
END
-- drop the temporary table
DROP TABLE #ActiveEmployees
Its working and it updates the columns. now the table has 26196 Records and it takes about 23:56 minutes to execute this stored procedure. In future the records will keep increasing and the execution time will be more.
Is there any way to bring down the execution time (suggest some optimization for the query )?
Or is there any other way i can sync the two table?
Or Is there any way to calculate the execution time for future also and i can set the connection timeout according to that?
Here is my final code
alter PROCEDURE usp_OracleSyncUpdate
AS
UPDATE ed
SET ed.[EmployeeName] = LTRIM(RTRIM(em.FIRST_NAME)),
ed.EmployeeLastName = LTRIM(RTRIM(em.LAST_NAME)),
ed.EmployeeCategoryId = ec.EmployeeCategoryId,
ed.Email = isnull(ed.Email,em.[EMAIL_ADDRESS]),
ed.[ContactNo] = isnull(ed.[ContactNo],em.ContactNo),
ed.Gender = em.Gender,
ed.JoiningDate = em.[DATE_OF_JOINING]
FROM dbo.EmployeeDetails ed
INNER JOIN [EmployeeMaster] em ON ed.EmployeeId = em.[EMPLOYEE_NUMBER]
INNER JOIN dbo.EmployeeCategory ec ON ec.Category = em.[JOB_NAME]
Thanks #marc_s

Get rid of the RBAR (row-by-agonizing-row) processing - relational databases work in sets of data - use a single set-based UPDATE statement and you're done.
Something along the lines of :
CREATE PROCEDURE usp_OracleSyncUpdate
AS
CREATE TABLE #ActiveEmployees (
RowID int IDENTITY(1, 1),
[EmployeeId] nvarchar(50),
)
-- into the temporary table
INSERT INTO #ActiveEmployees ([EmployeeId])
SELECT [EMPLOYEE_NUMBER]
FROM [dbo].[EmployeeMaster]
WHERE [EMPLOYEE_NUMBER] IN (SELECT EmployeeId
FROM [dbo].[EmployeeDetails])
UPDATE dbo.EmployeeDetails
SET ed.EmployeeName = LTRIM(RTRIM(ae.FIRST_NAME)),
ed.EmployeeLastName = (SELECT LTRIM(RTRIM(ae.LAST_NAME)),
ed.EmployeeCategoryId = ec.EmployeeCategoryId,
ed.Email = ae.Email,
ed.Gender = ae.Gender,
ed.JoiningDate = ae.JoiningDate
FROM dbo.EmployeeDetails ed
INNER JOIN #ActiveEmployees ae ON ed.EmployeeId = ae.EmployeeId
INNER JOIN dbo.EmployeeCategory ec ON .......
WHERE EmployeeId = #EmployeeId
I didn't know how the EmployeeCategory table is linked to the others - you'll need to add that JOIN condition on the INNER JOIN line there.
Also, quite possibly, you could also eliminate that temporary table altogether and just update EmployeeDetails directly from EmployeeMaster as a second step to save memory and processing time.

Well, instead of a loop do it in one sql statement and be done with it. Seriously.
I see a loop and I see a ton of indivisual statements and I can envision that being ONE UPATE STATEMENT ONLY.
On top, this is not "hugh table". It is tiny. Super tiny. One million rows is small. This has not even 27000 rows. You just abuse the server it by making a "Programmer approach" - loop, way too many statement. Use a set based approach and your whole loop can be written as ONE STATEMENT.

Related

Stored procedure not inserting data into table

I'm trying to implement a stored procedure like this where it's going to compare UserID from another table. If that user id already exists in dbo.user info table, it will insert data into [dbo].[BulkUploadTagDetail] table.
Every time I execute this stored procedure running into an issue that the data doesn't get inserted into my database table.
CREATE PROCEDURE [dbo].[InsertBulkUploadTagDetail]
(#BulkTagID INT,
#PortalUserID UNIQUEIDENTIFIER,
#EmailAddress VARCHAR(256),
#BulkUploadFileName VARCHAR(256),
#CreatedOn DATETIME2(7),
#IsCompleted BIT = 0)
AS
BEGIN
SET NOCOUNT ON
IF EXISTS (SELECT UserID
FROM dbo.UserInfo
WHERE UserID = #PortalUserID)
BEGIN
UPDATE [unp]
SET [unp].[BulkTagID] = #BulkTagID,
[unp].[UserID] = #PortalUserID,
[unp].[EmailAddress] = #EmailAddress,
[unp].[BulkUploadFileName] = #BulkUploadFileName,
[unp].[CreatedOn] = GETUTCDATE(),
[unp].[IsCompleted] = #IsCompleted
FROM
[dbo].[InsertBulkUploadTagDetail] unp
INNER JOIN
[dbo].[UserInfo] uinfo ON [unp].[UserID] = [uinfo].[UserID]
WHERE
[unp].[UserID] = #PortalUserID
END
ELSE
BEGIN
INSERT INTO [dbo].[BulkUploadTagDetail]
([BulkTagID], [PortalUserID], [EmailAddress],
[BulkUploadFileName], [CreatedOn], [IsCompleted])
VALUES (#BulkTagID, #PortalUserID, #EmailAddress,
#BulkUploadFileName, #CreatedOn, #IsCompleted)
END
END

Unable to delete records from SQL Server tables using stored procedure

I'm developing a desktop application for inventory management.
I have 3 tables in SQL Server:
TRANSACTIONS
MATERIAL_TRANSACTION
TRANSACTION_DETAILS
I've created a stored procedure to insert as well as update all the above mentioned tables.
TRANSACTIONS is my master table. So, I use update query to update its columns.
But records of the other two tables, which are the detail tables, need to be deleted before inserting new rows in the database.
I'm unable to delete the rows in this stored procedure. Please help!
I've used the following SQL Server stored procedure
ALTER PROCEDURE [dbo].[SAVE_TRANSACTION]
#ID NVARCHAR(50),
#REF_NO NVARCHAR(50),
#VCH_DATE DATE,
#VCH_TYPE INT,
#CR_AC INT,
#DR_AC INT,
#DOCKET_NO NVARCHAR(50),
#TRANSPORT NVARCHAR(50),
#DELIVERY_NOTE NTEXT,
#S_NAR NVARCHAR(100),
#L_NAR NTEXT,
#IS_REVERSE_CHARGE INT,
#INVOICE_REF INT,
#BUYER NVARCHAR(50),
#BUYER_CONTACT NVARCHAR(50),
#BUYER_ADDRESS NTEXT,
#BUYER_GSTIN NVARCHAR(50),
#BUYER_STATE NVARCHAR(50),
#BUYER_REG_TYPE INT,
#MATERIAL_TRANSACTION dbo.TVP_MATERIAL_TRANSACTION READONLY,
#TRANSACTION_DETAILS dbo.TVP_TRANSACTION_DETAILS READONLY,
#RETURN INT OUTPUT
AS
DECLARE #TRAN_ID INT
BEGIN
SET NOCOUNT ON;
DELETE FROM MATERIAL_TRANSACTION
WHERE ID > 0
AND TRANSACTION_ID = #ID;
DELETE FROM TRANSACTION_DETAILS
WHERE ID > 0
AND TRANSACTION_ID = #ID;
IF (#ID = 0)
BEGIN
INSERT INTO dbo.TRANSACTIONS(REF_NO, VCH_DATE, VCH_TYPE, CR_AC, DR_AC,
DOCKET_NO, TRANSPORT, DELIVERY_NOTE,
S_NAR, L_NAR, IS_REVERSE_CHARGE,
INVOICE_REF, BUYER, BUYER_CONTACT, BUYER_ADDRESS,
BUYER_GSTIN, BUYER_STATE, BUYER_REG_TYPE)
VALUES (#REF_NO, #VCH_DATE, #VCH_TYPE, #CR_AC, #DR_AC,
#DOCKET_NO, #TRANSPORT, #DELIVERY_NOTE,
#S_NAR, #L_NAR, #IS_REVERSE_CHARGE,
#INVOICE_REF, #BUYER, #BUYER_CONTACT, #BUYER_ADDRESS,
#BUYER_GSTIN, #BUYER_STATE, #BUYER_REG_TYPE);
SET #TRAN_ID = ##IDENTITY
END
ELSE
BEGIN
UPDATE dbo.TRANSACTIONS
SET REF_NO = #REF_NO,
VCH_DATE = #VCH_DATE, VCH_TYPE = #VCH_TYPE,
CR_AC = #CR_AC, DR_AC = #DR_AC,
DOCKET_NO = #DOCKET_NO,
TRANSPORT = #TRANSPORT, DELIVERY_NOTE = #DELIVERY_NOTE,
S_NAR = #S_NAR, L_NAR = #L_NAR,
IS_REVERSE_CHARGE = #IS_REVERSE_CHARGE,
INVOICE_REF = #INVOICE_REF,
BUYER = #BUYER, BUYER_CONTACT = #BUYER_CONTACT,
BUYER_ADDRESS = #BUYER_ADDRESS,
BUYER_GSTIN = #BUYER_GSTIN, BUYER_STATE = #BUYER_GSTIN,
BUYER_REG_TYPE = #BUYER_REG_TYPE
WHERE
ID = #ID;
SET #TRAN_ID = #ID;
END
--UPDATE MATERIAL_TRANSACTION SET IS_DELETED=1 WHERE TRANSACTION_ID=#ID;
-- UPDATE TRANSACTION_DETAILS SET IS_DELETED=1 WHERE TRANSACTION_ID=#ID;
INSERT INTO TRANSACTION_DETAILS (TRANSACTION_ID, AC_ID, TRAN_TYPE,
CR_AMT, DR_AMT, AMT_PERCENT, IS_PARENT)
SELECT
#TRAN_ID, [AC_ID], [TRAN_TYPE],
[CR_AMT], [DR_AMT], [AMT_PERCENT], [IS_PARENT]
FROM
#TRANSACTION_DETAILS TVP
INSERT INTO MATERIAL_TRANSACTION (TRANSACTION_ID, PRODUCT_ID, QTY,
UNIT_ID, DESCRIPTION, HSN_CODE,
PRICE, DISCOUNT_AMT, DISCOUNT_PERCENT,
CGST_PERCENT, SGST_PERCENT, IGST_PERCENT,
CGST_AMT, SGST_AMT, IGST_AMT)
SELECT
#TRAN_ID, [PRODUCT_ID], [QTY],
[UNIT_ID], [DESCRIPTION], [HSN_CODE],
[PRICE], [DISCOUNT_AMT], [DISCOUNT_PERCENT],
[CGST_PERCENT], [SGST_PERCENT], [IGST_PERCENT],
[CGST_AMT], [SGST_AMT], [IGST_AMT]
FROM
#MATERIAL_TRANSACTION TVP
SELECT #RETURN = #TRAN_ID
END

SQL insert statement for each update row

Now i made cursor to update in 2 tables and insert in one table based on specific select statement this select statement return 2 columns x , y i need x to update in table "PX" because x is Primary key in this table and need x to update in table "FX" because x is foreign key in this table then insert in third table x data.
I need to change this cursor and use update and insert script i tried but i found that i need to make loop to achieve my target so if any one know if i can change this cursor .
And thanks in advance
DECLARE #id int
DECLARE #clientid uniqueidentifier
DECLARE #code int
DECLARE #Wtime int
DECLARE #closeComplaint CURSOR
SET #closeComplaint = CURSOR FAST_FORWARD
FOR
SELECT ComplaintId, [ClientId]
FROM complaint
WHERE ComplaintStatusId = 5
AND (waitingForCutomerCloseDateTime < GETDATE() OR
waitingForCutomerCloseDateTime = GETDATE())
OPEN #closeComplaint
FETCH NEXT FROM #closeComplaint INTO #id, #clientid
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT
waitingForCutomerCloseTime = #Wtime
FROM
SystemConfiguration
WHERE
ClientId = #clientid
SELECT
[Code] = #code
FROM
[dbo].[resp_users]
WHERE
ClientId = #clientid
UPDATE activity
SET ActivityStatus = 4,
CompletionDate = GETDATE(),
ClosedBy = #code
WHERE [ComplaintId] = #id
UPDATE [dbo].[Complaint]
SET ComplaintStatusId = 2
WHERE [ComplaintId] = #id
INSERT INTO [dbo].[Note] ([Note_Description], [ClientId], [User_Code], [Visible_Internal],
[ComplaintId], [Note_DateTime], [ComplainantId],
[OneStopDesk_CustomerEmail], [OneStopDesk_CustomerUsername], [Private])
VALUES (N'Automatically closed by system after ' + #Wtime, #clientid, #code, 1,
#id, GETDATE(), null, null, null, 1)
FETCH NEXT FROM #closeComplaint INTO #id, #clientid
END
CLOSE #closeComplaint
DEALLOCATE #closeComplaint
I'm not entirely sure I got everything right (you didn't post the table structures, so I can really only guess at times how those tables are connected) - but you should be able to basically do all of this in 3 simple, set-based statements - and that should be a LOT faster than the cursor!
-- declare table variable
DECLARE #Input TABLE (CompaintID INT, ClientID INT)
-- save the rows into a table variable
INSERT INTO #Input (ComplaintID, ClientID)
SELECT ComplaintID, ClientID
FROM dbo.Complaint
WHERE ComplaintStatusId = 5
AND waitingForCustomerCloseDateTime <= GETDATE()
UPDATE a
SET ActivityStatus = 4,
CompletionDate = GETDATE(),
ClosedBy = u.Code
FROM dbo.Activity a
INNER JOIN #Input i ON a.ComplaintId = i.ComplaintId
INNER JOIN dbo.resp_users u ON i.ClientId = u.ClientId
UPDATE dbo.Complaint
SET ComplaintStatusId = 2
WHERE
ComplaintStatusId = 5
AND waitingForCustomerCloseDateTime <= GETDATE()
INSERT INTO dbo.Note ([Note_Description], [ClientId], [User_Code], [Visible_Internal],
[ComplaintId], [Note_DateTime], [ComplainantId],
[OneStopDesk_CustomerEmail], [OneStopDesk_CustomerUsername], [Private])
SELECT
N'Automatically closed by system after ' + sc.waitingForCustomerCloseTime,
i.ClientId, u.Code, 1,
i.ComplaintId, GETDATE(), null, null, null, 1
FROM
#Input i
INNER JOIN
dbo.SystemConfiguration sc ON i.ClientId = sc.ClientId
INNER JOIN
dbo.resp_user u ON u.ClientId = i.ClientId

Why my query isn't showing correct results?

I have many tables but for instance let's consider 2 tables i.e. Employees and FileEntries. I am picking these fields from both tables EmployeeID,FileStatus,FileName,EmployeeName.
These both tables have 1 common field i.e. EmployeeID. FileEntries can have EmployeeID being present even if it's not in Employees table. So i want to pick all records from both tables but a new field should show 'NotRegistered' if it is not in Employees table.
)
#EmployeeID int
)
Alter PROCEDURE [dbo].[Modify_RejectedFiles] -- '2015-01-06 07:41:00', '2015-01-06 07:41:00',1,3,'','20001018783815'
(
#FromDate SMALLDATETIME,
#ToDate SMALLDATETIME,
#OfficeID INT=0, --it represents id of a company/branch/organization being logged in
#Type INT=0, --it represents type of a user being logged in i.e. 1=orgranization, 2=company, 3=branch
#EmployerUniqueID VARCHAR(15)='',
#EmployeeUniqueID VARCHAR(15)=''
)
AS
BEGIN
DECLARE #Branches TABLE
(
BranchID INT
)
IF #Type = 1 --Organization
BEGIN
INSERT INTO #Branches
SELECT BranchID From vw_OrganizationTree WHERE OrganizationID = #OfficeID --inserts 3 always because we got only 1 organization i.e. 3
END
IF #Type = 2 --Company
BEGIN
INSERT INTO #Branches
SELECT BranchID From vw_OrganizationTree WHERE CompanyID = #OfficeID --inserts 3 always because we got only 1 company i.e. 3
END
IF #Type = 3 -- i.e. Branch
BEGIN
INSERT INTO #Branches
SELECT BranchID From vw_OrganizationTree WHERE BranchID = #OfficeID
END
Declare #IsRegistered varchar(20)= 'Registered'
If ((Select count(*) from RegisteredEmployees where EmployeeUniqueID= #EmployeeUniqueID) <1)
Begin
Set #IsRegistered = 'Not Yet'
End
Select distinct FE.EmployeeUniqueID, RE.EmployeeName, Empr.EmployerName, Br.BranchName,
FE.IsRejected, FE.RejectedFileCreationDateTime
From
File_EDREntries FE
left Join
RegisteredEmployees RE
ON FE.EmployeeUniqueID = RE.EmployeeUniqueID
left Join Employers Empr
ON RE.Employer_ID = Empr.ID
left Join Branches Br
ON Br.BranchID = Empr.Branch_ID
WHERE
FE.IsRejected = 1 --20017128203780
AND Empr.Branch_ID in (Select BranchID from #Branches)
Use Case when RE.EmployeeUniqueID is null then 'Not Yet' else 'Registered' End in your final select Query

SQL Query Optimization

This report used to take about 16 seconds when there were 8000 rows to process. Now there are 50000 rows and the report takes 2:30 minutes.
This was my first pass at this and the client needed it yesterday, so I wrote this code in the logical order of what needed to be done, but without optimization in mind.
Now with the report taking longer as the data increases, I need to take a second look at this and optimize it. I'm thinking indexed views, table functions, etc.
I think the biggest bottleneck is looping through the temp table, making 4 select statements, and updating the temp table...50,000 times.
I think I can condense ALL of this into one large SELECT with either (a) 4 joins to the same table to get the 4 statuses, but then I am not sure how to get the TOP 1 in there, or I can try (b) using nested subqueries, but both seem really messy compared to the current code.
I'm not expecting anyone to write code for me, but if some SQL experts can peruse this code and tell me about any obvious inefficiencies and alternate methods, or ways to speed this up, or techniques I should be using instead, it would be appreciated.
PS: Assume that this DB is for the most part normalized, but poorly designed, and that I am not able to add indexes. I basically have to work with it, as is.
Where the code says (less than) I had to replace a "less than" symbol because it was cropping some of my code.
Thanks!
CREATE PROCEDURE RptCollectionAccountStatusReport AS
SET NOCOUNT ON;
DECLARE #Accounts TABLE
(
[AccountKey] INT IDENTITY(1,1) NOT NULL,
[ManagementCompany] NVARCHAR(50),
[Association] NVARCHAR(100),
[AccountNo] INT UNIQUE,
[StreetAddress] NVARCHAR(65),
[State] NVARCHAR(50),
[PrimaryStatus] NVARCHAR(100),
[PrimaryStatusDate] SMALLDATETIME,
[PrimaryDaysRemaining] INT,
[SecondaryStatus] NVARCHAR(100),
[SecondaryStatusDate] SMALLDATETIME,
[SecondaryDaysRemaining] INT,
[TertiaryStatus] NVARCHAR(100),
[TertiaryStatusDate] SMALLDATETIME,
[TertiaryDaysRemaining] INT,
[ExternalStatus] NVARCHAR(100),
[ExternalStatusDate] SMALLDATETIME,
[ExternalDaysRemaining] INT
);
INSERT INTO
#Accounts (
[ManagementCompany],
[Association],
[AccountNo],
[StreetAddress],
[State])
SELECT
mc.Name AS [ManagementCompany],
a.LegalName AS [Association],
c.CollectionKey AS [AccountNo],
u.StreetNumber + ' ' + u.StreetName AS [StreetAddress],
CASE WHEN c.InheritedAccount = 1 THEN 'ZZ' ELSE u.State END AS [State]
FROM
ManagementCompany mc WITH (NOLOCK)
JOIN
Association a WITH (NOLOCK) ON a.ManagementCompanyKey = mc.ManagementCompanyKey
JOIN
Unit u WITH (NOLOCK) ON u.AssociationKey = a.AssociationKey
JOIN
Collection c WITH (NOLOCK) ON c.UnitKey = u.UnitKey
WHERE
c.Closed IS NULL;
DECLARE #MaxAccountKey INT;
SELECT #MaxAccountKey = MAX([AccountKey]) FROM #Accounts;
DECLARE #index INT;
SET #index = 1;
WHILE #index (less than) #MaxAccountKey BEGIN
DECLARE #CollectionKey INT;
SELECT #CollectionKey = [AccountNo] FROM #Accounts WHERE [AccountKey] = #index;
DECLARE #PrimaryStatus NVARCHAR(100) = NULL;
DECLARE #PrimaryStatusDate SMALLDATETIME = NULL;
DECLARE #PrimaryDaysRemaining INT = NULL;
DECLARE #SecondaryStatus NVARCHAR(100) = NULL;
DECLARE #SecondaryStatusDate SMALLDATETIME = NULL;
DECLARE #SecondaryDaysRemaining INT = NULL;
DECLARE #TertiaryStatus NVARCHAR(100) = NULL;
DECLARE #TertiaryStatusDate SMALLDATETIME = NULL;
DECLARE #TertiaryDaysRemaining INT = NULL;
DECLARE #ExternalStatus NVARCHAR(100) = NULL;
DECLARE #ExternalStatusDate SMALLDATETIME = NULL;
DECLARE #ExternalDaysRemaining INT = NULL;
SELECT TOP 1
#PrimaryStatus = a.StatusName, #PrimaryStatusDate = c.StatusDate, #PrimaryDaysRemaining = c.DaysRemaining
FROM CollectionAccountStatus c WITH (NOLOCK) JOIN AccountStatus a WITH (NOLOCK) ON c.AccountStatusKey = a.AccountStatusKey
WHERE c.CollectionKey = #CollectionKey AND a.StatusType = 'Primary Status' AND a.StatusName 'Cleared'
ORDER BY c.sysCreated DESC;
SELECT TOP 1
#SecondaryStatus = a.StatusName, #SecondaryStatusDate = c.StatusDate, #SecondaryDaysRemaining = c.DaysRemaining
FROM CollectionAccountStatus c WITH (NOLOCK) JOIN AccountStatus a WITH (NOLOCK) ON c.AccountStatusKey = a.AccountStatusKey
WHERE c.CollectionKey = #CollectionKey AND a.StatusType = 'Secondary Status' AND a.StatusName 'Cleared'
ORDER BY c.sysCreated DESC;
SELECT TOP 1
#TertiaryStatus = a.StatusName, #TertiaryStatusDate = c.StatusDate, #TertiaryDaysRemaining = c.DaysRemaining
FROM CollectionAccountStatus c WITH (NOLOCK) JOIN AccountStatus a WITH (NOLOCK) ON c.AccountStatusKey = a.AccountStatusKey
WHERE c.CollectionKey = #CollectionKey AND a.StatusType = 'Tertiary Status' AND a.StatusName 'Cleared'
ORDER BY c.sysCreated DESC;
SELECT TOP 1
#ExternalStatus = a.StatusName, #ExternalStatusDate = c.StatusDate, #ExternalDaysRemaining = c.DaysRemaining
FROM CollectionAccountStatus c WITH (NOLOCK) JOIN AccountStatus a WITH (NOLOCK) ON c.AccountStatusKey = a.AccountStatusKey
WHERE c.CollectionKey = #CollectionKey AND a.StatusType = 'External Status' AND a.StatusName 'Cleared'
ORDER BY c.sysCreated DESC;
UPDATE
#Accounts
SET
[PrimaryStatus] = #PrimaryStatus,
[PrimaryStatusDate] = #PrimaryStatusDate,
[PrimaryDaysRemaining] = #PrimaryDaysRemaining,
[SecondaryStatus] = #SecondaryStatus,
[SecondaryStatusDate] = #SecondaryStatusDate,
[SecondaryDaysRemaining] = #SecondaryDaysRemaining,
[TertiaryStatus] = #TertiaryStatus,
[TertiaryStatusDate] = #TertiaryStatusDate,
[TertiaryDaysRemaining] = #TertiaryDaysRemaining,
[ExternalStatus] = #ExternalStatus,
[ExternalStatusDate] = #ExternalStatusDate,
[ExternalDaysRemaining] = #ExternalDaysRemaining
WHERE
[AccountNo] = #CollectionKey;
SET #index = #index + 1;
END;
SELECT
[ManagementCompany],
[Association],
[AccountNo],
[StreetAddress],
[State],
[PrimaryStatus],
CONVERT(VARCHAR, [PrimaryStatusDate], 101) AS [PrimaryStatusDate],
[PrimaryDaysRemaining],
[SecondaryStatus],
CONVERT(VARCHAR, [SecondaryStatusDate], 101) AS [SecondaryStatusDate],
[SecondaryDaysRemaining],
[TertiaryStatus],
CONVERT(VARCHAR, [TertiaryStatusDate], 101) AS [TertiaryStatusDate],
[TertiaryDaysRemaining],
[ExternalStatus],
CONVERT(VARCHAR, [ExternalStatusDate], 101) AS [ExternalStatusDate],
[ExternalDaysRemaining]
FROM
#Accounts
ORDER BY
[ManagementCompany],
[Association],
[StreetAddress]
ASC;
Don't try to guess where the query is going wrong - look at the execution plan. It will tell you what's chewing up your resources.
You can update directly from another table, even from a table variable: SQL update from one Table to another based on a ID match
That would allow you to combine everything in your loop into a single (massive) statement. You can join to the same tables for the secondary and tertiary statuses using different aliases, e.g.,
JOIN AccountStatus As TertiaryAccountStatus...AND a.StatusType = 'Tertiary Status'
JOIN AccountStatus AS SecondaryAccountStatus...AND a.StatusType = 'Secondary Status'
I'll bet you don't have an index on the AccountStatus.StatusType field. You might try using the PK of that table instead.
HTH.
First use a temp table instead of a table varaiable. These can be indexed.
Next, do not loop! Looping is bad for performance in virtually every case. This loop ran 50000 times rather than once for 50000 records, it will be horrible when you havea million records! Here is a link that will help you understand how to do set-based processing instead. It is written to avoid cursos but loops are similar to cursors, so it should help.
http://wiki.lessthandot.com/index.php/Cursors_and_How_to_Avoid_Them
And (nolock) will give dirty data reads which can be very bad for reporting. If you are in a version of SQl Server higher than 2000, there are better choices.
SELECT #CollectionKey = [AccountNo] FROM #Accounts WHERE [AccountKey] = #index;
This query would benefit from a PRIMARY KEY declaration on your table variable.
When you say IDENTITY, you are asking the database to auto-populate the column.
When you say PRIMARY KEY, you are asking the database to organize the data into a clustered index.
These two concepts are very different. Typically, you should use both of them.
DECLARE #Accounts TABLE
(
[AccountKey] INT IDENTITY(1,1) PRIMARY KEY,
I am not able to add indexes.
In that case, copy the data to a database where you may add indexes. And use: SET STATISTICS IO ON