looking for cursor replacement - sql

I have 2 tables #TblFinal and #TblData.. I need to compare #TblData data with"#TblFinal based on unique key F_U_KEY vs D_U_KEY.
if #TblData data for a unique key is not exist in #TblFinal, then #TblData data record will insert into #TblFinal table. F_COUNTER will be 1 (default) and F_IS_CLEAR is 0 default
if #TblData data for a unique key is exist in #TblFinal, then first we get key F_ID from #TblFinal
case A: if D_SEV_ID of table "#TblData" is not equal to -11
then need to update "F_COUNTER" for table "#TblFinal" for "F_ID"
case B: if D_SEV_ID of table "#TblData" is equal to -11
then new entry for D_SEV_ID = -11 and F_IS_CLEAR =1 for "F_ID"
Here is the cursor I wrote, need some optimized solution as my actual #TblFinal having huge data and data compare table "#TblData" always have 100 records to compare.
CREATE TABLE #TblFinal
(F_ID INT IDENTITY(1,1), F_VAL NVARCHAR(20), F_SEV_ID INT, F_U_KEY NVARCHAR(200), F_COUNTER INT DEFAULT(1), F_IS_CLEAR BIT DEFAULT(0))
CREATE TABLE #TblData
(D_ID INT, D_VAL NVARCHAR(20), D_SEV_ID INT, D_U_KEY NVARCHAR(200))
INSERT INTO #TblData VALUES(1, 'test 1', 2, '1:100002135::::15124:9334'), (2, 'test 1', 2, '1:100002135::::15124:9334'),
(3, 'test', -11, '1:100002135::::15124:9334'), (4, 'test 1', 2, '1:100002135::::15124:9334'), (5, 'test 1', 2, '1:1024:9334')
DECLARE #D_ID INT
DECLARE #D_SEV_ID INT
DECLARE #D_U_KEY NVARCHAR(200)
DECLARE A_CUR CURSOR FOR
SELECT D_ID, D_SEV_ID, D_U_KEY FROM #TblData ORDER BY D_ID ASC
OPEN A_CUR
FETCH NEXT FROM A_CUR INTO #D_ID, #D_SEV_ID, #D_U_KEY
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT 1 FROM #TblFinal(NOLOCK) WHERE F_U_KEY = #D_U_KEY AND F_IS_CLEAR = 0 AND F_SEV_ID <> -11)
BEGIN
DECLARE #FId INT
SELECT #FId = F_ID FROM #TblFinal(NOLOCK) WHERE F_U_KEY = #D_U_KEY AND F_IS_CLEAR = 0 AND F_SEV_ID <> -11
--IF #D_SEV_ID != -11
IF (#D_SEV_ID <> -11)
BEGIN
UPDATE #TblFinal
SET F_COUNTER = F_COUNTER + 1 WHERE F_ID = #FId
END
--IF #D_SEV_ID = -11
ELSE IF(#D_SEV_ID = -11)
BEGIN
INSERT INTO #TblFinal (F_VAL, F_SEV_ID, F_U_KEY)
SELECT D_VAL, D_SEV_ID, D_U_KEY FROM #TblData(NOLOCK) WHERE D_ID = #D_ID
UPDATE #TblFinal
SET F_IS_CLEAR = 1 WHERE F_ID = #FId
END
ELSE
BEGIN
PRINT 'DO NOTHING'
END
END
ELSE
BEGIN
INSERT INTO #TblFinal (F_VAL, F_SEV_ID, F_U_KEY)
SELECT D_VAL, D_SEV_ID, D_U_KEY FROM #TblData(NOLOCK) WHERE D_ID = #D_ID
END
FETCH NEXT FROM A_CUR INTO #D_ID, #D_SEV_ID, #D_U_KEY
END
CLOSE A_CUR
DEALLOCATE A_CUR
--SELECT * FROM #TblData
SELECT * FROM #TblFinal
DROP TABLE #TblData
DROP TABLE #TblFinal

It does not look like a set-based approach will work for you since the order of your data looks important. That said, there are some things you can do which should speed up your query by about 50%.
Loop from min(D_ID) to max(D_ID), incrementing by 1.
Only look for the item in #TblFinal once instead of once in an exists and then again to get the #FId. Also, make sure #TblFinal has an index on F_U_KEY + F_IS_CLEAR + F_SEV_ID and that F_ID is either part of the clustered index or within an INCLUDE clause. Ideally it is the first field in the clustered index for the updates to be fast.
By storing D_VAL in a variable, you can insert directly into #TblFinal from variables rather than having to SELECT again from #TblData.
Here's the code:
DECLARE #D_ID INT
DECLARE #FId INT
DECLARE #D_SEV_ID INT
DECLARE #D_U_KEY NVARCHAR(200)
DECLARE #D_VAL NVARCHAR(20)
DECLARE #Min_D_ID INT, #Max_D_ID INT
SELECT #Min_D_ID = min(D_ID), #Max_D_ID = max(D_ID) FROM #TblData(NOLOCK)
SELECT #D_ID = #Min_D_ID
WHILE #D_ID <= #Max_D_ID
BEGIN
SELECT #D_SEV_ID = D_SEV_ID, #D_U_KEY = D_U_KEY, #D_VAL = D_VAL FROM #TblData(NOLOCK) WHERE D_ID = #D_ID
IF #D_U_KEY IS NOT NULL
BEGIN
SELECT #FId = F_ID FROM #TblFinal(NOLOCK) WHERE F_U_KEY = #D_U_KEY AND F_IS_CLEAR = 0 AND F_SEV_ID <> -11
IF #FId IS NOT NULL
BEGIN
IF (#D_SEV_ID <> -11)
BEGIN
UPDATE #TblFinal
SET F_COUNTER = F_COUNTER + 1 WHERE F_ID = #FId
END
ELSE IF(#D_SEV_ID = -11)
BEGIN
INSERT INTO #TblFinal (F_VAL, F_SEV_ID, F_U_KEY)
SELECT #D_VAL, #D_SEV_ID, #D_U_KEY
UPDATE #TblFinal
SET F_IS_CLEAR = 1 WHERE F_ID = #FId
END
ELSE
BEGIN
PRINT 'DO NOTHING'
END
END
ELSE
BEGIN
INSERT INTO #TblFinal (F_VAL, F_SEV_ID, F_U_KEY)
SELECT #D_VAL, #D_SEV_ID, #D_U_KEY
END
END
SELECT #D_ID = #D_ID + 1, #FId = NULL
END

You should be able to do this set based. Here is one way to tackle this problem. The only part I don't really understand is F_IS_CLEAR. The business rule for that is a bit fuzzy. If you need help with part I need to understand what the rationale is for that.
CREATE TABLE #Output
(F_ID INT IDENTITY(1,1), F_VAL NVARCHAR(20), F_SEV_ID INT, F_U_KEY NVARCHAR(200), F_COUNTER INT DEFAULT(1), F_IS_CLEAR BIT DEFAULT(0))
insert #Output
select x.D_VAL
, x.D_SEV_ID
, x.D_U_KEY
, x.F_COUNTER
, x.F_IS_CLEAR
from
(
select D_VAL = MIN(D_VAL)
, D_SEV_ID = MIN(D_SEV_ID)
, D_U_KEY
, F_COUNTER = COUNT(*)
, F_IS_CLEAR = 1
, SortOrder = 1
from #TblData d
where D_ID < (select d2.D_ID from #TblData d2 where d2.D_U_KEY = d.D_U_KEY and d2.D_SEV_ID = -11)
group by d.D_U_KEY
UNION ALL
select D_VAL
, D_SEV_ID
, D_U_KEY
, F_COUNTER = 1
, F_IS_CLEAR = 0
, SortOrder = 2
from #TblData d
where d.D_SEV_ID = -11
UNION ALL
select D_VAL = MIN(D_VAL)
, D_SEV_ID = MIN(D_SEV_ID)
, D_U_KEY
, F_COUNTER = COUNT(*)
, F_IS_CLEAR = 0
, SortOrder = 3
from #TblData d
where D_ID > (select d2.D_ID from #TblData d2 where d2.D_U_KEY = d.D_U_KEY and d2.D_SEV_ID = -11)
group by d.D_U_KEY
UNION ALL
select D_VAL = MIN(D_VAL)
, D_SEV_ID = MIN(D_SEV_ID)
, D_U_KEY
, F_COUNTER = COUNT(*)
, F_IS_CLEAR = 0
, SortOrder = 4
from #TblData d
where NOT EXISTS (select d2.D_ID from #TblData d2 where d2.D_U_KEY = d.D_U_KEY and d2.D_SEV_ID = -11)
group by d.D_U_KEY
) x
order by D_U_KEY, SortOrder
select * from #Output

Related

How to get records that between m records back and n records forward from a reference row - Non-sequential data

My scenario is as follows:
I have a reference record, say, ProductId = 1
The records each have a non-unique ItemTypeId
I would like to fetch records that exists between the following points
START POINT being 2 records BACKWARDS of type ItemTypeId = 1, from record of ProductId =1
END POINT being 3 records FORWARDS of type ItemTypeId = 1, from record of ProductId = 1
The query should get ALL data between the two points, inclusively
Here's a picture that illustrates this better than my words:
How would I structure my query to do this?
Any better way to do it without temp tables?
Thank-you!
Note that for this to work at all, you need that record ID to be an actual column in the table. Rows have no inherent order in a table.
With that in place, you can use LAG and LEAD to get what you want:
CREATE TABLE #t
(
RecordId INT IDENTITY(1,1),
ProductId INT,
ItemType INT
);
INSERT INTO #t(ProductId, ItemType)
VALUES
(5,1),(3,1),(7,3),(6,1),(2,7),
(1,1),(7,3),(8,1),(10,3),(9,5),
(11,1),(19,1),(17,4),(13,3);
WITH c1 AS
(
SELECT ProductId,
RecordId,
LAG(RecordId,2) OVER (ORDER BY RecordId) AS Back2,
LEAD(RecordId,3) OVER (ORDER BY RecordId) AS Forward3
FROM #t
WHERE ItemType = (SELECT ItemType FROM #t WHERE ProductId = 1)
),c2 AS
(
SELECT c1.Back2, c1.Forward3 FROM c1
WHERE c1.ProductId = 1
)
SELECT #t.*
FROM #t
INNER JOIN c2 ON #t.RecordId BETWEEN c2.Back2 AND c2.Forward3;
If you wanna do without using temp tables as you ask, the following solution work.
But it is not very nice i agree.
Well this is what i done :
CREATE DATABASE TEST;
USE TEST
CREATE TABLE PRODUCT
(
ProductId INT,
ItemType INT
)
INSERT INTO PRODUCT
VALUES
(5,1),
(3,1),
(7,3),
(6,1),
(2,7),
(1,1),
(7,3),
(8,1),
(10,3),
(9,5),
(11,1),
(19,1),
(17,4),
(13,3)
DECLARE product_cursor CURSOR FOR
SELECT * FROM PRODUCT;
OPEN product_cursor
DECLARE
#ProductId INT,
#ItemId INT,
#END_FETCH INT,
#countFrom INT,
#countTo INT
DECLARE #TableResult TABLE
(
RProductId INT,
RItemId INT
)
FETCH NEXT FROM product_cursor
INTO #ProductId, #ItemId
SET #END_FETCH = 0
SET #countFrom = 0
SET #countTo = 0
WHILE ##FETCH_STATUS = 0 AND #END_FETCH = 0
BEGIN
IF #ItemId = 1 AND (#countFrom = 0 AND #countTo = 0)
BEGIN
SET #countFrom = 3
SET #countTo = 3
END
ELSE
BEGIN
IF #countFrom > 0
BEGIN
--SELECT 'INSERTION : ' ,#ProductId,#ItemId
INSERT INTO #TableResult VALUES(#ProductId, #ItemId)
IF #ItemId = 1
BEGIN
SET #countFrom -= 1
--SELECT 'CountFrom : ', #countFrom
END
END
ELSE
BEGIN
IF #countTo > 0
BEGIN
--SELECT 'INSERTION : ' ,#ProductId,#ItemId
INSERT INTO #TableResult VALUES(#ProductId, #ItemId)
IF #ItemId = 1
BEGIN
SET #countTo -= 1
--SELECT 'CountTO : ', #countTo
END
END
ELSE
BEGIN
SET #END_FETCH = 1
END
END
END
FETCH NEXT FROM product_cursor
INTO #ProductId, #ItemId
END
CLOSE product_cursor
DEALLOCATE product_cursor
SELECT * FROM #TableResult
And this is the result i got :
RProductId RItemId
3 1
7 3
6 1
2 7
1 1
7 3
8 1
10 3
9 5
11 1
19 1
But i prefer the solution of #James Casey.
By the way, why won't you use temp table ?

Need to Add Another While in the below query

I've got the below code from one of the experts here in this forum (Which is working fine). So what I'm trying to do is to amend this query further to get the dates the same way but for all tasks. There is a table called "TASK" in the database and each task_id has a clndr_id and proj_id. The desired output would be (proj_id, task_id, clndr_id, date_Value), so I think we need to add another WHILE to get all dates for all tasks in the TASK table. I hope #CurseStacker or anyone else can help :)..thanks.
ALTER FUNCTION [dbo].[GetProjectDates]
(
#project_name varchar(50)
)
RETURNS #temp_tb TABLE([proj_id] int, [clndr_id] int , [date_value] date)
AS
BEGIN
-- Add the SELECT statement with parameter references here
DECLARE #project_id int
DECLARE #clndr_id int
DECLARE #walker int = 0
DECLARE #holder varchar(MAX)
DECLARE #date date
DECLARE #data varchar(MAX)
SELECT #project_id = [p].[proj_id]
,#clndr_id = [p].[clndr_id]
,#holder = [c].[clndr_data]
FROM [PMDB].[dbo].[PROJECT] AS [p]
INNER JOIN [PMDB].[dbo].[CALENDAR] AS [c] ON [p].[clndr_id] = [c].[clndr_id]
WHERE [p].[proj_short_name] = #project_name
WHILE #walker <> LEN(#holder) + 1
BEGIN
IF SUBSTRING(#holder, #walker, 2) = 'd|'
BEGIN
SET #data = SUBSTRING(#holder, #walker, 10)
IF SUBSTRING(#data, LEN(#data) - 2, 3) = ')()'
BEGIN
SET #date = DATEADD(D, CAST(SUBSTRING(#data, 3, 5) AS int) -2, '01/01/1900')
INSERT INTO #temp_tb VALUES (#project_id, #clndr_id, #date)
END
END
SET #walker = #walker + 1
END
RETURN
END
GO
So "TASK" table includes (proj_id, task_id, clndr_id) and from "Calendar" table we can get the clndr_data by linking to proj_id in task table (The above code gets the dates in clndr_data between 'd|' and ')()' for only one clndr_id, and it works fine' Now I need to do the same but for several calendars;
proj_id task_id clndr_id clndr_data
4917 310449 7143 (0||CalendarData()(.....
4917 310450 7144 (0||CalendarData()(.....
4917 310451 7149 (0||CalendarData()(.....
Desired Outcome (Assuming clndr_id 7143 has only 2 dates between 'd|' and ')()', clndr_id 7144 has 3 dates, and cldnr_id 7149 has two dates) JUST AN ASSUMPTION
proj_id task_id clndr_id date
4917 310449 7143 2018-09-24
4917 310449 7143 2018-09-25
4917 310450 7144 2018-09-26
4917 310450 7144 2018-10-01
4917 310450 7144 2018-10-02
4917 310451 7149 2018-10-03
4917 310451 7149 2018-10-04
try something like below:
ALTER FUNCTION [dbo].[GetProjectDates]
(
#project_name varchar(50)
)
RETURNS #temp_tb TABLE([proj_id] int, [task_id] int, [clndr_id] int , [date_value] date)
AS
BEGIN
-- Add the SELECT statement with parameter references here
DECLARE #project_id int
DECLARE #task_id int
DECLARE #clndr_id int
DECLARE #walker int = 0
DECLARE #holder varchar(MAX) = ''
DECLARE #date date
DECLARE #data varchar(MAX)
declare #tab table (project_id int, task_id int, clndr_id int, holder varchar(max))
insert into #tab
SELECT [T].[proj_id] as project_id
,[T].[task_id] as task_id
,[c].[clndr_id] as clndr_id
,[c].[clndr_data] as holder
FROM [PMDB].[dbo].[TASK] as [T]
INNER JOIN [PMDB].[dbo].[CALENDAR] AS [c] ON [T].[clndr_id] = [c].[clndr_id] and [T].[Proj_id] = [c].[proj_id]
WHERE [T].[proj_id] = (select proj_id FROM [PMDB].[dbo].[PROJECT] where [proj_short_name] = #project_name)
declare #cur_task int, #cur_clndr int
set #cur_task = (select top 1 task_id from #tab)
set #cur_clndr = (select top 1 clndr_id from #tab where task_id = #task_id)
set #holder = (select top 1 holder from #tab where task_id = #task_id)
while ((select count(1) from #tab) > 0)
begin
print 'Current loop running for Task_Id' + convert(varchar(10), #cur_task)
WHILE #walker <> LEN(#holder) + 1
BEGIN
IF SUBSTRING(#holder, #walker, 2) = 'd|'
BEGIN
SET #data = SUBSTRING(#holder, #walker, 10)
IF SUBSTRING(#data, LEN(#data) - 2, 3) = ')()'
BEGIN
SET #date = DATEADD(D, CAST(SUBSTRING(#data, 3, 5) AS int) -2, '01/01/1900')
INSERT INTO #temp_tb VALUES (#project_id, #task_id, #clndr_id, #date)
END
END
SET #walker = #walker + 1
END
delete #tab where task_id = #cur_task
set #cur_task = (select top 1 task_id from #tab)
set #cur_clndr = (select top 1 clndr_id from #tab where task_id = #task_id)
set #holder = (select top 1 holder from #tab where task_id = #task_id)
set #walker = 0
end
RETURN
END

How to assign a variable different values based on a column cell values

I need to loop through all rows and do the following:
If the Date_Deleted is null then SET #Form = '01' else #Form = '02'
Currently is returning Form = 01 only.
Thank you!
SELECT #CHECKDATE = [DATE_DELETED]
FROM Executive__Vehicles
WHERE (STATE = 'NC')
IF ( #CHECKDATE is null )
BEGIN
SET #FORM = '01'
END
ELSE
BEGIN
SET #FORM = '02'
END
You need to load all the results from Executive_vehicles into a temp table and then loop through them one by one to do the set.
CREATE TABLE #tempTable1(iID int identity(1,1),date_deleted datetime)
INSERT INTO #tempTable1
select [DATE_DELETED]
FROM Executive__Vehicles WHERE (STATE = 'NC')
DECLARE #countROws int
DECLARE #CHECKDATE datetime
DECLARE #topRow INT
SET #countRows =(SELECT COUNT(*) FROM #tempTable1)
WHILE #countRows>0
BEGIN
SET #topRow = (SELECT TOP 1 iID from #tempTable1)
SET #CHECKDATE = (SELECT TOP 1 date_deleted from #tempTable1)
IF ( #CHECKDATE is null )
BEGIN SET #FORM = '01'
END
ELSE
BEGIN
SET #FORM = '02'
END
DELETE FROM #tempTable1 where iID=#topRow
set #countRows = (SELECT count(*) FROM #tempTable1)
set #CHECKDATE = NULL
END
editted to add identity column as date_deleted could be same for multiple rows and delete would delete all of them.

How to use if condition with dynamic SQL

I want to put if condition for following query but getting error
ALTER PROCEDURE [dbo].[GetExternalDocumentHistory]
#reoid int,
#spType VARCHAR(50),
#maximumRows INT = 0,
#startRowIndex INT = 0,
#recordCount INT OUTPUT
AS BEGIN
DECLARE #data TABLE(
id INT IDENTITY(1,1),
property_address NVARCHAR(250),
property_id INT )
INSERT #data( property_address, property_id )
IF( #spType = 'ED' )
BEGIN
SELECT property_address, property_id
FROM external_documents_history as edu
WHERE edu.property_id = #reoid
END;
IF (#spType = 'GD' )
BEGIN
SELECT property_address, property_id
FROM external_documents_history as edu
WHERE edu.page_type = 'GD'
END;
SELECT #recordCount = COUNT(*) FROM #data
IF #maximumRows = 0
BEGIN
SET #startRowIndex = 0
SET #maximumRows = #recordCount
END
SELECT * FROM #data
WHERE id BETWEEN #startRowIndex + 1 AND #startRowIndex + #maximumRows
ORDER BY id
END;
What is the issue with the above query ?
Besides #Alex's two conditional INSERTs (the best way I think), here's a variation on #Sorpigals's answer, without the CASE::
INSERT INTO #data( property_address, property_id )
SELECT property_address
, property_id
FROM external_documents_history as edu
WHERE ( #spType = 'ED' AND edu.property_id = #reoid )
OR ( #spType = 'GD' AND edu.page_type = 'GD' )
You cannot use an IF after the INSERT statement , the simplest option is to restate the insert for all possible conditions;
IF (#spType = 'ED') BEGIN
INSERT #data(property_address, property_id)
SELECT property_address, property_id FROM external_documents_history as edu WHERE edu.property_id = #reoid
END
You can also skip SELECT #recordCount = COUNT(*) FROM #data and use ##ROWCOUNT
Use a case statement in your where clause, e.g.
DECLARE #data TABLE(
id INT IDENTITY(1,1),
property_address NVARCHAR(250),
property_id INT )
INSERT INTO #data( property_address, property_id )
select property_address, property_id FROM external_documents_history as edu
where
case when #spType = 'ED' and edu.property_id = #reoid then 1
case when #spType = 'GD' and edu.page_type = 'GD' then 1
else 0
end = 1
;
SELECT #recordCount = COUNT(*) FROM #data
IF #maximumRows = 0
BEGIN
SET #startRowIndex = 0
SET #maximumRows = #recordCount
END
SELECT * FROM #data
WHERE id BETWEEN #startRowIndex + 1 AND #startRowIndex + #maximumRows
ORDER BY id
END;
For one thing this is not correct:
INSERT #data( property_address, property_id )
Should be:
INSERT INTO table_name
VALUES (value1, value2, value3,...)
And being that this is before your if statement that is probably why your query analyzer / management studio is complaining about the if.
If you are trying a dynamic INSERT INTO Table SELECT A, B, C FROM OtherTableDynamically you will need to prepare the entire statement and use sp_executesql.

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.