How to optimize cursor in a stored procedure - sql-server

I'm having problems with a stored procedure that iterates over a table, it works fine with a few hundred rows however when the table is over the thousands it saturates the memory and crashes.
The procedure should iterate row by row and fill a column with a value which is calculated from another column in the row. I suspect it is the cursor that crashes the procedure and in other questions I've read to use a while loop but I'm no expert in sql and the examples I tried from those answers didn't work.
CREATE PROCEDURE [dbo].[GenerateNewHashes]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #module BIGINT = 382449983
IF EXISTS(SELECT 1 FROM dbo.telephoneSource WHERE Hash IS NULL)
BEGIN
DECLARE hash_cursor CURSOR FOR
SELECT a.telephone, a.Hash
FROM dbo.telephoneSource AS a
OPEN hash_cursor
FETCH FROM hash_cursor
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE dbo.telephoneSource
SET Hash = CAST(telephone AS BIGINT) % #module
WHERE CURRENT OF hash_cursor
FETCH NEXT FROM hash_cursor
END
CLOSE hash_cursor
DEALLOCATE hash_cursor
END
END
Basically the stored procedure is intended to fill a new column called Hash that was added to the existing table, when the script that updates the table ends the new column is filled with NULL values and then this stored procedure is supposed to fill each null value with the operation telephone number (which is a bigint) % module variable (big int as well).
Is there anything besides changing to a while loop that I can do to make it use less memory or just don't crash? Thanks in advance.

You could do the following:
WHILE 1=1
BEGIN
UPDATE TOP (10000) dbo.telephoneSource
SET Hash = CAST(telephone AS BIGINT)%#module
WHERE Hash IS NULL;
IF ##ROWCOUNT = 0
BEGIN
BREAK;
END;
END;
This will update Hash as long as there are NULL values and will exit once there have been no records updated.
Adding a filtered index could be useful as well:
CREATE NONCLUSTERED INDEX IX_telephoneSource_Hash_telephone
ON dbo.telephoneSource (Hash)
INCLUDE (telephone)
WHERE Hash IS NULL;
It will speed up lookups in order to update it. But this might be not needed.

Here is example of code to do it in loops from my comment above with out using a cursor, and if you add where your field you are updating IS NOT NULL into the inner loop it wont update ones that were already done (in case you need to restart the process or something.
I didnt include your specific tables in there but if you need me to I can add it in there.
DECLARE #PerBatchCount as int
DECLARE #MAXID as bigint
DECLARE #WorkingOnID as bigint
Set #PerBatchCount = 1000
--Find range of IDs to process using yoru tablename
SELECT #WorkingOnID = MIN(ID), #MAXID = MAX(ID)
FROM YouTableHere WITH (NOLOCK)
WHILE #WorkingOnID <= #MAXID
BEGIN
-- do an update on all the ones that exist in the offer table NOW
--DO YOUR UPDATE HERE
-- include this where clause where ID is your PK you are looping through
WHERE ID BETWEEN #WorkingOnID AND (#WorkingOnID + #PerBatchCount -1)
set #WorkingOnID = #WorkingOnID + #PerBatchCount
END
SET NOCOUNT OFF;

I would simply add computed column:
ALTER TABLE dbo.telephoneSource
ADD Hash AS (CAST(telephone AS BIGINT)%382449983) PERSISTED;

Related

SQL Server trigger Insert/Update specific column

Point is to make a trigger which will:
Check the configuration table which contains a column ConnectionField nvarchar(50)
It should return the string value (columnName) which will be used as a key
So on insert/update on table Workers, the code should set my Xfield value to the value from column ConnectionField, read from the Configuration table.
In short since this is all messy. I want to be able to let my end user to write down in configuration which column he will use as unique (Worker ID, SNSID, Name etc... ) based on his pick trigger need to put that field value to my Xfield
Don't ask why. It's really confusing.
I've written a trigger which will do that but it just is stuck somewhere in an infinite loop
CREATE TRIGGER [dbo].Tr_ConnectionField
ON [dbo].Workers
FOR INSERT, UPDATE
AS
SET NOCOUNT ON;
DECLARE #ID BIGINT
DECLARE #tmpUpit CURSOR;
DECLARE #ConFieldSETUP NVARCHAR(50)
-- Here I will read the field from configuration which will be used as key
SET #ConFieldSETUP = (SELECT TOP 1 ISNULL(ConnectionField, 'SNSID')
FROM ConfigurationTable)
BEGIN
SET #tmpUpit = CURSOR LOCAL SCROLL FOR
SELECT i.id FROM inserted i
OPEN #tmpUpit
END
FETCH NEXT FROM #tmpUpit INTO #ID
WHILE ##fetch_status = 0
BEGIN
-- Here I will use the configuration columns value to my Xfield
UPDATE Workers
SET Xfield = (SELECT #ConFieldSETUP
FROM Workers cld
WHERE cld.Id = #ID)
WHERE Id = #ID
END
FETCH NEXT FROM #tmpUpit INTO #ID
DEALLOCATE #tmpUpit
Try
CREATE TRIGGER [dbo].Tr_ConnectionField ON [dbo].Textt
FOR INSERT, UPDATE AS
SET NOCOUNT ON;
DECLARE #ConFieldSETUP nvarchar(50);
-- Stop recursion for the trigger
IF TRIGGER_NESTLEVEL(OBJECT_ID('dbo.Tr_ConnectionField')) > 1
RETURN;
-- Here i will read the field from configuration which will be used as key
SET #ConFieldSETUP = (SELECT TOP 1 ISNULL(ConnectionField, 'SNSID')
FROM ConfigurationTable
-- ORDER BY ...
);
-- Update Xfield depending on configuration
UPDATE w
SET Xfield = CASE #ConFieldSETUP
WHEN 'SNSID' THEN w.SNSID
WHEN 'Name' THEN w.Name
...
END
FROM Workers w
JOIN inserted i ON i.Id = w.Id;

