Using cursor to loop through a table variable in SQL Server - sql

I have a parameter of a stored procedure which gets some data in the format
1/1/2018-2/1/2018,2/1/2018-3/1/2018,3/1/2018-4/1/2018,4/1/2018-5/1/2018,5/1/2018-6/1/2018,6/1/2018-7/1/2018,7/1/2018-8/1/2018,8/1/2018-9/1/2018,9/1/2018-10/1/2018,
10/1/2018-11/1/2018,11/1/2018-12/1/2018,12/1/2018-12/31/2018
I have a function which splits the data based on the , character and stores the results into a table variable as shown here:
declare #SPlitDates table(ItemNumber int, Item nvarchar(max))
insert into #SPlitDates
select *
from dbo.SPlitFunction(#RequestData, ',')
After this I have to perform certain operations on the data range so I use cursors to loop through the temp table as shown below
DECLARE cur CURSOR FOR
SELECT Item
FROM #SPlitDates
ORDER BY ItemNumber
OPEN cur
FETCH NEXT FROM cur INTO #monthStart
WHILE ##FETCH_STATUS = 0
BEGIN
-- Some operation
END
The max data points that I will get in the temp table is the date range for 12 months.
My question is that could I be using something else apart from cursors to improve performance or it doesn't matter when the dataset is really this small.
Thanks
Edit - To show operation inside the cursor
declare #SPlitDates table(ItemNumber int, Item nvarchar(max))
insert into #SPlitDates
select *
from dbo.SPlitFunction(#RequestData, ',')
declare #SPlitDatesData table (ItemNumber varchar(100), Item nvarchar(max))
declare #SPlitDatesAvgData table(Code nvarchar(100), Val decimal(18,2))
declare #dataFilter as nvarchar(max),
#SQL as nvarchar(max);
declare #monthStart nvarchar(100)
declare #count int
set #count = 0
--Declaring a cursor to loop through all the dates as defined in the requested quarter
DECLARE cur CURSOR FOR
SELECT Item
FROM #SPlitDates
ORDER BY ItemNumber
OPEN cur
FETCH NEXT FROM cur INTO #monthStart
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Period NVARCHAR(100)
SET #Period = #monthStart
INSERT INTO #SPlitDatesData
--split the dates to get the start and the end dates
SELECT *
FROM dbo.SPlitFunction(#Period, '-')
DECLARE #PeriodStart NVARCHAR(100)
DECLARE #PeriodEnd NVARCHAR(100)
SET #PeriodStart = (SELECT Item FROM #SPlitDatesData WHERE ItemNumber = 1)
SET #PeriodEnd = (SELECT Item FROM #SPlitDatesData WHERE ItemNumber = 2)
DELETE FROM #SPlitDatesData
--add the start and end dates to the filter
SET #dataFilter = 'StatusDate between convert(datetime,('''+#PeriodStart+'''))
and DATEADD(dy, 1, convert(datetime,('''+#PeriodEnd+''')))'
SET #count = #count +1;
SET #SQL = 'INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
VALUES (#count,
''SL Payroll'',(select dbo.GetAverageCycleBetweenBids('''+#PeriodStart+''',
'''+#PeriodEnd+''',''SL''))
)'
EXEC SP_ExecuteSQL #SQL, N'#count int', #count;
SET #count = #count +1;
SET #SQL = 'INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
VALUES (#count,
''GV Payroll'',(select dbo.GetAverageCycleBetweenBids('''+#PeriodStart+''',
'''+#PeriodEnd+''',''GV''))
)'
EXEC SP_ExecuteSQL #SQL , N'#count int', #count;
SET #count = #count +1;
SET #SQL = 'Insert into #BidAverageCycleCalculation (SortOrder,Code,Data)
Values (#count,
''Global Payroll'',(select dbo.GetAverageCycleBetweenBids('''+#PeriodStart+''',
'''+#PeriodEnd+''',''GVS''))
)'
EXEC SP_ExecuteSQL #SQL, N'#count int', #count;
SET #count = #count +1;
SET #SQL = 'Insert into #BidAverageCycleCalculation (SortOrder,Code,Data)
Values (#count,
''TimeHCM'',(select dbo.GetAverageCycleBetweenBids('''+#PeriodStart+''',
'''+#PeriodEnd+''',''Time''))
)'
EXEC SP_ExecuteSQL #SQL, N'#count int', #count;
delete from #SPlitDatesAVgData
FETCH NEXT FROM cur INTO #monthStart
END
CLOSE cur
DEALLOCATE cur

This uses two parts - first convert your string into a table, then do bulk inserts into your destination. No need for cursors.
** Please excuse any syntax errors as I'm doing it without access to your actual tables or functions so cant test it, but you get the idea
declare #in varchar(max)
set #in= '1/1/2018-2/1/2018,2/1/2018-3/1/2018,3/1/2018-4/1/2018,4/1/2018-5/1/2018,5/1/2018-6/1/2018,6/1/2018-7/1/2018,7/1/2018-8/1/2018,8/1/2018-9/1/2018,9/1/2018-10/1/2018,10/1/2018-11/1/2018,11/1/2018-12/1/2018,12/1/2018-12/31/2018'
declare #xml xml;
set #xml= convert(xml,'<r><f>'+replace(replace(#in,',','</t></r><r><f>'),'-','</f><t>') +'</t></r>')
declare #t table(id int identity, f date, t date)
insert #t
select
Tbl.Col.value('f[1]', 'date') f,
Tbl.Col.value('t[1]', 'date') t
FROM #xml.nodes('//r') Tbl(Col)
select * from #t
declare #count int;
select #count=count(*) from #t
INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
select id, 'SL Payroll',(select dbo.GetAverageCycleBetweenBids(f,t,'SL')) from #t
INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
select id+#count,'GV Payroll',(select dbo.GetAverageCycleBetweenBids(f,t,'GV')) from #t
INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
select id+#count*2,'Global Payroll',(select dbo.GetAverageCycleBetweenBids(f,t,'GVS')) from #t
INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
select id+#count*3,'TimeHCM',(select dbo.GetAverageCycleBetweenBids(f,t,'Time')) from #t

Related

How to get dynamic SQL query output into declared variable in SQL Server? [duplicate]

I have a piece of dynamic SQL I need to execute, I then need to store the result into a variable.
I know I can use sp_executesql but can't find clear examples around about how to do this.
If you have OUTPUT parameters you can do
DECLARE #retval int
DECLARE #sSQL nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
DECLARE #tablename nvarchar(50)
SELECT #tablename = N'products'
SELECT #sSQL = N'SELECT #retvalOUT = MAX(ID) FROM ' + #tablename;
SET #ParmDefinition = N'#retvalOUT int OUTPUT';
EXEC sp_executesql #sSQL, #ParmDefinition, #retvalOUT=#retval OUTPUT;
SELECT #retval;
But if you don't, and can not modify the SP:
-- Assuming that your SP return 1 value
create table #temptable (ID int null)
insert into #temptable exec mysp 'Value1', 'Value2'
select * from #temptable
Not pretty, but works.
DECLARE #vi INT
DECLARE #vQuery NVARCHAR(1000)
SET #vQuery = N'SELECT #vi= COUNT(*) FROM <TableName>'
EXEC SP_EXECUTESQL
#Query = #vQuery
, #Params = N'#vi INT OUTPUT'
, #vi = #vi OUTPUT
SELECT #vi
DECLARE #tab AS TABLE (col1 VARCHAR(10), col2 varchar(10))
INSERT into #tab EXECUTE sp_executesql N'
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2'
SELECT * FROM #tab
Return values are generally not used to "return" a result but to return success (0) or an error number (1-65K). The above all seem to indicate that sp_executesql does not return a value, which is not correct. sp_executesql will return 0 for success and any other number for failure.
In the below, #i will return 2727
DECLARE #s NVARCHAR(500)
DECLARE #i INT;
SET #s = 'USE [Blah]; UPDATE STATISTICS [dbo].[TableName] [NonExistantStatisticsName];';
EXEC #i = sys.sp_executesql #s
SELECT #i AS 'Blah'
SSMS will show this
Msg 2727, Level 11, State 1, Line 1
Cannot find index 'NonExistantStaticsName'.
If you want to return more than 1 value use this:
DECLARE #sqlstatement2 NVARCHAR(MAX);
DECLARE #retText NVARCHAR(MAX);
DECLARE #ParmDefinition NVARCHAR(MAX);
DECLARE #retIndex INT = 0;
SELECT #sqlstatement = 'SELECT #retIndexOUT=column1 #retTextOUT=column2 FROM XXX WHERE bla bla';
SET #ParmDefinition = N'#retIndexOUT INT OUTPUT, #retTextOUT NVARCHAR(MAX) OUTPUT';
exec sp_executesql #sqlstatement, #ParmDefinition, #retIndexOUT=#retIndex OUTPUT, #retTextOUT=#retText OUTPUT;
returned values are in #retIndex and #retText
Declare #variable int
Exec #variable = proc_name
DECLARE #ValueTable TABLE
(
Value VARCHAR (100)
)
SELECT #sql = N'SELECT SRS_SizeSetDetails.'+#COLUMN_NAME+' FROM SRS_SizeSetDetails WHERE FSizeID = '''+#FSizeID+''' AND SRS_SizeSetID = '''+#SRS_SizeSetID+'''';
INSERT INTO #ValueTable
EXEC sp_executesql #sql;
SET #Value='';
SET #Value = (SELECT TOP 1 Value FROM #ValueTable)
DELETE FROM #ValueTable
This worked for me:
DECLARE #SQL NVARCHAR(4000)
DECLARE #tbl Table (
Id int,
Account varchar(50),
Amount int
)
-- Lots of code to Create my dynamic sql statement
insert into #tbl EXEC sp_executesql #SQL
select * from #tbl
Here's something you can try
DECLARE #SqlStatement NVARCHAR(MAX) = ''
,#result XML
,#DatabaseName VARCHAR(100)
,#SchemaName VARCHAR(10)
,#ObjectName VARCHAR(200);
SELECT #DatabaseName = 'some database'
,#SchemaName = 'some schema'
,#ObjectName = 'some object (Table/View)'
SET #SqlStatement = '
SELECT #result = CONVERT(XML,
STUFF( ( SELECT *
FROM
(
SELECT TOP(100)
*
FROM ' + QUOTENAME(#DatabaseName) +'.'+ QUOTENAME(#SchemaName) +'.' + QUOTENAME(#ObjectName) + '
) AS A1
FOR XML PATH(''row''), ELEMENTS, ROOT(''recordset'')
), 1, 0, '''')
)
';
EXEC sp_executesql #SqlStatement,N'#result XML OUTPUT', #result = #result OUTPUT;
SELECT DISTINCT
QUOTENAME(r.value('fn:local-name(.)', 'VARCHAR(200)')) AS ColumnName
FROM #result.nodes('//recordset/*/*') AS records(r)
ORDER BY ColumnName
This was a long time ago, so not sure if this is still needed, but you could use ##ROWCOUNT variable to see how many rows were affected with the previous sql statement.
This is helpful when for example you construct a dynamic Update statement and run it with exec. ##ROWCOUNT would show how many rows were updated.
Here is the definition

Need assistance with a exec( string )

I am writing an exe string in sql 2012 and I am getting the following error.
Incorrect syntax near Group By. Any assistance would be much appreciated in advance.
declare #counter int = (select count(1) from #temp) SELECT * FROM #temp
declare #tab_name varchar(100)
declare #col_name varchar(100)
while (#counter > 0)
begin
set #tab_name = (select table_name from #temp where rn = #counter)
set #col_name = (select field_name from #temp where rn = #counter)
exec ('select
'''+#tab_name+''','''+#col_name+''',max(len('+#col_name+')),'''+#col_name+'''
from [x3v7].[LIVE].'+#tab_name+'''Group By'''+#col_name+'''Having
max(len('+#col_name+'))>12''')
set #counter -= 1
end
Here's a the basic structure of a cursor loop with dynamic SQL to get you started.
The string concatenation is still a mess. Clean it up using the CONCAT and QUOTENAME functions to generate the query.
declare #tab_name varchar(100)
declare #col_name varchar(100)
declare c cursor local for
select table_name, field_name
from #temp
open c;
fetch next from c into #tab_name, #col_name;
while ##FETCH_STATUS = 0
begin
declare #sql nvarchar(max) = 'select
'''+#tab_name+''','''+#col_name+''',max(len('+#col_name+')),'''+#col_name+'''
from [x3v7].[LIVE].'+#tab_name+'''Group By'''+#col_name+'''Having
max(len('+#col_name+'))>12'''
print #sql
--exec(#sql);
fetch next from c into #tab_name, #col_name
end
close c;
deallocate c;

Values not passed to dynamic query in sql server

Is it possible to print the Dynamic select statement after passing the parameters values.When i print the SELECT #SQL.It is giving only select statement without parameter values.In my below procedure the dynamic select statement not giving correct output after passing the parameters.But when i directly passing the the parameter values into the select statement it is giving correct output.In my below procedure splitting function is working fine.Else part in
if statement is not working properly.
CREATE TYPE TableVariable AS TABLE
(
id int identity(1,1),
field_ids INT,
value VARCHAR(MAX)
)
Alter PROCEDURE Testing
(
#TableVar TableVariable READONLY,
#Catalog_id INT
)
AS
Declare #maxPK INT
Declare #pk INT
Declare #fid INT
Declare #is_List SMALLINT
Declare #val VARCHAR(MAX)
Declare #field_Type VARCHAR(50)
Declare #Where VARCHAR(MAX)
Declare #SQL NVARCHAR(MAX);
Set #pk = 1
BEGIN
BEGIN TRY
SET NOCOUNT ON;
Select #maxPK = count(*) From #TableVar
SELECT #Catalog_id
Set #SQL = 'SELECT DISTINCT v1.entity_id from values v1 inner join listings l ON v1.entity_id = l.entity_id WHERE l.c_id=#Catalog_id'
While #pk <= #maxPK
BEGIN
SELECT #fid= field_ids FROM #TableVar where id=#pk;
SELECT #val= value FROM #TableVar where id=#pk;
SELECT #field_Type=type,#is_List=is_list FROM FIELD WHERE ID=#fid
IF (#is_List = 0)
BEGIN
SET #SQL += ' and exists (select 1 from values v'+convert(varchar(15),#pk+1)+' where v1.entity_id = v'+convert(varchar(15),#pk+1)+'.entity_id and v'+convert(varchar(15),#pk+1)+'.field_id=#fid and(value IN(SELECT val FROM spliting(#val,'',''))))'
SELECT #fid
END
else IF (#is_List = 1 OR #field_Type = 'xy')
BEGIN
SET #SQL += ' and exists (select 1 from values v'+convert(varchar(15),#pk+1)+' where v1.entity_id = v'+convert(varchar(15),#pk+1)+'.entity_id and v'+convert(varchar(15),#pk+1)+'.field_id=#fid and(value in(#val)))'
SELECT #fid
END
Select #pk = #pk + 1
END
EXECUTE SP_EXECUTESQL #SQL, N'#Catalog_id int,#fid int,#val varchar(max)',#Catalog_id=#Catalog_id,#fid=#fid,#val=#val
SELECT #SQL
END TRY
BEGIN CATCH
END CATCH
END
DECLARE #DepartmentTVP AS TableVariable;
insert into #DepartmentTVP values(1780,'Smooth As Silk Deep Moisture Shampoo,Smooth As Silk Deeper Moisture Conditioner')
--insert into #DepartmentTVP values(1780,'Smooth As Silk Deeper Moisture Conditioner')
insert into #DepartmentTVP values(1782,'037-05-1129')
insert into #DepartmentTVP values(2320,'["fairtrade","usda_organic","non_gmo_verified"]')
SELECT * FROM #DepartmentTVP
EXEC Testing #DepartmentTVP,583
Yes right before the statment:
EXECUTE SP_EXECUTESQL #SQL, N'#Catalog_id int,#fid int,#val varchar(max)',#Catalog_id=#Catalog_id,#fid=#fid,#val=#val
type:
print #SQL

Evaluate expression stored in Varchar Column directly in the "select" statement

I have a table named PriceDetails with three columns
"price","discount formula" and "finalprice". I have to calculate final price based on "Price" and "discount formula" columns.
My table looks like this,
Price DiscountFormulae
100 100*(3/100)
200 200*(1.1/100)+200*(5/100)
300 300*(1/100)+300*(3/100)+300*(2/100)
400 400*(7/100)+400*(6.6/100)+400*(5.5/100)+400*(4/100)
I want to calculate final price. The formula would be "Price"-"DiscountFormulae"
for example
100 - (100*(3/100)) =97
My Expected Output would be
Price DiscountFormulae FinalPrice
100 100*(3/100) 97
Note: My DiscountFormulae Column is Varchar(1000)
How do I get this. Please help me.
Not very good solution, but it shows, how difficult is it:
CREATE TABLE #temp (Price int, DiscountFormula Varchar(1000))
INSERT INTO #temp
(#temp.Price, #temp.DiscountFormula)
VALUES
(100,'100.0*(3.0/100)'),
(200,'200.0*(1.1/100)+200*(5.0/100)'),
(300,'300.0*(1.0/100)+300*(3.0/100)+300*(2.0/100)'),
(400,'400.0*(7.0/100)+400*(6.6/100)+400*(5.5/100)+400*(4.0/100)')
DECLARE #price int
DECLARE cur CURSOR FOR SELECT t.Price FROM #temp AS t
OPEN cur
FETCH NEXT FROM cur INTO #price
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #sql nvarchar(MAX) = N'
SELECT
t.Price,
t.DiscountFormula,
' + (SELECT TOP 1 DiscountFormula FROM #Temp WHERE Price = #price) + N' as Value
FROM #temp AS t
WHERE Price = ' +CONVERT(nvarchar(max),#price)
EXEC sp_executesql #sql
FETCH NEXT FROM cur INTO #price
END
CLOSE cur
DROP TABLE #temp
By the way, use float values in formula.
You could do it in a stored procedure, like:
But dynamic SQL, like exec or sp_executesql, is not allowed in user defined functions.
DECLARE #calc varchar(max)
SET #calc = '100*(3/100)'
DECLARE #sql nvarchar(max)
DECLARE #result float
SET #sql = N'SET #result = ' + #calc
exec sp_executesql #sql, N'#result float output', #result out
SELECT #result

Populate temp variable within stored procedure using dynamic sql

I have a stored procedure where I am declaring an int variable that needs to be populated using a dynamic sql
CREATE PROCEDURE USP_aTABLE_ADD
/*
stored procedure variables
*/
AS
DECLARE #count int
SET #count = 1
DECLARE #qry nvarchar(max)
/*
SET UP #qry which will look like this
SELECT #count = count(*) FROM aTABLE WHERE (col1 = 'val1' AND col2 = 'val2'...)
*/
/*
How to get the value of #count so that I can continue with add process
*/
IF #count = 0
BEGIN
/*add logic*/
END
Use sp_executeSQL and an output parameter:
DECLARE #count INT = 1
DECLARE #qry NVARCHAR(MAX)
SET #qry = N'set #count = (....)'
EXEC sp_executesql #qry, N'#count INT OUTPUT', #count OUTPUT
SELECT #count
You could try and work with a temporary table like this
DECLARE #tmp TABLE(cnt INT)
DECLARE #qry nvarchar(max)
-- Insert the count returned by dynamic SQL into temp table
SET #qry = 'SELECT COUNT(*) FROM Table WHERE condition'
INSERT INTO #tmp EXEC(#qry)
DECLARE #count INT
SET #count = (SELECT TOP 1 cnd FROM #tmp)