I am trying to write a stored procedure to validate data before inserted into a table.
I have a CHANGES table that a user will be inserting values into (basically if there is wrong data in certain tables/columns in our system, this user will send us the correct data to update certain columns). Before the data can be inserted into this CHANGES table, I would like to validate the input data against a LOOKUP table. This lookup table essentially shows what is the accepted data type, length of column etc before insert.
Here is the lookup table:
ID
TABLENAME
COLUMNAME
ACCEPTEDDATATYPE
ACCEPTEDLENGTH
1
EMPLOYEE
AGE
INT
2
EMPLOYEE
MIDDLENAME
VARCHAR
50
3
DEPARTMENT
BLDG
VARCHAR
10
Basically ID is the unique identifier, so if a user is inserting an ID of 1, we know they are trying to insert data for the employee table and making a correction to the AGE column, and in this case we only want to accept an int value.
Here is the structure of the CHANGES table:
ID
PKFROMTABLE
NEWVALUE
USERNAME
1
234
39
sk
1
345
john
jf
2
455
BIOL
jf
I would like to wrap this in a stored procedure: This is what I have so far
CREATE PROCEDURE [dbo].[sp_CleanupData]
(
#ID int,
#uniqueid VARCHAR(40),
#value VARCHAR(50)
)
AS
BEGIN TRANSACTION AddLoopback
DECLARE #columnlength int;
DECLARE #columndatatype varchar(30);
set #columnlength=(select acceptedlength from lookup where ID=#ID)
set #columndatatype=(select accepteddatatype from lookup where ID=#ID)
IF len(#value) <= #columnlength
BEGIN
INSERT INTO [CHANGES] (ID, pkfromtable,newvalue,USERNAME) VALUES (#ID, #uniqueid, #value,SUSER_SNAME())
PRINT 'New Record Inserted'
COMMIT TRANSACTION
END
ELSE
BEGIN
ROLLBACK TRANSACTION AddLoopback
PRINT 'id is not acceptable'
END
GO
How would I add validation for the datatype? And how would I make sure all validation passes before the insert? If any validation would fail per record, I do not want that record inserted into the CHANGES tables.
You can just check each datatype, do something like this
DECLARE #result BIT = 0;
IF #columndatatype = 'INT'
SET #result = IIF(TRY_CAST(#value AS INT) IS NULL, 0, 1)
ELSE IF #columndatatype = 'BIGINT'
SET #result = IIF(TRY_CAST(#value AS BIGINT) IS NULL, 0, 1)
ELSE IF #columndatatype = 'VARCHAR'
SET #result = IIF(len(#value) <= #columnlength, 0, 1)
SELECT
#result
If the cast fails, the result of the cast is null, and you set you #result to 0.
When you've finsihed your check you know if it is the correct format.
You could of course do this in a dynamic way, using sp_executesql as well.
Related
I have several days trying to solve this problem, but my lack of knowledge is stopping me, I don’t know if is possible what I am trying to accomplish.
I need to have a table like this:
The first field should be a custom primary key ID (auto incremented):
YYYYMMDD-99
Where YYYMMDD is the actual day and “99” is a counter that should be incremented automatically from 01 to 99 in every new row added and need to be automatically restarted to 01 the next day.
The second field is a regular NVARCHAR(40) text field called: Name
For example, I add three rows, just introducing the “Name” of the person, the ID is automatically added:
ID Name
---------------------------
20160629-01 John
20160629-02 Katie
20160629-03 Mark
Then, the next day I add two new rows:
ID Name
-------------------------
20160630-01 Bob
20160630-02 Dave
The last two digits should be restarted, after the day changes.
And, what is all this about ?
Answer: Customer requirement.
If is possible to do it in a stored procedure, it will works for me too.
Thanks in advance!!
This is pretty easy to achieve, but a bit complicated to do so it is safe with multiple clients.
What you need is a new table (for example named IndexHelper) that actually stores the parts of the index as it should be using two columns: One has the current date properly formatted as you want it in your index and one is the current index as integer. Example:
DateString CurrentIndex
-------------------------------
20160629 13
Now you need some code that helps you get the next index value atomically, i.e. in a way that also works when more than one client try to insert at the same time without getting the same index more than once.
T-SQL comes to the rescue with its UPDATE ... OUTPUT clause, which allows you to update a table, at the same time outputting the new values as an atomic operation, which can not be interrupted.
In your case, this statement could look like this:
DECLARE #curDay NVARCHAR(10)
DELCARE #curIndex INT
DECLARE #tempTable TABLE (theDay NVARCHAR(10), theIndex INT)
UPDATE IndexHelper SET CurrentIndex = CurrentIndex + 1 OUTPUT INSERTED.DateString, INSERTED.CurrentIndex INTO #temptable WHERE CurrentDate = <code that converts CURRENT_TIMESTAMP into the string format you want>
SELECT #curDay = theDay, #curIndex = theIndex FROM #tempTable
Unfortunately you have to go the temporary table way, as it is demanded by the OUTPUT clause.
This increments the CurrentIndex field in IndexHelper atomically for the current date. You can combine both into a value like this:
DECLARE #newIndexValue NVARCHAR(15)
SET #newIndexValue = #curDay + '-' + RIGHT('00' + CONVERT(NVARCHAR, #curIndex), 2)
Now the question is: How do you handle the "go back to 01 for the next day" requirement? Also easy: Add entries into IndexHelper for 2 days in advance with the respective date and index 0. You can do this safely everytime your code is called if you check that an entry for a day is actually missing. So for today your table might look like this:
DateString CurrentIndex
-------------------------------
20160629 13
20160630 0
20160701 0
The first call tomorrow would make this look like:
DateString CurrentIndex
-------------------------------
20160629 13
20160630 1
20160701 0
20160702 0
Wrap this up into a stored procedure that does the entire INSERT process into your original table, what you get is:
Add missing entries for the next two days to IndexHelper table.
Get the next ID atomically as described above
Combine date string and ID from the UPDATE command into a single string
Use this in the INSERT command for your actual data
This results in the following stored procedure you can use to insert your data:
-- This is our "work date"
DECLARE #now DATETIME = CURRENT_DATETIME
-- These are the date strings that we need
DECLARE #today NVARCHAR(10) = CONVERT(NVARCHAR, #now, 112)
DECLARE #tomorrow NVARCHAR(10) = CONVERT(NVARCHAR, DATEADD(dd, 1, #now), 112)
DECLARE #datomorrow NVARCHAR(10) = CONVERT(NVARCHAR, DATEADD(dd, 2, #now), 112)
-- We will need these later
DECLARE #curDay NVARCHAR(10)
DELCARE #curIndex INT
DECLARE #tempTable TABLE (theDay NVARCHAR(10), theIndex INT)
DECLARE #newIndexValue NVARCHAR(15)
-- Add entries for next two days into table
-- NOTE: THIS IS NOT ATOMIC! SUPPOSED YOU HAVE A PK ON DATESTRING, THIS
-- MAY EVEN FAIL! THAT'S WHY IS USE BEGIN TRY
BEGIN TRY
IF NOT EXISTS (SELECT 1 FROM IndexHelper WHERE DateString = #tomorrow)
INSERT INTO IndexHelper (#tomorrow, 0)
END TRY
BEGIN CATCH
PRINT 'hmpf'
END CATCH
BEGIN TRY
IF NOT EXISTS (SELECT 1 FROM IndexHelper WHERE DateString = #datomorrow)
INSERT INTO IndexHelper (#datomorrow, 0)
END TRY
BEGIN CATCH
PRINT 'hmpf again'
END CATCH
-- Now perform the atomic update
UPDATE IndexHelper
SET
CurrentIndex = CurrentIndex + 1
OUTPUT
INSERTED.DateString,
INSERTED.CurrentIndex
INTO #temptable
WHERE CurrentDate = #today
-- Get the values after the update
SELECT #curDay = theDay, #curIndex = theIndex FROM #tempTable
-- Combine these into the new index value
SET #newIndexValue = #curDay + '-' + RIGHT('00' + CONVERT(NVARCHAR, #curIndex), 2)
-- PERFORM THE INSERT HERE!!
...
One way to achieve customised auto increment is using INSTEAD OF trigger in SQL Server.
https://msdn.microsoft.com/en-IN/library/ms189799.aspx
I have tested this using below code.
This might be helpful.
It is written with the assumption that maximum 99 records will be inserted in a given day.
You will have to modify it to handle more than 99 records.
CREATE TABLE dbo.CustomerTb(
ID VARCHAR(50),
Name VARCHAR(50)
)
GO
CREATE TRIGGER dbo.InsertCustomerTrigger ON dbo.CustomerTb INSTEAD OF INSERT
AS
BEGIN
DECLARE #MaxID SMALLINT=0;
SELECT #MaxID=ISNULL(MAX(RIGHT(ID,2)),0)
FROM dbo.CustomerTb
WHERE LEFT(ID,8)=FORMAT(GETDATE(),'yyyyMMdd');
INSERT INTO dbo.CustomerTb(
ID,
Name
)
SELECT FORMAT(GETDATE(),'yyyyMMdd')+'-'+RIGHT('00'+CONVERT(VARCHAR(5),ROW_NUMBER() OVER(ORDER BY Name)+#MaxID),2),
Name
FROM inserted;
END
GO
TEST CASE 1
INSERT INTO dbo.CustomerTb(NAME) VALUES('A'),('B');
SELECT * FROM dbo.CustomerTb;
TEST CASE 2
INSERT INTO dbo.CustomerTb(NAME) VALUES('P'),('Q');
SELECT * FROM dbo.CustomerTb;
I have a column called IdNumber in my Client table. Each client was assigned an IDNumber of ID100 (and it increments by one every time I add a new client). I had to add an A to the end of it, so all new values are ID100A, ID101A, ID102A and so on.
How would I go about updating all the old ID's so that they have the A added to it?
Thanks!
If you meant how to update existing values:
UPDATE ClientTable
SET IDNumber = IDNumber + 'A'
EDIT Just in case
WHERE IDNumber NOT LIKE '%A'
END EDIT
Of course you should not exceed column length
Otherwise if you have to insert new values:
DECLARE #MaxValue nvarchar(64)
DECLARE #NewValue nvarchar(64)
DECLARE #CurValue int
SELECT #MaxValue = MAX(IDNumber) FROM ClientTable
SELECT #CurValue = CONVERT (SUBSTRING(#MaxValue, 2, LEN(#MaxValue) - 3), int)
SELECT #NewValue = 'ID' + CONVERT(nvarchar(61), #CurValue) + 'A'
cannot test right now, but it should work
I want to copy data from one table (rawdata, all columns are VARCHAR) to another table (formatted with corresponding column format).
For copying data from the rawdata table into formatted table, I'm using cursor in order to identify which row is affected. I need to log that particular row in an error log table, skip it, and continue copying remaining rows.
It takes more time to copying. Is there any other way to achieve this?
this is my query
DECLARE #EntityId Varchar(16) ,
#PerfId Varchar(16),
#BaseId Varchar(16) ,
#UpdateStatus Varchar(16)
DECLARE CursorSample CURSOR FOR
SELECT EntityId, PerfId, BaseId, #UpdateStatus
FROM RawdataTable
--Returns 204,000 rows
OPEN CursorSample
FETCH NEXT FROM CursorSample INTO #EntityId,#PerfId,#BaseId,#UpdateStatus
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
--try insertting row in formatted table
Insert into FormattedTable
(EntityId,PerfId,BaseId,UpdateStatus)
Values
(Convert(int,#EntityId),
Convert(int,#PerfId),
Convert(int,#BaseId),
Convert(int,#UpdateStatus))
END TRY
BEGIN CATCH
--capture Error EntityId in errorlog table
Insert into ERROR_LOG
(TableError_Message,Error_Procedure,Error_Log_Time)
Values
(Error_Message()+#EntityId,’xxx’, GETDATE())
END CATCH
FETCH NEXT FROM outerCursor INTO #EntityId, #BaseId
END
CLOSE CursorSample
DEALLOCATE CursorSampler –cleanup CursorSample
You should just be able to use a INSERT INTO statement to put the records directly into the formatted table. INSERT INTO will perform much better than using a cursor.
INSERT INTO FormattedTable
SELECT
CONVERT(int, EntityId),
CONVERT(int, PerfId),
CONVERT(int, BaseId),
CONVERT(int, UpdateStatus)
FROM RawdataTable
WHERE
IsNumeric(EntityId) = 1
AND IsNumeric(PerfId) = 1
AND IsNumeric(BaseId) = 1
AND IsNumeric(UpdateStatus) = 1
Note that IsNumeric can sometimes return 1 for values that will then fail on CONVERT. For example, IsNumeric('$e0') will return 1, so you may need to create a more robust user defined function for determining if a string is a number, depending on your data.
Also, if you need a log of all records that could not be moved into the formatted table, just modify the WHERE clause:
INSERT INTO ErrorLog
SELECT
EntityId,
PerfId,
BaseId,
UpdateStatus
FROM RawdataTable
WHERE
NOT (IsNumeric(EntityId) = 1
AND IsNumeric(PerfId) = 1
AND IsNumeric(BaseId) = 1
AND IsNumeric(UpdateStatus) = 1)
EDIT
Rather than using IsNumeric directly, it may be better to create a custom UDF that will tell you if a string can be converted to an int. This function worked for me (albeit with limited testing):
CREATE FUNCTION IsInt(#value VARCHAR(50))
RETURNS bit
AS
BEGIN
DECLARE #number AS INT
DECLARE #numeric AS NUMERIC(18,2)
SET #number = 0
IF IsNumeric(#value) = 1
BEGIN
SET #numeric = CONVERT(NUMERIC(18,2), #value)
IF #numeric BETWEEN -2147483648 AND 2147483647
SET #number = CONVERT(INT, #numeric)
END
RETURN #number
END
GO
The updated SQL for the insert into the formatted table would then look like this:
INSERT INTO FormattedTable
SELECT
CONVERT(int, CONVERT(NUMERIC(18,2), EntityId)),
CONVERT(int, CONVERT(NUMERIC(18,2), PerfId)),
CONVERT(int, CONVERT(NUMERIC(18,2), BaseId)),
CONVERT(int, CONVERT(NUMERIC(18,2), UpdateStatus))
FROM RawdataTable
WHERE
dbo.IsInt(EntityId) = 1
AND dbo.IsInt(PerfId) = 1
AND dbo.IsInt(BaseId) = 1
AND dbo.IsInt(UpdateStatus) = 1
There may be a little weirdness around handling NULLs (my function will return 0 if NULL is passed in, even though an INT can certainly be null), but that can be adjusted depending on what is supposed to happen with NULL values in the RawdataTable.
You can put a WHERE clause in your cursor definition so that only valid records are selected in the first place. You might need to create a function to determine validity, but it should be faster than looping over them.
Actually, you might want to create a temp table of the invalid records, so that you can log the errors, then define the cursor only on the rows that are not in the temp table.
Insert into will work much more better than Cursor.
As Cursor work solely in Memory of your PC and slows down the optimization of SQL Server. We should avoid using Cursors but (of course) there are situations where usage of Cursor cannot be avoided.
I am creating a customer table with a parent table that is company.
It has been dictated(chagrin) that I shall create a primary key for the customer table that is a combination of the company id which is an existing varchar(4) column in the customer table, e.g. customer.company
The rest of the varchar(9) primary key shall be a zero padded counter incrementing through the number of customers within that company.
E.g. where company = MSFT and this is a first insert of an MSFT record: the PK shall be MSFT00001
on subsequent inserts the PK would be MSFT00001, MSFT00002 etc.
Then when company = INTL and its first record is inserted, the first record would be INTL00001
I began with an instead of trigger and a udf that I created from other stackoverflow responses.
ALTER FUNCTION [dbo].[GetNextID]
(
#in varchar(9)
)
RETURNS varchar(9) AS
BEGIN
DECLARE #prefix varchar(9);
DECLARE #res varchar(9);
DECLARE #pad varchar(9);
DECLARE #num int;
DECLARE #start int;
if LEN(#in)<9
begin
set #in = Left(#in + replicate('0',9) , 9)
end
SET #start = PATINDEX('%[0-9]%',#in);
SET #prefix = LEFT(#in, #start - 1 );
declare #tmp int;
set #tmp = len(#in)
declare #tmpvarchar varchar(9);
set #tmpvarchar = RIGHT( #in, LEN(#in) - #start + 1 )
SET #num = CAST( RIGHT( #in, LEN(#in) - #start + 1 ) AS int ) + 1
SET #pad = REPLICATE( '0', 9 - LEN(#prefix) - CEILING(LOG(#num)/LOG(10)) );
SET #res = #prefix + #pad + CAST( #num AS varchar);
RETURN #res
END
How would I write my instead of trigger to insert the values and increment this primary key. Or should I give it up and start a lawnmowing business?
Sorry for that tmpvarchar variable SQL server was giving me strange results without it.
Whilst I agree with the naysayers, the principle of "accepting that which cannot be changed" tends to lower the overall stress level, IMHO. Try the following approach.
Disadvantages
Single-row inserts only. You won't be doing any bulk inserts to your new customer table as you'll need to execute the stored procedure each time you want to insert a row.
A certain amount of contention for the key generation table, hence a potential for blocking.
On the up side, though, this approach doesn't have any race conditions associated with it, and it isn't too egregious a hack to really and truly offend my sensibilities. So...
First, start with a key generation table. It will contain 1 row for each company, containing your company identifier and an integer counter that we'll be bumping up each time an insert is performed.
create table dbo.CustomerNumberGenerator
(
company varchar(8) not null ,
curr_value int not null default(1) ,
constraint CustomerNumberGenerator_PK primary key clustered ( company ) ,
)
Second, you'll need a stored procedure like this (in fact, you might want to integrate this logic into the stored procedure responsible for inserting the customer record. More on that in a bit). This stored procedure accepts a company identifier (e.g. 'MSFT') as its sole argument. This stored procedure does the following:
Puts the company id into canonical form (e.g. uppercase and trimmed of leading/trailing whitespace).
Inserts the row into the key generation table if it doesn't already exist (atomic operation).
In a single, atomic operation (update statement), the current value of the counter for the specified company is fetched and then incremented.
The customer number is then generated in the specified way and returned to the caller via a 1-row/1-column SELECT statement.
Here you go:
create procedure dbo.GetNewCustomerNumber
#company varchar(8)
as
set nocount on
set ansi_nulls on
set concat_null_yields_null on
set xact_abort on
declare
#customer_number varchar(32)
--
-- put the supplied key in canonical form
--
set #company = ltrim(rtrim(upper(#company)))
--
-- if the name isn't already defined in the table, define it.
--
insert dbo.CustomerNumberGenerator ( company )
select id = #company
where not exists ( select *
from dbo.CustomerNumberGenerator
where company = #company
)
--
-- now, an interlocked update to get the current value and increment the table
--
update CustomerNumberGenerator
set #customer_number = company + right( '00000000' + convert(varchar,curr_value) , 8 ) ,
curr_value = curr_value + 1
where company = #company
--
-- return the new unique value to the caller
--
select customer_number = #customer_number
return 0
go
The reason you might want to integrate this into the stored procedure that inserts a row into the customer table is that it makes globbing it all together into a single transaction; without that, your customer numbers may/will get gaps when an insert fails land gets rolled back.
As others said before me, using a primary key with calculated auto-increment values sounds like a very bad idea!
If you are allowed to and if you can live with the downsides (see at the bottom), I would suggest the following:
Use a normal numeric auto-increment key and a char(4) column which only contains the company id.
Then, when you select from the table, you use row_number on the auto-increment column and combine that with the company id so that you have an additional column with a "key" that looks like you wanted (MSFT00001, MSFT00002, ...)
Example data:
create table customers
(
Id int identity(1,1) not null,
Company char(4) not null,
CustomerName varchar(50) not null
)
insert into customers (Company, CustomerName) values ('MSFT','First MSFT customer')
insert into customers (Company, CustomerName) values ('MSFT','Second MSFT customer')
insert into customers (Company, CustomerName) values ('ABCD','First ABCD customer')
insert into customers (Company, CustomerName) values ('MSFT','Third MSFT customer')
insert into customers (Company, CustomerName) values ('ABCD','Second ABCD customer')
This will create a table that looks like this:
Id Company CustomerName
------------------------------------
1 MSFT First MSFT customer
2 MSFT Second MSFT customer
3 ABCD First ABCD customer
4 MSFT Third MSFT customer
5 ABCD Second ABCD customer
Now run the following query on it:
select
Company + right('00000' + cast(ROW_NUMBER() over (partition by Company order by Id) as varchar(5)),5) as SpecialKey,
*
from
customers
This returns the same table, but with an additional column with your "special key":
SpecialKey Id Company CustomerName
---------------------------------------------
ABCD00001 3 ABCD First ABCD customer
ABCD00002 5 ABCD Second ABCD customer
MSFT00001 1 MSFT First MSFT customer
MSFT00002 2 MSFT Second MSFT customer
MSFT00003 4 MSFT Third MSFT customer
You could create a view with this query and let everyone use that view, to make sure everyone sees the "special key" column.
However, this solution has two downsides:
You need at least SQL Server 2005 in
order for row_number to work.
The numbers in the special key will change when you delete companies from the table. So, if you don't want the numbers to change, you have to make sure that nothing is ever deleted from that table.
I currently have the following stored procedure;
CREATE PROCEDURE web.insertNewCampaign
(
#tmp_Id BIGINT,
#tmp_Title VARCHAR(100),
#tmp_Content VARCHAR(8000),
#tmp_Pledge DECIMAL(7,2),
--#tmp_Recipients BIGINT,
#tmp_Date DATETIME,
#tmp_Private BIT,
#tmp_Template BIGINT,
#tmp_AddyBook BIGINT
)
AS
declare #recipients BIGINT
declare #tmp_IDENTITY BIGINT
declare #fave BIGINT
declare #allocation VARCHAR(50)
--insert campaign data
BEGIN TRAN
SELECT #recipients = addMaster_NoRecipients FROM tbl_AddressBookMaster
WHERE addMaster_UserId = #tmp_Id AND addMaster_Key = #tmp_AddyBook;
INSERT INTO TBL_CAMPAIGNS ([campaign_MemberId], [campaign_Title], [campaign_Content], [campaign_Pledge], [campaign_Date], [campaign_Private], [campaign_Template], [campaign_AddressBook], [campaign_Recipients])
VALUES (#tmp_Id, #tmp_Title, #tmp_Content, #tmp_Pledge, #tmp_Date, #tmp_Private, #tmp_Template, #tmp_AddyBook, #recipients)
SELECT #tmp_IDENTITY = SCOPE_IDENTITY() --this returns the newly added IDENTITY ID
COMMIT
......
So i have 2 questions:
1) How do i divide #tmp_Pledge by #recipients to give #allocation eg:(#allocation = #tmp_Pledge / #recipients)
2) Is it possible to compound these statements into a more efficient statement(s) with #allocation effectively being inserted as a value into the column [campaign_RecipShare], and reducing the need for these declared variables?
Many Thanks for any help you can offer for either question.
;-)
After the first select, you can do this to set #allocation:
set #allocation = #tmp_pledge / #recepients
As for making it more efficient, it's already fairly efficient--you won't go through any less steps, but you can condense the code a bit:
INSERT INTO TBL_CAMPAIGNS (
[campaign_MemberId], [campaign_Title], [campaign_Content],
[campaign_Pledge], [campaign_Date], [campaign_Private],
[campaign_Template], [campaign_AddressBook], [campaign_Recipients],
[capmain_RecipShare])
SELECT
#tmp_Id, #tmp_Title, #tmp_Content,
#tmp_Pledge, #tmp_Date, #tmp_Private,
#tmp_Template, #tmp_AddyBook, addMaster_NoRecipients,
#tmp_Pledge / addMaster_NoReceipients as Allocation
FROM
tbl_AddressBookMaster
WHERE
addMaster_UserId = #tmp_Id
AND addMaster_Key = #tmp_AddyBook
SELECT #tmp_IDENTITY = SCOPE_IDENTITY() --this returns the newly added IDENTITY ID
This also removes the need for you calculating the #allocation member outside of the insert statement.
1) #tmp_pledge / #recepients - I'll assume allocation is a numeric field of some form in TBL_CAMPAIGNS holding a number in varchar is not a good idea.
2) You just need to build a select that returns all the values from the other table and the parameters matching the columns to insert into.
insert into TBL_CAMPAIGNS ([campaign_MemberId], [campaign_Title], [campaign_Content], [campaign_Pledge], [campaign_Date], [campaign_Private], [campaign_Template], [campaign_AddressBook], [campaign_Recipients], [campaign_allocation)
select #tmp_Id, #tmp_Title, #tmp_Content, #tmp_Pledge, #tmp_Date, #tmp_Private, #tmp_Template, #tmp_AddyBook, addMaster_NoRecipients, #tmp_pledge / addMaster_NoRecipients
FROM FROM tbl_AddressBookMaster
WHERE addMaster_UserId = #tmp_Id AND addMaster_Key = #tmp_AddyBook;
SELECT #tmp_IDENTITY = SCOPE_IDENTITY() --this returns the newly added IDENTITY ID
set #allocation = #tmp_pledge / (#recepients* 1.0)
You want to do that because othewise you will run into integer math and the result will round to an integer.