Pulling Values From Temp Table After An Iteration

I'm querying for the total sizes of recent data in certain databases.
I create a table containing the DBs to be queried then iterate over it to get the DB names and total number of times to run the iteration.
I then create a temptable where the needed data will be inserted into.
I run the iteration to grab the information and push it into the temptable for each database.
After the iteration finishes I'm not able to pull the values from this newly created table.
I wrote a little comment next to each portion of code explaining what I'm trying to do and what I expect to happen.
/*check if the #databases table is already present and then drop it*/
IF OBJECT_ID('tempdb..#databases', 'U') IS NOT NULL
begin
drop table #databases;
end
select ArtifactID into #databases from edds.eddsdbo.[Case]
where name like '%Review%'
/*Once this first statement has been run there will now be a
number column that is associated with the artificatID. Each database has an area that is
titled [EDDS'artifactID']. So if the artifactID = 1111111 then the DB would
be accessed at [EDDS1111111]*/
declare #runs int = 1; /*this will track the number of times iterated
over the result set*/
declare #max int = 0; /*this will be the limit*/
declare #databasename sysname='' /*this will allow the population of each
database name*/
/*check if your temp table exists and drop if necessary*/
IF OBJECT_ID('tempdb..#temptable', 'U') IS NOT NULL
begin
drop table #temptable;
end
/*create the temp table as outside the loop*/
create table #temptable(
fileSize dec,
extractedTextSize dec
)
while #runs<=#max
begin
select #max=count(*) from #databases;
/*the #max is now the number of databases inserted in to this table*/
/*This select statement pulls the information that will be placed
into the temptable. This second statment should be inside the loop. One time
for each DB that appeared in the first query's results.*/
/*begin the loop by assigning your database name, I don't know what the
column is called so I have just called it databasename for now*/
select top 1 #databasename = ArtifactID from #databases;
/*generate your sql using the #databasename variable, if you want to make
the database and table names dynamic too then you can use the same formula*/
insert into #temptable
select SUM(fileSize)/1024/1024/1024, SUM(extractedTextSize)/1024/1024
FROM [EDDS'+cast(#databasename as nvarchar(128))+'].[EDDSDBO].[Document] ed
where ed.CreatedDate >= (select CONVERT(varchar,dateadd(d,-
(day(getdate())),getdate()),106))'
/*remove that row from the databases table so the row won't be redone
This will take the #max and lessen it by one*/
delete from #databases where ArtifactID=#databasename;
/* Once the #max is less than 1 then end the loop*/
end
/* Query the final values in the temp table after the iteration is complete*/
select filesize+extractedTextSize as Gigs from #temptable
When that final select statement runs to pull values from #temptable the response is a single gigs column(as expected) but the table itself is blank.
Something is happening to clear the data out of the table and I'm stuck.
I'm not sure if my error is in syntax or a general error of logic but any help would be greatly appreciated.
Made a few tweaks to formatting but main issue is your loop would never run.
You have #runs <= #max, but #max = 1 and #runs = 0 at start so it will never loop
To fix this you can do a couple different things but I set the #max before loop, and in the loop just added 1 to #runs each loop, since you know how many you need #max before loop runs, and just add it to number of runs and do your compare.
But one NOTE there are much better ways to do this then the way you have. Put identity on your #databases table, and in your loop just do where databaseID = loopCount (then you dont have to delete from the table)
--check if the #databases table is already present and then drop it
IF OBJECT_ID('tempdb..#databases', 'U') IS NOT NULL
drop table #databases;
--Once this first statement has been run there will now be a number column that is associated with the artificatID. Each database has an area that is
-- titled [EDDS'artifactID']. So if the artifactID = 1111111 then the DB would be accessed at [EDDS1111111]
select ArtifactID
INTO #databases
FROM edds.eddsdbo.[Case]
where name like '%Review%'
-- set to 0 to start
DECLARE #runs int = 0;
--this will be the limit
DECLARE #max int = 0;
--this will allow the population of each database name
DECLARE #databasename sysname = ''
--check if your temp table exists and drop if necessary
IF OBJECT_ID('tempdb..#temptable', 'U') IS NOT NULL
drop table #temptable;
--create the temp table as outside the loop
create table #temptable(
fileSize dec,
extractedTextSize dec
)
-- ***********************************************
-- Need to set the value your looping on before you get to your loop, also so if you dont have any you wont do your loop
-- ***********************************************
--the #max is now the number of databases inserted in to this table
select #max = COUNT(*)
FROM #databases;
while #runs <= #max
BEGIN
/*This select statement pulls the information that will be placed
into the temptable. This second statment should be inside the loop. One time
for each DB that appeared in the first query's results.*/
/*begin the loop by assigning your database name, I don't know what the
column is called so I have just called it databasename for now*/
select top 1 #databasename = ArtifactID from #databases;
/*generate your sql using the #databasename variable, if you want to make
the database and table names dynamic too then you can use the same formula*/
insert into #temptable
select SUM(fileSize)/1024/1024/1024, SUM(extractedTextSize)/1024/1024
FROM [EDDS'+cast(#databasename as nvarchar(128))+'].[EDDSDBO].[Document] ed
where ed.CreatedDate >= (select CONVERT(varchar,dateadd(d,- (day(getdate())),getdate()),106))
--remove that row from the databases table so the row won't be redone This will take the #max and lessen it by one
delete from #databases where ArtifactID=#databasename;
--Once the #max is less than 1 then end the loop
-- ***********************************************
-- no need to select from the table and change your max value, just change your runs by adding one for each run
-- ***********************************************
--the #max is now the number of databases inserted in to this table
select #runs = #runs + 1 --#max=count(*) from #databases;
end
-- Query the final values in the temp table after the iteration is complete
select filesize+extractedTextSize as Gigs from #temptable
This is second answer, but its alternative to like I mentioned in above and cleaner to post to post as an alternative answer to keep them seperate
This is a better way to do your looping (not fully tested out yet so you will have to verify).
But instead of deleting from your table just add an ID to it and loop through it using that ID. Way less steps and much cleaner.
--check if the #databases table is already present and then drop it
IF OBJECT_ID('tempdb..#databases', 'U') IS NOT NULL
drop table #databases;
--create the temp table as outside the loop
create table #databases(
ID INT IDENTITY,
ArtifactID VARCHAR(20) -- not sure of this ID's data type
)
--check if your temp table exists and drop if necessary
IF OBJECT_ID('tempdb..#temptable', 'U') IS NOT NULL
drop table #temptable;
--create the temp table as outside the loop
create table #temptable(
fileSize dec,
extractedTextSize dec
)
--this will allow the population of each database name
DECLARE #databasename sysname = ''
-- initialze to 1 so it matches first record in temp table
DECLARE #LoopOn int = 1;
--this will be the max count from table
DECLARE #MaxCount int = 0;
--Once this first statement has been run there will now be a number column that is associated with the artificatID. Each database has an area that is
-- titled [EDDS'artifactID']. So if the artifactID = 1111111 then the DB would be accessed at [EDDS1111111]
-- do insert here so it adds the ID column
INSERT INTO #databases(
ArtifactID
)
SELECT ArtifactID
FROM edds.eddsdbo.[Case]
where name like '%Review%'
-- sets the max number of loops we are going to do
select #MaxCount = COUNT(*)
FROM #databases;
while #LoopOn <= #MaxCount
BEGIN
-- your table has IDENTITY so select the one for the loop your on (initalize to 1)
select #databasename = ArtifactID
FROM #databases
WHERE ID = #LoopOn;
--generate your sql using the #databasename variable, if you want to make
--the database and table names dynamic too then you can use the same formula
insert into #temptable
select SUM(fileSize)/1024/1024/1024, SUM(extractedTextSize)/1024/1024
-- dont know/think this will work like this? If not you have to use dynamic SQL
FROM [EDDS'+cast(#databasename as nvarchar(128))+'].[EDDSDBO].[Document] ed
where ed.CreatedDate >= (select CONVERT(varchar,dateadd(d,- (day(getdate())),getdate()),106))
-- remove all deletes/etc and just add one to the #LoopOn and it will be selected above based off the ID
select #LoopOn += 1
end
-- Query the final values in the temp table after the iteration is complete
select filesize+extractedTextSize as Gigs
FROM #temptable

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.

