Advanece SQL Update query - sql-server

Question:
Want to update Inflow by checking the condition what is the value we have under inflow column in reporting table
e.g if we have a CASE WHEN FLAG = '1' THEN 'AS' ELSE 'LI' it should check the value for flag column if it is 1 then value should be 'AS' else 'L1'
Note: INFLOW column under reporting have different values.
Table Structure:
create table dummy ( ID VARCHAR(10), PRODUCT VARCHAR(10), INFLOW VARCHAR(10) );
INSERT INTO dummy(ID,PRODUCT) VALUES('10','999')
INSERT INTO dummy(ID,PRODUCT) VALUES('11','888')
INSERT INTO dummy(ID,PRODUCT) VALUES('12','111')
INSERT INTO dummy(ID,PRODUCT) VALUES('13','222')
create table REPORTING_FLAG
(
ID VARCHAR(10),
PRODUCT VARCHAR(10),
INFLOW VARCHAR(2000),
FLAG VARCHAR(10),
L3 VARCHAR(10)
);
INSERT INTO REPORTING VALUES('10','999','CASE WHEN FLAG = ''1'' THEN ''AS'' ELSE ''LI''','1','SALR')
INSERT INTO REPORTING VALUES('11','888','CASE WHEN L3 = ''SALR'' THEN ''SALR'' ELSE ''OTHR''','1','XYZ')
INSERT INTO REPORTING VALUES('12','111','ABC','2','PQR')
INSERT INTO REPORTING VALUES('13','222','SAP','3','QWE')

When performing larger inserts, it is wise and correctly-applied logic to perform transformation on the sets of data, rather than each individual row.
Even if this is unavoidable, do not make the mistake of being comfortable with myriads of update/insert statements,which not only costs resources but trashes statistics, fragments your indexes and drives alike.
Run a SELECT statement that uses a CASE logic and verify the results. Once you can safely perform this on the select statement, convert it to an Update statement (note your update can use joins)

