Stored procedure for finding out if student is eligible for course with pre reqs - sql

I have this stored procedure for a course registration system I am working on. My intention is to return a value of -1 if the query returns a course which a student has not taken according to the course pre requisites.
The pre req table only has two columns; CourseID being the course and PreReqCourse_ID being the required course for that specified course. If the student has taken all pre req courses then it should return a value of 1. I keep getting a value of -1 even when I run the query for a student who has taken the required pre req courses. Any help would be much appreciated!
CREATE PROCEDURE CheckPreReq
#StudentID INT,
#CourseID INT
AS
DECLARE #theCount INT
IF EXISTS (SELECT *
FROM PreReq
INNER JOIN Student_History ON (PreReq.Course_ID = #CourseID)
WHERE Student_History.Course_ID != PreReq.PreReqCourse_ID
AND Student_History.Student_ID = #StudentID)
BEGIN
SET #theCount =-1
END
ELSE
BEGIN
SET #theCount = 1
END
RETURN #theCount

Would something like this work?
DECLARE #PreReqsTotal tinyint
DECLARE #PreReqsFulfilled tinyint
-- Count pre-req courses.
SELECT #PreReqsTotal = COUNT(*)
FROM PreReq
WHERE [CourseID] = #CourseId
-- Count how many fulfilled by student.
SELECT #PreReqsFulfilled = count(*)
FROM Student_History hist
JOIN PreReq pre
on hist.Course_ID = pre.PreReqCourse_ID
WHERE pre.CourseID = #CourseID
and hist.Student_ID = #StudentID
RETURN CASE WHEN #PreReqsTotal = #PreReqsFulfilled THEN 1 ELSE -1 END
...or something like this:
IF EXISTS
(
SELECT blah.*
FROM
(
SELECT pre.*
,[PreFulfilled] = CASE WHEN hist.Course_ID is null THEN 0 ELSE 1 END
FROM PreReq pre
LEFT JOIN
Student_History hist
on pre.PreReqCourse_ID = hist.Course_ID
and hist.Student_ID = #StudentID
WHERE pre.CourseID = #CourseID
) blah
WHERE blah.[PreFulfilled] = 0 -- Unfulfilled PreReq.
)
BEGIN
RETURN -1 -- Has an unfulfilled PreReq.
END
RETURN 1 -- No unfulfilled PreReqs.

You should JOIN the PreReq table with the Student_History on the the PreReq.PreReqCourse_ID and Student_History.CourseID columns (take a look at my example). Then your SP should work.
--create tmp example tables
IF OBJECT_ID('tempdb..#PreReq') IS NOT NULL DROP TABLE #PreReq
CREATE TABLE #PreReq(
CourseID int,
PreReqCourse_ID int
)
--insert Course 3 which depends on Course 2 and 1 in #PreReq
INSERT INTO #PreReq
values(3,2),(3,1)
IF OBJECT_ID('tempdb..#Student_History') IS NOT NULL DROP TABLE #Student_History
CREATE TABLE #Student_History(
CourseID int not null,
StudentID int not null
);
--insert Student 1 who has visited Course 1 and 2
insert into #Student_History
VALUES(1,1),(2,1)
--declare variables
DECLARE #CourseID AS INT = 3
,#StudentID AS INT = 1
--COUNT on how many Courses #CourseID depends
,#necessaryCourses AS INT
--COUNT on how many Courses the Student has taken
,#countTakenCourses AS INT
,#theCount AS INT
SET #necessaryCourses = (SELECT count(*) FROM #PreReq WHERE CourseID = #CourseID);
SET #countTakenCourses = (
SELECT count(*)
FROM #PreReq p
--JOIN with Student_History to check if the student has visited the necessary course
JOIN #Student_History h on p.PreReqCourse_ID = h.CourseID
WHERE p.CourseID = #CourseID AND h.StudentID = #StudentID
)
IF #necessaryCourses = #countTakenCourses
BEGIN
set #theCount = 1
END
ELSE
BEGIN
set #theCount = -1
END
SELECT #theCount AS theCount