Get Key values of the Row whose values are not Updated during Multiple row update

This is a Continuation of my previous question
sql update for dynamic row number
This time I am having an updated requirement.
I am having 2 tables
CraftTypes & EmployeeCraftTypes.
I need to update multiple rows in the CraftType Table and
I was able to update it as per the answer provided by TheGameiswar
Now there is a modification in the requirement.
In the table CraftTypes, there is a foreign key reference for the column CraftTypeKey with the table EmployeeCraftsTypes.
If there exist an entry for CraftTypeKey in the EmployeeCrafttypes table, then the row should not be updated.
Also the CraftTypeKey's whose row values are not updated must be obtained for returning the FK_restriction status of the rows.
This is the sql query I am using.
CREATE TYPE [DBO].[DEPARTMENTTABLETYPE] AS TABLE
( DepartmentTypeKey SMALLINT, DepartmentTypeName VARCHAR(50),DepartmentTypeCode VARCHAR(10) , DepartmentTypeDescription VARCHAR(128) )
ALTER PROCEDURE [dbo].[usp_UpdateDepartmentType]
#DEPARTMENTDETAILS [DBO].[DEPARTMENTTABLETYPE] READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #rowcount1 INT
BEGIN
BEGIN TRY
BEGIN TRANSACTION
UPDATE D1
SET
D1.[DepartmentTypeName]=D2.DepartmentTypeName
,D1.[DepartmentTypeCode]=D2.DepartmentTypeCode
,D1.[DepartmentTypeDescription]=D2.DepartmentTypeDescription
FROM
[dbo].[DepartmentTypes] D1
INNER JOIN
#DEPARTMENTDETAILS D2
ON
D1.DepartmentTypeKey=D2.DepartmentTypeKey
WHERE
D2.[DepartmentTypeKey] not in (select 1 from [dbo].[EmployeeDepartment] where [DepartmentTypeKey]=D2.DepartmentTypeKey)
SET #ROWCOUNT1=##ROWCOUNT
COMMIT
END TRY
BEGIN CATCH
SET #ROWCOUNT1=0
ROLLBACK TRAN
END CATCH
IF #rowcount1 =0
SELECT -174;
ELSE
SELECT 100;
END
END
Please Help
And Thanks in Advance
Ok
I think I figured out a way for it this time. I am not sure this is the right way, but its enough for me to meet the requirements.
I selected the distinct rows with FK reference from EmployeeCraftsTypes table as a second select query.
Now I can get the Row values which are not getting updated due to FK constraint.
This is the sql query I have used
ALTER PROCEDURE [dbo].[usp_UpdateCraftType]
#CRAFTDETAILS [DBO].[CRAFTTABLETYPE] READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #STATUSKEY TINYINT = (SELECT DBO.GETSTATUSKEY('ACTIVE'))
DECLARE #ROWCOUNT1 INT
BEGIN
BEGIN TRY
BEGIN TRANSACTION
UPDATE C1
SET
[C1].[CraftTypeName]=C2.CRAFTTYPENAME
,[C1].[CRAFTTYPEDESCRIPTION]=C2.CRAFTTYPEDESCRIPTION
,[C1].[StatusKey]=C2.[StatusKey]
FROM
[dbo].[CRAFTTYPES] C1
INNER JOIN
#CRAFTDETAILS C2
ON
C1.CRAFTTYPEKEY=C2.CRAFTTYPEKEY
WHERE
C2.[CRAFTTYPEKEY] NOT IN (SELECT EC.[CRAFTTYPEKEY] from [dbo].[EmployeeCrafts] EC where EC.[CRAFTTYPEKEY]=C2.[CRAFTTYPEKEY])
SET #ROWCOUNT1=##ROWCOUNT
COMMIT
END TRY
BEGIN CATCH
SET #ROWCOUNT1=0
ROLLBACK TRAN
END CATCH
--SET #ROWCOUNT1 = ##ROWCOUNT
IF #ROWCOUNT1 =0
SELECT -172;
ELSE
BEGIN
SELECT 100;
SELECT DISTINCT EC.[CRAFTTYPEKEY],'Value Already Assigned' as Reason
FROM [DBO].[EmployeeCrafts] EC
JOIN #CRAFTDETAILS C3
on C3.[CRAFTTYPEKEY]=EC.[CRAFTTYPEKEY]
WHERE EC.[CRAFTTYPEKEY]=C3.[CRAFTTYPEKEY]
END
END
END
Now in the Web API side I can check if there is any update failure by checking the rowcount for the second table.
If the row count is more than 0, then update error message can be generated
Hope it will be helpful to someone ....