This should work
declare dummyproducts cursor for select ID, Product, Inflow from reporting
declare #ID VARCHAR(10), #PRODUCT VARCHAR(10), #INFLOW VARCHAR(2000)
OPEN dummyproducts
Fetch next from dummyProducts into #ID, #PRODUCT, #INFLOW
while ##fetch_Status = 0
BEGIN
declare #sql nvarchar(max)
if CHARINDEX('CASE', #INFLOW) > 0
set #Inflow = #inflow + ' end'
else
set #inflow = 'r.inflow'
set #sql = 'Update dummy set INFLOW = ' +
#INFLOW
+ ' from
reporting r inner join dummy d on r.Product = d.Product and r.ID = d.ID where d.ID = ' + #ID + ' and d.Product = ' + #Product
print #sql
exec (#sql)
Fetch next from dummyProducts into #ID, #PRODUCT, #INFLOW
END
close dummyProducts
deallocate dummyProducts
GO

Related

Dynamic Database Stored Procedure on SQL Server 2016

I'm trying to build a stored procedure that will query multiple database depending on the databases required.
For example:
SP_Users takes a list of #DATABASES as parameters.
For each database it needs to run the same query and union the results together.
I believe a CTE could be my best bet so I have something like this at the moment.
SET #DATABASES = 'DB_1, DB_2' -- Two databases in a string listed
-- I have a split string function that will extract each database
SET #CURRENT_DB = 'DB_1'
WITH UsersCTE (Name, Email)
AS (SELECT Name, Email
FROM [#CURRENT_DB].[dbo].Users),
SELECT #DATABASE as DB, Name, Email
FROM UsersCTE
What I don't want to do is hard code the databases in the query. The steps I image are:
Split the parameter #DATABASES to extract and set the #CURRENT_DB Variable
Iterate through the query with a Recursive CTE until all the #DATABASES have been processed
Union all results together and return the data.
Not sure if this is the right approach to tackling this problem.
Using #databases:
As mentioned in the comments to your question, variables cant be used to dynamically select a database. Dynamic sql is indicated. You can start by building your template sql statement:
declare #sql nvarchar(max) =
'union all ' +
'select ''#db'' as db, name, email ' +
'from [#db].dbo.users ';
Since you have sql server 2016, you can split using the string_split function, with your #databases variable as input. This will result in a table with 'value' as the column name, which holds the database names.
Use the replace function to replace #db in the template with value. This will result in one sql statement for each database you passed into #databases. Then, concatenate the statements back together. Unfortunately, in version 2016, there's no built in function to do that. So we have to use the famous for xml trick to join the statements, then we use .value to convert it to a string, and finally we use stuff to get rid of the leading union all statement.
Take the results of the concatenated output, and overwrite the #sql variable. It is ready to go at this point, so execute it.
I do all that is described in this code:
declare #databases nvarchar(max) = 'db_1,db_2';
set #sql = stuff(
(
select replace(#sql, '#db', value)
from string_split(#databases, ',')
for xml path(''), type
).value('.[1]', 'nvarchar(max)')
, 1, 9, '');
exec(#sql);
Untested, of course, but if you print instead of execute, it seems to give the proper sql statement for your needs.
Using msForEachDB:
Now, if you didn't want to have to know which databases had 'users', such as if you're in an environment where you have a different database for every client, you can use sp_msForEachDb and check the structure first to make sure it has a 'users' table with 'name' and 'email' columns. If so, execute the appropriate statement. If not, execute a dummy statement. I won't describe this one, I'll just give the code:
declare #aggregator table (
db sysname,
name int,
email nvarchar(255)
);
insert #aggregator
exec sp_msforeachdb '
declare #sql nvarchar(max) = ''select db = '''''''', name = '''''''', email = '''''''' where 1 = 2'';
select #sql = ''select db = ''''?'''', name, email from ['' + table_catalog + ''].dbo.users''
from [?].information_schema.columns
where table_schema = ''dbo''
and table_name = ''users''
and column_name in (''name'', ''email'')
group by table_catalog
having count(*) = 2
exec (#sql);
';
select *
from #aggregator
I took the valid advice from others here and went with this which works great for what I need:
I decided to use a loop to build the query up. Hope this helps someone else looking to do something similar.
CREATE PROCEDURE [dbo].[SP_Users](
#DATABASES VARCHAR(MAX) = NULL,
#PARAM1 VARCHAR(250),
#PARAM2 VARCHAR(250)
)
BEGIN
SET NOCOUNT ON;
--Local variables
DECLARE
#COUNTER INT = 0,
#SQL NVARCHAR(MAX) = '',
#CURRENTDB VARCHAR(50) = NULL,
#MAX INT = 0,
#ERRORMSG VARCHAR(MAX)
--Check we have databases entered
IF #DATABASES IS NULL
BEGIN
RAISERROR('ERROR: No Databases Provided,
Please Provide a list of databases to execute procedure. See stored procedure:
[SP_Users]', 16, 1)
RETURN
END
-- SET Number of iterations based on number of returned databases
SET #MAX = (SELECT COUNT(*) FROM
(SELECT ROW_NUMBER() OVER (ORDER BY i.value) AS RowNumber, i.value
FROM dbo.udf_SplitVariable(#DATABASES, ',') AS i)X)
-- Build SQL Statement
WHILE #COUNTER < #MAX
BEGIN
--Set the current database
SET #CURRENTDB = (SELECT X.Value FROM
(SELECT ROW_NUMBER() OVER (ORDER BY i.value) AS RowNumber, i.value
FROM dbo.udf_SplitVariable(#DATABASES, ',') AS i
ORDER BY RowNumber OFFSET #COUNTER
ROWS FETCH NEXT 1 ROWS ONLY) X);
SET #SQL = #SQL + N'
(
SELECT Name, Email
FROM [' + #CURRENTDB + '].[dbo].Users
WHERE
(Name = #PARAM1 OR #PARAM1 IS NULL)
(Email = #PARAM2 OR #PARAM2 IS NULL)
) '
+ N' UNION ALL '
END
PRINT #CURRENTDB
PRINT #SQL
SET #COUNTER = #COUNTER + 1
END
-- remove last N' UNION ALL '
IF LEN(#SQL) > 11
SET #SQL = LEFT(#SQL, LEN(#SQL) - 11)
EXEC sp_executesql #SQL, N'#CURRENTDB VARCHAR(50),
#PARAM1 VARCHAR(250),
#PARAM2 VARCHAR(250)',
#CURRENTDB,
#PARAM1 ,
#PARAM2
END
Split Variable Function
CREATE FUNCTION [dbo].[udf_SplitVariable]
(
#List varchar(8000),
#SplitOn varchar(5) = ','
)
RETURNS #RtnValue TABLE
(
Id INT IDENTITY(1,1),
Value VARCHAR(8000)
)
AS
BEGIN
--Account for ticks
SET #List = (REPLACE(#List, '''', ''))
--Account for 'emptynull'
IF LTRIM(RTRIM(#List)) = 'emptynull'
BEGIN
SET #List = ''
END
--Loop through all of the items in the string and add records for each item
WHILE (CHARINDEX(#SplitOn,#List)>0)
BEGIN
INSERT INTO #RtnValue (value)
SELECT Value = LTRIM(RTRIM(SUBSTRING(#List, 1, CHARINDEX(#SplitOn, #List)-1)))
SET #List = SUBSTRING(#List, CHARINDEX(#SplitOn,#List) + LEN(#SplitOn), LEN(#List))
END
INSERT INTO #RtnValue (Value)
SELECT Value = LTRIM(RTRIM(#List))
RETURN
END

Query to select next column if the current column returns no rows

The question is quite extensive, please bear with me. I have a single mapping table with the following structure:
This particular table is used in the process of generating a hierarchy. The order and position of the columns in the table indicate the order of hierarchy (Organization, Category, Continent, Country.. etc.) Each entity in this hierarchy has a related table with associated Id and Name. For example, there is a Country table with CountryId and CountryName. Note that since the MappingTable's values are all nullable there are no foreign key constraints.
I want to generate a procedure that will do the following:
Based on conditions provided, retrieve values of the next entity in the hierarchy. For example, if the OrganizationId and CategoryId are given, the values of ContinentId that satisfy said condition need to be retrieved.
Also, if the value of ContinentId is NULL, then the values of CountryId need to be retrieved. Here, given the condition OrganizationId = 1 and CategoryId = 1 the procedure should return the list of RegionId.
In addition to retrieving the RegionId, the corresponding RegionName should be retrieved from the Region Table.
So far, the procedure looks something like this - just a few things to explain here.
ALTER PROCEDURE [dbo].[GetHierarchy]
(
#MappingTableName VARCHAR(30),
#Position VARCHAR(5),
-- Given in the form of Key-value pairs 'OrganizationId:1,CategoryId:1'
#InputData VARCHAR(MAX),
#Separator CHAR(1),
#KeyValueSeperator CHAR(1)
)
AS
BEGIN
DECLARE #Sql NVARCHAR(MAX)
DECLARE #Result NVARCHAR(MAX)
DECLARE #Sql1 NVARCHAR(MAX);
DECLARE #TableName NVARCHAR(30)
DECLARE #Exists bit
SELECT #TableName = COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #MappingTableName AND ORDINAL_POSITION = #position
SET #TableName = SUBSTRING(#TableName,0,LEN(#TableName) - 1)
-- Returns a dynamic query like "SELECT ContinentId from Continent WHERE OrganizationId = 1 and CategoryId = 1".
SELECT #Sql = [dbo].[KeyValuePairs](#TableName, #InputData, #Separator, #KeyValueSeperator)
SET #Sql1 = N'SET #Exists = CASE WHEN EXISTS(' + #Sql + N' AND ' + #TableName + N'Id IS NOT NULL) THEN 1 ELSE 0 END'
PRINT #Sql
EXEC sp_executesql #Sql1,
N'#Exists bit OUTPUT',
#Exists = #Exists OUTPUT
IF(#Exists = 1)
BEGIN
SET #Sql1 = 'SELECT ' + #TableName + 'Id, ' + #TableName + 'Name FROM '+ #TableName+' WHERE ' + #TableName +'Id IN (' + #Sql + ')';
PRINT #Sql1
EXECUTE sp_executesql #SQL1
END
ELSE
BEGIN
--PRINT 'NOT EXISTS'
DECLARE #nextPosition INT
SELECT #nextPosition = CAST(#position AS INT)
SET #nextPosition = #nextPosition + 1
SET #Position = CONVERT(VARCHAR(5), CAST(#position AS INT))
EXEC [dbo].[GetHierarchy] #MappingTableName, #Position, #InputData, #Separator, #KeyValueSeperator
END
END
The logic of this procedure is such that, I get the name of the column at a particular position (based on the conditions here, it is Continent) and generate the dynamic query to retrieve the next column's values based on the condition of the input condition (I am using a separate function to do this for me).
Once retrieved, I run the query to check if it returns any rows. If the query returns rows, then I retrieve the corresponding ContinentName from the Continent table. If no rows are returns, I recursively call the procedure again with the next position as the input.
On the business side of things, it seems like a two step process. But, as a procedure it is quite complex, extensive and - not to mention, recursive. Is there an easier way to do this? I am not familiar with CTEs - can the same logic be implemented using CTEs?
This is quite similar to what is asked here: Working with a dynamic hierarchy SQL Server
Might be the little lengthy approach. Try this
DECLARE #T TABLE
(
SeqNo INT IDENTITY(1,1),
CatId INT,
Country INT,
StateId INT,
DistId INT
)
DECLARE #State TABLE
(
StateId INT,
StateNm VARCHAR(20)
)
DECLARE #Country TABLE
(
CountryId INT,
CountryNm VARCHAR(20)
)
INSERT INTO #State
VALUES(3,'FL')
INSERT INTO #Country
VALUES(2,'USA')
INSERT INTO #T(CatId)
VALUES(1)
INSERT INTO #T(CatId,Country)
VALUES(1,2)
INSERT INTO #T(CatId,StateId)
VALUES(1,3)
;WITH CTE
AS
(
SELECT
*,
IdVal = COALESCE(Country,StateId,DistId),
IdCol = COALESCE('Country '+CAST(Country AS VARCHAR(50)),'StateId '+CAST(StateId AS VARCHAR(50)),'DistId '+CAST(DistId AS VARCHAR(50)))
FROM #T
WHERE CatId = 1
),C2
AS
(
SELECT
SeqNo,
CatId,
Country,
StateId,
DistId,
IdVal,
IdCol = LTRIM(RTRIM(SUBSTRING(IdCol,1,CHARINDEX(' ',IdCol))))
FROM CTE
)
SELECT
C2.SeqNo,
C2.CatId,
S.StateNm,
C.CountryNm
FROM C2
LEFT JOIN #State S
ON C2.IdCol ='StateId'
AND C2.IdVal = S.StateId
LEFT JOIN #Country C
ON C2.IdCol ='Country '
AND C2.IdVal = c.CountryId

SqlServer Trigger to add multiple rows

I have the following trigger:
ALTER TRIGGER .[dbo].[trgAfterInsertComment]
ON .[dbo].[Comment]
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #Id int;
declare #LoanId int;
declare #CopyId int;
declare #CustomerId int;
declare #Comment nvarchar(255);
--DECLARE cur CURSOR FOR
select #Id = Id from inserted
select #LoanId = LoanId from inserted
--select #CopyId = CopyId from deleted
--select #CustomerId = CustomerId from deleted
select #Comment = Comment from inserted
-- OPEN cur
--FETCH NEXT FROM cur INTO #Id, #ISBN, #Purchase_Date
--WHILE ##FETCH_STATUS = 0 BEGIN
-- your business logic
Declare #Title nvarchar(255);
select #Title = (Select Title from Book where ISBN = (select ISBN from copy where Id = (select CopyId from Loan where Id = #LoanId)))
select #CustomerId = (Select CustomerId from Loan where Id = #LoanId)
select #CopyId = (Select CopyId from Loan where Id = #LoanId)
insert into Activity("Heading", "Date")
values(Concat('New Comment added - Id: ', #Id, ' Title: ', #Title, ' Copy Id: ', #CopyId, ' Customer Id: ', #CustomerId, ' Comment: ', #Comment), GETDATE())
--FETCH NEXT FROM cur INTO #Id, #ISBN, #Purchase_Date
--END
--CLOSE cur
--DEALLOCATE cur
end
As you can see I have commented out a cursor that I was using to handle multiple inserts. Could someone tell me how I can handle multiple inserts without the cursor, as after reading around I see that using a cursor is a bad idea?
With the above trigger, if I try to insert multiple lines like this:
USE [Library]
GO
INSERT INTO [dbo].[Comment]
([LoanId]
,[Comment])
VALUES
(47, 'test'),
(48, 'test'),
(50, 'test')
GO
Only the first row is inserted into my Activity table. Thanks for any help
You need to shift it to be set based, using variables and a loop will cause you issues. Can't test the below, but something like:
INSERT INTO Activity
(
Heading ,
[Date]
)
SELECT CONCAT('New Comment added - Id: ', I.id, ' Title: ', COALESCE(B.Title,''), ' Copy Id: ', COALESCE(L.CopyID,''), ' Customer Id: ', COALESCE(L.CustomerID,'')) ,
GETDATE()
FROM inserted AS I
LEFT JOIN Loan AS L ON I.loanId = L.loanId
LEFT JOIN Copy AS C ON C.Id = L.CopyId
LEFT JOIN Book AS B ON B.ISBN = C.ISBN;
Do this querying inserted table directly.
insert into [dbo].[Comment] (LoanId, Comment)
select LoanId, Comment from inserted
You can change the select query to more complex to achieve the result using query only.

Logging data changes into table with dynamically changing name in MS SQL

I am trying to log data changes in MS SQL with trigger. I want to create a new History table in every month. After I found the answer how to change table name Dynamically I can't access the DELETED and INSERTED tables anymore. It says invalid object name.
ALTER TRIGGER [dbo].[teszttablatrigger] ON [teszt].[dbo].[teszt] FOR DELETE, INSERT, UPDATE AS
declare #hist nvarchar(40)
set #hist='teszthistory_' + CAST(YEAR(getdate()) as NCHAR(4))+ '_' + (case when Month(GETDATE())<10 then '0' + CAST (Month(GETDATE()) as NCHAR(1))
when Month(GETDATE())>=10 then CAST (Month(GETDATE()) as NCHAR(2)) end)
declare #DynamicSql1 nvarchar(2000)
declare #DynamicSql2 nvarchar(2000)
set #DynamicSql1 = N'IF NOT EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N''[History][dbo].[#hist]'')
AND OBJECTPROPERTY(id, N''IsUserTable'') = 1)
CREATE TABLE [History].[dbo].[#hist] ( kulcs int, szoveg varchar(40), modtip varchar(40), datum datetime default getdate())'
Exec sp_executesql #DynamicSql1, N'#hist nvarchar(40)', #hist=#hist
set #DynamicSql2 = N'INSERT INTO [History].[dbo].[#hist] (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''delete''
FROM DELETED
INSERT INTO [History].[dbo].[#hist] (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''insert''
FROM INSERTED'
Exec sp_executesql #DynamicSql2, N'#hist nvarchar(40)', #hist=#hist
Thanks for the answers in advance.
Dynamic sql is executed in his own scope, so you can't acces inserted/deleted objects.
You could write a SQLCLR trigger in C# look this example SQLCLR Trigger
but I think the easiest way is to use a temp table to write changes to, so the dynamic part is fixed.
Take a look:
DROP TRIGGER [test_history]
GO
CREATE TRIGGER [test_history] ON [test_table]
FOR DELETE, INSERT, UPDATE
AS
BEGIN
declare #date datetime = getdate()
declare #guid uniqueidentifier = newid()
declare #hist nvarchar(40)= 'test_history_' + CAST(YEAR(#date ) as VARCHAR(4))+ '_' + right('0' + CAST(Month(#date) as VARCHAR(2)), 2)
DECLARE #T1 BIT = 0
SELECT top 1 #T1 = 1 FROM sys.tables WHERE [TYPE] = 'U' AND name = 'test_history_9999_99'
IF #T1 = 1 TRUNCATE table test_history_9999_99
DECLARE #T2 BIT = 0
SELECT top 1 #T2 = 1 FROM sys.tables WHERE [TYPE] = 'U' AND name = #hist
IF #T1=0 BEGIN
SELECT ID, [desc], #date DATE_TIME, cast('delete' as varchar(20)) as operation, CAST(#guid AS varchar(64)) BATCH
INTO test_history_9999_99
FROM DELETED
END else begin
INSERT INTO test_history_9999_99
SELECT ID, [desc], #date, cast('delete' as varchar(20)) as operation, CAST(#guid AS varchar(64)) BATCH
FROM DELETED
end
INSERT INTO test_history_9999_99
SELECT ID, [desc], #date, cast('insert' as varchar(20)) as operation, CAST(#guid AS varchar(64)) BATCH
FROM inserted
IF #T2 = 0 BEGIN
EXEC sp_rename 'test_history_9999_99', #hist
END ELSE BEGIN
declare #DynamicSql nvarchar(2000)
SET #DynamicSql = 'INSERT INTO ' + #hist + ' SELECT * FROM test_history_9999_99;'
Exec sp_executesql #DynamicSql
END
END
My test_table contains only two columns ID and [Desc].
In the history tables I have added a DATETIME column with change date and a UNIQUEIDENTIFIER column so you can group all changes in a batch if you INSERT/UPDATE many records with a single operation
Tanks for the answer #MtwStark. Now it works, I can check if the table exists, and create it if not. And have eaccess to the DELETED and INSERTED tables.
I'm not sure, if in your solution I have to create the test_history_9999_99 table in advance. Because when I used your trigger I've got an error about column insertion (I didn't understand the error completly).
Now my code looks like this. I'm not sure if it can handle INSERT/UPDATE many records with a single operation. Probably I still need to insert this code for it? CAST(#guid AS varchar(64)) BATCH . I'm not sure what it really does, I have to look into it deeper.
CREATE TRIGGER [dbo].[teszttablatrigger] ON [teszt].[dbo].[teszt] FOR DELETE, INSERT, UPDATE AS
declare #hist nvarchar(40)
set #hist='teszthistory_' + CAST(YEAR(getdate()) as NCHAR(4))+ '_' + (case when Month(GETDATE())<10 then '0' + CAST (Month(GETDATE()) as NCHAR(1))
when Month(GETDATE())>=10 then CAST (Month(GETDATE()) as NCHAR(2)) end)
select * into #ins from inserted
select * into #del from deleted
declare #DynamicSql nvarchar(2000)
DECLARE #T2 BIT = 0
SELECT top 1 #T2 = 1 FROM sys.tables WHERE [TYPE] = 'U' AND name = #hist
if #T2=0 begin
set #DynamicSql = N'CREATE TABLE [' + #hist + '] ( kulcs int, szoveg varchar(40), modtip varchar(40), datum datetime default getdate())'
Exec sp_executesql #DynamicSql
end
set #DynamicSql = N'INSERT INTO ' + #hist + ' (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''delete''
FROM #del
INSERT INTO ' + #hist + ' (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''insert''
FROM #ins'
Exec sp_executesql #DynamicSql
Try refreshing intellisense. Ctrl+Shift+R see if that might help. Or do a database table refresh.
If you have SQL Server enterprise (check your version) Then better way will be to enable CDC.
https://msdn.microsoft.com/en-us/library/cc645937(v=sql.110).aspx

Rewrite stored procedure to remove cursors

I'm attempting to remove the cursors from this stored procedure but not sure of the Best latest best practise for this kind of operational to run in a efficient statement.
Can anyone offer any pseudo code on what to implement to eliminate these from a Dev perspective?
--Generate the channel date from a specified date
DECLARE #ConvDate DATETIME
SET #ConvDate = DateAdd(day,-100,getDate())
WHILE DateDiff(day,GetDate(), #ConvDate ) < 0
BEGIN
EXEC mltGenerateChannelData #ConvDate
SET #ConvDate = DateAdd(day, 1, #ConvDate)
END
CREATE PROCEDURE [dbo].[mltGenerateChannelData] (#ConvDate DATETIME) AS
BEGIN
DECLARE #ChannelId INT,
#URLSignature Varchar(30),
#RawSQL VARCHAR(2000),
#SQLQuery VARCHAR(4000),
#ThisUTMId BIGINT
DECLARE cursChannels CURSOR STATIC FOR
SELECT
ChannelId,
URLSignature,
RawSQL
FROM dbo.TrackingChannel_tbl (NOLOCK)
WHERE ProcessVisitDate = 1
SET #ConvDate = dbo.datePart_fn(#ConvDate)
--Clear out any existing data for this conversion date
DELETE FROM TrackingChannelDailyTotal_tbl
WHERE TrackingDate = #ConvDate
OPEN cursChannels
FETCH cursChannels INTO #ChannelId, #URLSignature, #RawSQL
CREATE TABLE #UTM
(trpUTMID BIGINT PRIMARY KEY,
TotalMArgin MONEY,
RawURLRequest Varchar(2000),
Keywords VARCHAR(1000),
VisitDate DATETIME,
RefererURL VARCHAR(2000))
INSERT INTO #UTM (trputmid, TotalMargin)
SELECT trpUTMID, SUM(b.TotalMArgin)
FROM TrackingConversion_tbl c(NOLOCK), Booking_tbl b(NOLOCK)
WHERE c.BookingId = b.BookingId
AND c.BookedDate >= #ConvDate
GROUP BY trputmid
UPDATE u
SET RawURLRequest = v.RawURLRequest,
Keywords = v.Keywords,
VisitDate = v.VisitDate,
RefererURL = v.RefererURL
FROM #UTM u,
TrackingVisit_tbl (NOLOCK) v
WHERE v.trpUTMID = u.trpUTMId
CREATE TABLE #UTM2 (trpUTMID BIGINT PRIMARY KEY)
WHILE ##FETCH_STATUS = 0
BEGIN
Print 'Processing Channel Id : ' + Convert(varchar(10), #ChannelId)
TRUNCATE TABLE #UTM2
SET #SQLQuery = ' INSERT INTO #UTM2 (trpUTMId)
SELECT u.TrpUTMId
FROM #UTM u
WHERE u.VisitDate >= ''' + COnvert(varchar,#ConvDate) + '''
AND u.VisitDate < DateAdd(day,1,''' + Convert(varchar,#ConvDate) + ''') '
IF #URLSignature <> ''
BEGIN
SET #SQLQuery = #SQLQuery + 'AND u.RawURLRequest like ''%' + #URLSignature + '%'' '
END
IF #RawSQL <> ''
BEGIN
SET #SQLQuery = #SQLQuery + #RawSQL
END
EXEC (#SQLQuery)
INSERT INTO TrackingChannelDailyTotal_tbl (ChannelId, TrackingDate, Conversions, TotalMargin)
SELECT #ChannelId, #ConvDate, Count(u1.trpUTMID), IsNUll(SUM(TotalMargin),0)
FROM #UTM u1, #UTM2 u2
WHERE u1.TRputmid = u2.trputmid
FETCH cursChannels INTO #ChannelId, #URLSignature, #RawSQL
END
CLOSE cursChannels
DEALLOCATE cursChannels
If you use SSMS Tools (http://www.ssmstoolspack.com/) and 'Include Actual Execution plan' with the query, you can break down the bottle-necks in the query, take each part and try to isolate and improve the query.
You may find that the issue is with a different part of the query, and not the cursor.
if by old server you mean low memory or processing power
and if it is feasible to upgrade the hardware
being a DBA i will suggest to upgrade the hardware.

Resources