Related

Sending SQL a boolean and update a table as 1 or -1 and how to "update" an empty row for the initial values

I have a Songs table with a likes column that holds the number of likes users sent . Each user sends a boolean (1 or 0) through a C# app which adds to the likes column.
About my procedure:
I want to know if there is more an efficient and short way of writing the part 1 of the function?
I had to manually insert '0' instead of the NULL for the first time for the function to work. It wasn't working because the initial value for Likes column is NULL. Is there a way to affect the row for the first time when it has NULL in it?
For part 2 of the function with [Users_Likes_Songs] table, I want to update if the user send a like (true = 1) or removed it (false = 0).
How can I update this table for the first time when the users 'like' must be valued as '1', when its rows are completely empty?
I thank you very much if you can help me.
The procedure:
CREATE PROCEDURE Songs_Likes
#User_ID INT,
#SongID INT,
#Song_like BIT
AS
BEGIN
--- part 1 of the function
IF (#Song_like = 1)
BEGIN
UPDATE [Songs]
SET [Likes] = [Likes] + #Song_like
WHERE [Song_ID] = #SongID
END
IF (#Song_like = 0)
BEGIN
UPDATE [Songs]
SET [Likes] = [Likes] - 1
WHERE [Song_ID] = #SongID
END
--- part 2 of the function with the second table
UPDATE [Users_Likes_Songs]
SET [LikeSong] = #Song_like
WHERE ([UserID] = #User_ID) AND ([SongID] = #SongID)
END
I think that the better method would be to change your design to calculate the likes and have a table that stores the likes for each user. In simple terms, something like:
USE Sandbox;
GO
CREATE SCHEMA music;
GO
CREATE TABLE music.song (SongID int IDENTITY(1,1),
Artist nvarchar(50) NOT NULL,
Title nvarchar(50) NOT NULL,
ReleaseDate date);
CREATE TABLE music.[User] (UserID int IDENTITY(1,1),
[Login] nvarchar(128));
CREATE TABLE music.SongLike (LikeID bigint IDENTITY(1,1),
SongID int,
UserID int,
Liked bit);
CREATE UNIQUE NONCLUSTERED INDEX UserLike ON music.SongLike(SongID, UserID); --Stops multiple likes
GO
--To add a LIKE you can then have a SP like:
CREATE PROC music.AddLike #SongID int, #UserID int, #Liked bit AS
BEGIN
IF EXISTS (SELECT 1 FROM music.SongLike WHERE UserID = #UserID AND SongID = #SongID) BEGIN
UPDATE music.SongLike
SET Liked = #Liked
WHERE UserID = #UserID
AND SongID = #SongID
END ELSE BEGIN
INSERT INTO music.SongLike (SongID,
UserID,
Liked)
VALUES (#SongID, #UserID, #Liked);
END
END
GO
--And, if you want the number of likes:
CREATE VIEW music.SongLikes AS
SELECT S.Artist,
S.Title,
S.ReleaseDate,
COUNT(CASE SL.Liked WHEN 1 THEN 1 END) AS Likes
FROM music.Song S
JOIN music.SongLike SL ON S.SongID = SL.SongID
GROUP BY S.Artist,
S.Title,
S.ReleaseDate;
GO
For 1) this is a bit clearer, shorter and a bit more efficient.
UPDATE [Songs]
SET [Likes] = COALESCE([Likes], 0) + CASE WHEN #Song_like = 1 THEN 1
WHEN #Song_like = 0 THEN -1
ELSE 0 END
WHERE [Song_ID] = #SongID;
For the second part you can do something like this:
IF NOT EXISTS (SELECT 1
FROM [Users_Likes_Songs]
WHERE [UserID] = #User_ID
AND [SongID] = #SongID)
INSERT INTO [Users_Likes_Songs] (User_ID, SongID, [LikeSong])
VALUES (#User_ID, #SongID, #Song_like)
ELSE
UPDATE [Users_Likes_Songs]
SET [LikeSong] = #Song_like WHERE ([UserID] = #User_ID) AND ([SongID] = #SongID)
You can try this query in your procedure
UPDATE [songs]
SET [likes] = Isnull ([likes], 0) + ( CASE WHEN #Song_like THEN 1 ELSE -1 END)
WHERE [song_id] = #SongID

Why doesn't a where condition work correctly?

I have this procedure and it should return records based on give dates and few other inputs. First it places a branch_ID in a temp table #branches and then pick that branch_ID from temp table but it returns records for all branches not only for the one that has been picked. Why ? But when i put static Branch_ID i.e. 3 then it picks for that.
ALTER PROCEDURE [dbo].[usp_RejectedFiles]
(
#FromDate SMALLDATETIME,
#ToDate SMALLDATETIME,
#OfficeID INT=0,
#Type INT=0
)
AS
BEGIN
DECLARE #Branches TABLE
(
BranchID INT
)
IF #Type = 1
BEGIN
INSERT INTO #Branches SELECT BranchID From vw_OrganizationTree WHERE OrganizationID = #OfficeID
END
IF #Type = 2
BEGIN
INSERT INTO #Branches SELECT BranchID From vw_OrganizationTree WHERE CompanyID = #OfficeID
END
IF #Type = 3
BEGIN
INSERT INTO #Branches SELECT BranchID From vw_OrganizationTree WHERE BranchID = #OfficeID
END
SELECT C.CompanyName,B.BranchName,E.EmployerName,FE.EmployeeUniqueID,pcr.EmployerUniqueID,
FE.IncomeFixedComponent,FE.IncomeVariableComponent,
S.StatusDescription, FE.IsRejected, FE.ID 'EdrID'
From File_EdrEntries FE JOIN PAFFiles pe ON pe.ID = FE.PAFFile_ID
inner join File_PCREntries pcr on pe.ID=pcr.PAFFile_ID
JOIN Employers E ON E.EmployerID = pcr.EmployerUniqueID
JOIN Branches B ON B.BranchID = E.Branch_ID JOIN companies C ON C.COMPANYID = B.COMPANY_ID
JOIN Statuses S ON S.StatusID = FE.Status_ID
where fe.IsRejected=1
AND E.Branch_id = (Select Branch_ID from #Branches)
END
Note :Please confirm that it is not typo
The Problem with the column Name i.e Column Name declare in #Branches table is different than the used in where clause.
Column Name in #Branches table
DECLARE #Branches TABLE
(
BranchID INT
)
And in where clause
...
AND E.Branch_id = (Select Branch_ID from #Branches)
There may be possibility that #Branches table may have multiple rows so instead of comparing equality you should use IN of EXISTS
AND E.Branch_id IN (Select BranchID from #Branches)
Rather than using this temp table, you should be able to compose everything into a single query:
...
JOIN Statuses S ON S.StatusID = FE.Status_ID
where fe.IsRejected=1
AND EXISTS (
SELECT * FROM vw_OrganizationTree v
WHERE v.BranchID = e.BranchID AND
(#Type = 1 and v.OrganizationID = #OfficeID) or
(#Type = 2 and v.CompanyID = #OfficeID) or
(#Type = 3 and v.BranchID = #OfficeID)
)
And as JaydipJ alludes to, the reason why your current code isn't working is because here:
AND E.Branch_id = (Select Branch_ID from #Branches)
There is no column called Branch_ID in the #Branches table. So it becomes a reference to a column in the outer query (if there were more than one such column, it would generate an error). And so it has effectively become:
AND E.Branch_id = E.Branch_ID
Which is why it returns all rows. This is why it's always a good idea to use prefixed column names when using subqueries. This:
AND E.Branch_id = (Select x.Branch_ID from #Branches x)
Would have generated an error about a non-existent column and given a clue to why it wasn't working.

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

Return Value Found in UDF Query Based on Multiple Conditions

Big edit to include more details
We display conferences for attendees to select from. Each conference has a duplicate which is treated as a "waiting list" version. The waitlist ID is stored in its parent record in the field waitListProdID. Certain fields are flagged as well.
Conference records = isWaitlist = 0, waitListProdID = *whatever its waitlist ID is*
Conference waitlist records = isWaitList = 1, waitListProdID = 0
Attendees see the waitlist version ONLY on two conditions:
When the conference's currentAttendance equals maxAttendance
If two people from the attendee's company have already registered with any given conference and currentAttendance < maxAttendance
Waitlist conferences have no attendance cap or per-company limit.
I have been tasked to generate a UDF to deal with the second issue. It should return the conference ID (primary or waitlist). The UDF will receive 3 inputs, and 1 output, with the output being either the passed in conferenceID or its waitlist equivalent.
I'm aware that my UDF syntax is incorrect as I'm more familiar with stored procedure creation, but I tried to make it as close as possible.
//This query calls the UDF (build in ColdFusion)
SELECT dt.conferenceID, ec.description, ec.price
FROM (
SELECT dbo.fnGetConfID(eci.conferenceID, #companyID#, #eventID#) as conferenceID
FROM tblEventConferences eci
WHERE eci.eventID = #eventID#
) dt
INNER JOIN tblEventConferences eco ON eci.conferneceID = eco.conferenceID
WHERE eco.eventID = #eventID#
AND eco.currentAttendance < eco.maxAttendance
//The function should return only the correct conferenceID
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION dbo.fnGetConfID (
#conferenceID int,
#companyID varchar(32),
#eventID int,
) RETURNS int
AS
BEGIN
DECLARE #count smallint;
DECLARE #waitListID int;
SET NOCOUNT ON;
--Search only conferences that are not waitlist
SELECT #count = count(rc.regID), #waitListID = rc.conferenceID
FROM tblRegistration r
INNER JOIN tblRegConferences rc ON r.ID = rc.regID
INNER JOIN tblEventConferences ec ON rc.conferenceID = ec.conferenceID
WHERE r.eventID = #eventID
AND r.optfield2 = #companyID
AND rc.conferenceID = #conferenceID
AND ec.isWaitList = 0
AND ec.currentAttendance < ec.maxAttendance
GROUP BY rc.conferenceID
--More than 1 person from same company registered for this conference. Return waitlistID
IF #count > 1
BEGIN
SELECT #conferenceIDout = conferenceID
FROM tblEventConferences
WHERE conferenceID = #waitListID
END
--Company limit not reached. Return conferenceID that was used originally
IF #count <= 1
BEGIN
SET #conferenceIDout = #conferenceID
END
END
GO

Optimizing T-SQL Insert -- temptable, CTEs, WHILE loops

I have a situation where I need to process and finally insert 1000s of records from a temptable into a database table. Before each insert, I need to ensure that conditions are met and right after each insert, I need to update a second table in my database. My problem is that currently, it takes approximately 25 minutes to run the query and I would like to drastically cut that time so my application can be more responsive. How can I go about doing this please?
DECLARE #rowcounter as INTEGER
CREATE TABLE #temporary_phonetable
(
rownumber int not null identity(1,1),
record_no BIGINT,
phone_name BIGINT,
phone_number Varchar(25) not null ,
responsemessage Varchar(200) not null ,
messagepriority Varchar(14) not null ,
phone_id BIGINT,
AD_show BIGINT,
power_show Varchar(400),
service_provider VARCHAR(30),
Phone_flag VARCHAR(30),
questionMessage BIGINT,
PRIMARY KEY (phone_id, phone_number, rownumber)
,UNIQUE (questionMessage, record_no, rownumber)
)
--GET PHONE DATA
--if phone numbers are sent in from the client, then we want to process those instead
IF ( ( ( #listofphones IS NULL OR LEN(#listofphones) <1) AND LEN(#peoplegroups) >0) )
BEGIN
--NO PHONENUMBER BUT THERE ARE GROUPS AVAILABLE
INSERT INTO #temporary_phonetable(phone_name, phone_number, messagepriority, phone_id, AD_show, power_show, responsemessage)
SELECT n.phone_name, n.phone_number,u.messagepriority, n.phone_id , u.AD_show, u.power_show , CASE #includegreetings WHEN 1 THEN LTRIM(RTRIM(phone_name)) + #responsemessages
ELSE #responsemessages END as text_message
FROM user u WITH(NOLOCK)
INNER JOIN Phonenumbers n WITH(NOLOCK) ON n.user_no = u.user_no
INNER JOIN PeopleGroupRelations g ON g.phone_id=n.phone_id
INNER JOIN ( Select items FROM Split(#peoplegroups, #listofphonesdelimiter)) gg ON g.group_no = gg.items
WHERE n.user_no=#userid
AND n.status=''active''
SET #rowcounter = ##ROWCOUNT
END
ELSE IF ( LEN(#listofphones) >1 AND LEN(#peoplegroups) >0)
BEGIN
--PHONENUMBER AND GROUPS
INSERT INTO #temporary_phonetable(phone_name, phone_number, messagepriority, phone_id, AD_show, power_show, responsemessage)
SELECT n.phone_name, n.phone_number,u.messagepriority, n.phone_id , u.AD_show, u.power_show , CASE #includegreetings WHEN 1 THEN LTRIM(RTRIM(phone_name)) + #responsemessages
ELSE #responsemessages END as text_message
FROM Split(#listofphones, ''|'') s
INNER JOIN PhoneNumbers n WITH(NOLOCK) ON n.phone_number = s.items
INNER JOIN User u WITH(NOLOCK) ON n.user_no =u.user_no
INNER JOIN PeoplegroupRelations g ON g.phone_id=n.phone_id
INNER JOIN ( Select items FROM Split(#peoplegroups, #listofphonesdelimiter)) gg ON g.group_no = gg.items
WHERE n.user_no=#userid
AND n.status=''active''
SET #rowcounter = ##ROWCOUNT
END
ELSE IF ( LEN(#listofphones) >1 AND LEN(#peoplegroups) >0)
BEGIN
--PHONENUMBER AND NO GROUPS
INSERT INTO #temporary_phonetable(phone_name, phone_number, messagepriority, phone_id, AD_show, power_show, responsemessage)
SELECT n.phone_name, n.phone_number,u.messagepriority, n.phone_id , u.AD_show, u.power_show , CASE #includegreetings WHEN 1 THEN LTRIM(RTRIM(phone_name)) + #responsemessages
ELSE #responsemessages END as text_message
FROM Split(#listofphones, ''|'') s
INNER JOIN PhoneNumbers n WITH(NOLOCK) ON n.phone_number = s.items
INNER JOIN User u WITH(NOLOCK) ON n.user_no =u.user_no
INNER JOIN PeopleGroupRelations g ON g.phone_id=n.phone_id
INNER JOIN ( Select items FROM Split(#peoplegroups, #listofphonesdelimiter)) gg ON g.group_no = gg.items
WHERE n.user_no=#userid
AND n.status=''active''
SET #rowcounter = ##ROWCOUNT
END
ELSE
BEGIN
-- NO PHONENUMBER NO GROUP --- IE. SEND TO ALL PHONE NUMBERS
INSERT INTO #temporary_phonetable(phone_name, phone_number, messagepriority, phone_id, AD_show, power_show,responsemessage)
SELECT n.phone_name, n.phone_number,u.messagepriority, n.phone_id , u.AD_show, u.power_show , CASE #includegreetings WHEN 1 THEN LTRIM(RTRIM(phone_name)) + #responsemessages
ELSE #responsemessages END as text_message
FROM User u
INNER JOIN PhoneNumbers n ON n.user_no = u.user_no
WHERE
n.status=''active''
AND n.user_no=#userid
SET #rowcounter = ##ROWCOUNT
END
IF( #rowcounter>0)
BEGIN
DECLARE #service_provider as Varchar(30)
DECLARE #PhoneType as Varchar(30)
IF (LOWER(RTRIM(LTRIM(#sendresponseswhen))) ='now')
BEGIN
SET #dateresponsessent = GETDATE()
END
DECLARE #rownumber int
DECLARE #power_show BIT
DECLARE #AD_show BIT
set #rownumber = 0
WHILE #rownumber < #rowcounter
BEGIN
set #rownumber = #rownumber + 1
-- THE VARIABLES
DECLARE #record_no as BIGINT
DECLARE #phone_name VARCHAR(30)
DECLARE #messagepriority as INTEGER
DECLARE #phone_number VARCHAR(30)
DECLARE #phone_id BIGINT
DECLARE #questionMessage BIGINT
SELECT
#phone_name = n.phone_name, #phone_number =n.phone_number, #messagepriority =n.messagepriority, #phone_id=n.phone_id ,
#AD_show=n.AD_show, #power_show=n.power_show
FROM
#temporary_phonetable n WITH(NOLOCK)
WHERE n.rownumber = #rownumber
SET #record_no = AddMessageToQueue(#phone_number, #responsemessages, #dateresponsessent, #savednames, #userid, un.messagepriority, #responsetype,
un.AD_show, un.power_show, #service_provider, #PhoneType)
If(#questionid > 0)
BEGIN
SET #questionMessage = AddQuestionMessage(#questionid,#phone_id, #record_no, DATEADD(d, 30, GETDATE()) )
END
UPDATE #temporary_phonetable SET record_no = #record_no, questionMessage=#questionMessage WHERE phone_number = #phone_number AND rownumber = #rownumber
END
IF( #power_show >0)
BEGIN
SET #responsemessages = #responsemessages + dbo.returnPoweredBy()
END
IF( #AD_show > 0)
BEGIN
SELECT #responsemessages = #responsemessages + CASE
WHEN (LEN(#responsemessages) + 14)< 160 THEN dbo.returnAD(#responsemessages)
ELSE '''' END
END
RETURN #rowcounter
END
I believe this is the place where the bulk of the issue resides.
WHILE #rownumber < #rowcounter
BEGIN
set #rownumber = #rownumber + 1
-- THE VARIABLES
DECLARE #record_no as BIGINT
DECLARE #phone_name VARCHAR(30)
DECLARE #messagepriority as INTEGER
DECLARE #phone_number VARCHAR(30)
DECLARE #phone_id BIGINT
DECLARE #questionMessage BIGINT
SELECT
#phone_name = n.phone_name, #phone_number =n.phone_number, #messagepriority =n.messagepriority, #phone_id=n.phone_id ,
#AD_show=n.AD_show, #power_show=n.power_show
FROM
#temporary_phonetable n WITH(NOLOCK)
WHERE n.rownumber = #rownumber
SET #record_no = AddMessageToQueue(#phone_number, #responsemessages, #dateresponsessent, #savednames, #userid, un.messagepriority, #responsetype,
un.AD_show, un.power_show, #service_provider, #PhoneType)
If(#questionid > 0)
BEGIN
SET #questionMessage = AddQuestionMessage(#questionid,#phone_id, #record_no, DATEADD(d, 30, GETDATE()) )
END
UPDATE #temporary_phonetable SET record_no = #record_no, questionMessage=#questionMessage WHERE phone_number = #phone_number AND rownumber = #rownumber
END
Add a unique constraint to rownumber in your temp table. Rewrite the WHILE as a CTE. Use APPLY to call the functions.
You also might want to consider using a table variable rather than a temporary table. You won't be writing to the tempdb and as table variables are created in memory they're faster.
This article has a nice comparison.