Stored Procedure for SQL Server

I have written this stored procedure and it executes but it doesn't update the customer. The question is: Create a procedure named prc_cus_balance_update that will take the invoice number as a parameter and update the customer balance.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE PRC_CUS_BALANCE_UPDATE3
#INV_NUMBER INT
AS
BEGIN
DECLARE #CUS_CODE INT
SELECT #CUS_CODE=CUS_CODE
FROM INVOICE
WHERE #INV_NUMBER=INV_NUMBER
UPDATE CUSTOMER
SET CUS_BALANCE=CUS_BALANCE +
(SELECT INV_TOTAL FROM INVOICE WHERE #INV_NUMBER=INV_NUMBER)
WHERE #CUS_CODE=CUS_CODE
END
GO
While developing, I'd put in some "extras" to figure out what is going on.
Pseudo code below.
You want to make sure you found a matching row.
And you want to make sure at least one row was actually updated.
I'm NOT saying the code below is "production ready". But will show the concepts.
CREATE PROCEDURE PRC_CUS_BALANCE_UPDATE3
#INV_NUMBER INT
AS
BEGIN
DECLARE #CUS_CODE INT
DECLARE #MYROWCOUNT INT
SELECT #CUS_CODE=CUS_CODE
FROM INVOICE
WHERE #INV_NUMBER=INV_NUMBER
if(Not(#CUS_CODE IS NULL))
BEGIN
SET NOCOUNT OFF
UPDATE CUSTOMER
SET CUS_BALANCE=CUS_BALANCE +
(SELECT INV_TOTAL FROM INVOICE WHERE #INV_NUMBER=INV_NUMBER)
WHERE #CUS_CODE=CUS_CODE
select #MYROWCOUNT = ##ROWCOUNT
if(#MYROWCOUNT <=0)
BEGIN
print 'No row updated. :<'
END
SET NOCOUNT OFF
END
ELSE
BEGIN
print "#CUS_CODE match not found."
END
END
GO
Try putting some better bulletproofing in. Multiple rows, null values, and the like, can all cause problems with how you currently have it. Here's a stab at it, without me knowing the specifics of your data model (it may not be appropriate to sum together the totals from invoices, I'm just saying you could have multiple rows and need to deal with that).
CREATE PROCEDURE PRC_CUS_BALANCE_UPDATE3
#INV_NUMBER INT
AS
BEGIN
DECLARE #CUS_CODE INT
SELECT
TOP 1 #CUS_CODE = CUS_CODE
FROM
INVOICE
WHERE
INV_NUMBER=#INV_NUMBER
IF #CUS_CODE IS NOT NULL
BEGIN
UPDATE
CUSTOMER
SET
CUS_BALANCE = ISNULL(CUS_BALANCE, 0.0) +
ISNULL(
(SELECT
SUM(INV_TOTAL)
FROM
INVOICE
WHERE
#INV_NUMBER = INV_NUMBER),
0.0)
WHERE
CUS_CODE = #CUS_CODE
END
END
GO

Resources