How to recursively update a table with a stored procedure - sql

I have a stored procedure that selects some data in a different format from my original table:
USE [VolTracker]
GO
DECLARE #return_value int,
#offset int
SET #offset = 5
WHILE #offset >= 1 BEGIN
EXEC #return_value = [dbo].[sp_getStats]
#Doffset = #offset,
#StartTime = --Some Datetime,
#EndTime = --Some later Datetime,
#Contract = NULL
SET #offset = #offset - 1
END
GO
This specific example selects all 5 of the tables that I would like it to properly. However, I would like all of these tables joined into one table. How might I go about doing this?

Create a table variable that matches the schema of the resultset returned by sp_getStats. Then, insert into this variable within your loop:
...
declare #Stage table (YourColumn1 varchar(10), YourColumn2 int, ...);
WHILE #offset >= 1 BEGIN
INSERT INTO #Stage
EXEC #return_value = [dbo].[sp_getStats]
#Doffset = #offset,
#StartTime = --Some Datetime,
#EndTime = --Some later Datetime,
#Contract = NULL
SET #offset = #offset - 1
END
select * from #Stage;
The above will work to return the union of all the resultsets returned, however if its possible for you to modify the procedure (or create a new one) that can return the complete set without a loop then I would suggest doing do.

Create a temporary table or table variable and insert into table each time you execute the stored procedure. Check this out.
//declare your #temptable here
WHILE #offset >= 1 BEGIN
INSERT INTO #tempTable
EXEC #return_value = [dbo].[sp_getStats]
#Doffset = #offset,
#StartTime = --Some Datetime,
#EndTime = --Some later Datetime,
#Contract = NULL
SET #offset = #offset - 1
END

Related

Dynamic SQL DateTime comparison while data archive is not working

I have written this stored procedure for data purge from one of my tables. I have made it configurable to either archive the data or do a hard delete. Since the volume of data is quiet large, i am using loops to do the same.
The delete part of it is working fine However the archive part of it is giving me hard times and i am kind of stuck there with multiple tries.
Here is my SP.
ALTER PROCEDURE [dbo].[spPurgeRecords_new] (
#Age AS INT,
#NumberOfLoops AS BIGINT,
#DeleteSize BIGINT,
#IsArchive BIT
)
AS
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN
DECLARE #CurrentLoop AS BIGINT;
SET #CurrentLoop = 0
declare #cutoffDate datetime;
declare #maxXDate datetime;
declare #loop varchar(50);
select #cutoffDate = dateadd(year,-#Age,getdate())
select #maxXDate = max(dateCreated) from cbr_audit where dateCreated < #cutoffDate
declare #date varchar(100), #cmd varchar(1000),#archivedate varchar(100)
set #date = (select FORMAT(getdate(), 'yyyyMMdd'));
set #archivedate = (select FORMAT(#maxXDate, 'yyyyMMdd'));
declare #backupTable varchar(100)
set #backupTable = 'cbr_audit_Backup_' + #date;
BEGIN TRY
BEGIN TRANSACTION
WHILE #CurrentLoop < #NumberOfLoops
BEGIN
IF #IsArchive = 1
BEGIN
--Archive the records into a backup table
IF OBJECT_ID (#backupTable, N'U') IS NULL
begin
set #cmd = 'SELECT * INTO [cbr_audit_Backup_'+ #date +'] FROM [cbr_audit] WITH (NOLOCK) where convert(datetime,dateCreated,101) <= CONVERT(DATETIME, ''' + #archivedate + ''', 101)'
exec(#cmd)
end
--Delete the rows from cbr_audit table
DELETE
FROM dbo.cbr_audit
WHERE id IN
(SELECT TOP(#DeleteSize) id
FROM dbo.cbr_audit WITH (NOLOCK)
WHERE dateCreated <= #maxXDate);
END
ELSE
BEGIN
-- Delete the records
DELETE
FROM dbo.cbr_audit
WHERE id IN
(SELECT TOP(#DeleteSize) id
FROM dbo.cbr_audit WITH (NOLOCK)
WHERE dateCreated <= #maxXDate);
END
-- WAITFOR DELAY '00:00:00:500';
SET #CurrentLoop = #CurrentLoop + 1;
set #loop = cast(#currentloop as varchar(50))
RAISERROR (#loop, 0, 1) WITH NOWAIT
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
--Rollback
RETURN
END CATCH
END
In This SP, in the archive part of code the dynamic sql is not giving any results. DateCreated is of Datetime type. Can someone please help me on this.
Thanks in Advance.
Seems that you are using SQL 2016 or above, try CONCAT, something like:
select concat('SELECT * INTO [cbr_audit_Backup_', #date,'] FROM [cbr_audit] WITH (NOLOCK) where convert(datetime,dateCreated,121) <= ''', CONVERT(varchar(30), #archivedate , 121), '''')
few more things:
I strongly recommend you always to use style 121 (canonical)
or 126 (ISO8601) so you'll SQL will not be confused by mm/dd/yyyy (101) or dd/mm/yyyy (103).
you are deleting by chunks, but the transaction is for all the block. consider doing only transaction for each delete (implicit)
instead insert into and then delete, have a look to OUTPUT clause to DELETE here

While loop became slower after some recursions in sql server

I have a stored procedure which uses a while loop to iterate to each record
and do some calculations. The stored procedure has the following logic:
Inserts some records from old_table to new_table for a specific range
(7.8 million records) . insert took 4 minutes.
Then it copies records which have amount > 100 into a temp table
(37 000 records).
create clustered index on the id column of temp table.
start the while loop to iterate to each id column in temp table (total 37 000 records).
for each iteration a stored procedure is called inside the while loop.
The Sp works fine (faster) Up to 5 000 records (it completes in 5 mins)
but after 5 000 there is a delay in processing records. The delay occurs after processing each 100 records. Is there any reason for this delay?
sample structure of the stored procedure:
use my_db
go
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID('[dbo].[usp_datacopy]') IS NULL -- Check if SP Exists
EXEC('CREATE PROCEDURE [dbo].[usp_datacopy] AS SET NOCOUNT ON;') -- Create dummy/empty SP
GO
ALTER PROCEDURE [dbo].[usp_datacopy]
#Startdate datetime,
#Enddate datetime,
#Percent int
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRAN
DECLARE #PerVal NUMERIC(10,2) = #percent /100.00,--Get the percent value for input.
#wkstartdate DateTime,
#wkenddate Datetime,
#OfficeID int,
#id int = 1,
#spcount int ,
#rdate datetime,
IF OBJECT_ID ('TEMPDB..#temptable') IS NOT NULL
DROP TABLE #temptable
INSERT INTO [dbo].[new_table]
( TimeID, OfficeID, dateval, rHour, Complexity,RowCountval)
SELECT TimeID + 364, OfficeID,dateadd(dd,364,dateval),rHour, Complexity , RowCountval,
FROM [dbo].[old_table] WITH (NOLOCK)
WHERE dateval BETWEEN #Startdate AND #Enddate
--select Day records with value > 100
SELECT ROW_NUMBER() over(order by timeid) id,TimeID, OfficeID, dateval, rHour, Complexity,RowCountval
INTO #temptable
FROM [dbo].[new_table] WITH (NOLOCK)
WHERE RDATE BETWEEN #wkstartdate AND #wkenddate
AND RowCountval > 100
CREATE CLUSTERED INDEX id_index on #temptable (id)
SELECT #spcount = COUNT (1) from #temptable
WHILE #id <= #spcount --37000 records
BEGIN
SELECT #OfficeID = officeid FROM #temptable WHERE id = #id
SELECT #rdate = dateval FROM #temptable WHERE id = #id
EXEC CalcPercentforEachRecord #rdate, #OfficeID, #PerVal
PRINT #ID
SELECT #id = #id + 1
SELECT #OfficeID = NULL
SELECT #rdate = NULL
END
IF ##ERROR = 0
BEGIN
COMMIT TRAN;
END
END TRY
BEGIN CATCH
SELECT ##ERROR AS ERROR,ERROR_MESSAGE() AS ErrorMessage,ERROR_LINE() AS ErrorLineNo
ROLLBACK TRAN;
END CATCH
SET NOCOUNT OFF;
END

SQL Converting int to varchar

I need help converting an integer to a varchar.
I'm trying to write a procedure that takes in a ProfileID and a Currenttime; using those two values it finds the start time of the profileID and subtracts currenttime from
starttime and returns hours:minutes:seconds.
What am I doing wrong, is there a better way to write this?
Thanks.
CREATE PROCEDURE [dbo].[CalculateElaspedTime]
-- Add the parameters for the stored procedure here
#ProfileID nvarchar(10),
#CurrentDateTime datetime = ''
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
if #CurrentDateTime = CAST('' as datetime)
set #CurrentDateTime = GETDATE()
DECLARE #StartTime datetime;
DECLARE #ElaspedTime time;
DECLARE #hh int;
DECLARE #mm int;
DECLARE #ss int;
Declare #TimeString varchar
set #StartTime = (Select top 1 [DateTime] From Log WHERE ProfileID = #ProfileID);
set #hh = DateDiff(hour,#StartTime,#CurrentDateTime);
set #mm = DateDiff(minute,#StartTime,#CurrentDateTime)-60*#hh;
set #ss = DateDiff(second,#StartTime,#CurrentDateTime)-60*#mm;
set #TimeString = (Select CAST(#hh as varchar)); -- Fails Here
set #ElaspedTime = convert(datetime, cast(#hh as varchar) + ':' + cast(#mm as varchar) + ':' + cast(#ss as varchar));
INSERT INTO Log (ElaspedTime) Values (#ElaspedTime);
END
Try this. All of that excitement in the function may be unnecessary.
CONVERT(varchar(10),(#CurrentDateTime-#Start_Time),108)
One problem you have is this statement:
set #StartTime = (Select top 1 [DateTime] From Log WHERE ProfileID = #ProfileID);
Its results are indeterminate as SQL makes no guarantees about result order unless you explicitly specify it in an ORDER BY clause. You should be using ORDER BY or using an aggregate function like MAX() to get the row you want.
And you're doing a lot more work than necessary. SQL Server (recent versions, anyway) support date arithmetic, with the result of subtracting two dates being another date (offset from the SQL Server epoch of 1 Jan 1900 00:00:00.000. This simpler form ought to do you, unless the elapsed time will exceed 1 day:
create procedure dbo.CalculateElaspedTime
#ProfileID nvarchar(10) ,
#CurrentDateTime datetime = ''
as
set nocount on
declare
#now dateTime ,
#start datetime ,
#elapsed varchar(32)
select #now = case coalesce(#currentDateTime,'') when '' then current_timestamp else #currentDateTime end ,
#start = max( [DateTime] )
from dbo.Log
where ProfileId = #profileId
set #elapsed = convert(varchar,#now-#start,108)
insert dbo.Log ( ElapsedTime ) Values (#elapsed);
return 0
go
If your elapsed time might exceed one day, then your original approach is what you want:
create procedure dbo.CalculateElaspedTime
#ProfileID nvarchar(10) ,
#CurrentDateTime datetime = ''
as
set nocount on
declare #now dateTime = case coalesce(#currentDateTime,'') when '' then current_timestamp else #currentDateTime end ,
declare #start datetime = ( select max([DateTime]) from dbo.Log where profileId = #profileId )
declare #elapsed int = select datediff(ss,#now,#start)
declare
#hh int ,
#mm int ,
#ss int
set #hh = #elapsed / 3600 -- 3600 is seconds/hour
set #elapsed = #elapsed % 3600
set #mm = #elapsed / 60 -- 60 is seconds/minute
set #elapsed = #elapsed % 60
set #ss = #elapsed / 1 -- 1 is seconds/second :)
declare #hhmmss = right('00'+convert(varchar,#hh),2)
+ ':' + right('00'+convert(varchar,#mm),2)
+ ':' + right('00'+convert(varchar,#ss),2)
insert dbo.Log ( ElapsedTime ) Values (#hhmmss);
return 0
go

call sql function in stored procedure with different userId each time

I have a sql function and using sql server 2005.
dbo.util (#dailyDate,#userId)
Now I want to call this function for each #userId for a particular Date.So I am writing a Stored Procedure.
Create PROCEDURE [dbo].[DailyAttendenceTemp]
#dailyDate nvarchar(10)
WITH EXEC AS CALLER
AS
Select * FROM dbo.util (#dailyDate,#userId) //I think error is here.
WHERE #userId in (SELECT UserId From TTransactionLog1)
GO
but when I execute the procedure it give the error that-
SQL Server Database Error: Must declare the scalar variable "#userId".
So please tell me how to correct the procedure so that I give only date as a parameter and it run for the same function for each #userId.
I got the answer,,now I am using While loop and it solve my problem.......
DECLARE #i int
DECLARE #userid nvarchar(10)
DECLARE #numrows int
DECLARE #tempUserId_table TABLE (
idx smallint Primary Key IDENTITY(1,1)
, userid nvarchar(10)
)
INSERT #tempUserId_table
SELECT distinct UserID FROM TUser
-- enumerate the table
SET #i = 1
SET #numrows = (SELECT COUNT(*) FROM #tempUserId_table)
IF #numrows > 0
WHILE (#i <= (SELECT MAX(idx) FROM #tempUserId_table))
BEGIN
-- get the next userId primary key
SET #userid = (SELECT userid FROM #tempUserId_table WHERE idx = #i)
Select * FROM dbo.util (#dailyDate,#userid)
-- increment counter for next userId
SET #i = #i + 1
END

How to assign an exec result to a sql variable?

How do you assign the result of an exec call to a variable in SQL? I have a stored proc called up_GetBusinessDay, which returns a single date.
Can you do something like this:
exec #PreviousBusinessDay = dbo.up_GetBusinessDay #Date, -1
I always use the return value to pass back error status. If you need to pass back one value I'd use an output parameter.
sample stored procedure, with an OUTPUT parameter:
CREATE PROCEDURE YourStoredProcedure
(
#Param1 int
,#Param2 varchar(5)
,#Param3 datetime OUTPUT
)
AS
IF ISNULL(#Param1,0)>5
BEGIN
SET #Param3=GETDATE()
END
ELSE
BEGIN
SET #Param3='1/1/2010'
END
RETURN 0
GO
call to the stored procedure, with an OUTPUT parameter:
DECLARE #OutputParameter datetime
,#ReturnValue int
EXEC #ReturnValue=YourStoredProcedure 1,null, #OutputParameter OUTPUT
PRINT #ReturnValue
PRINT CONVERT(char(23),#OutputParameter ,121)
OUTPUT:
0
2010-01-01 00:00:00.000
This will work if you wish to simply return an integer:
DECLARE #ResultForPos INT
EXEC #ResultForPos = storedprocedureName 'InputParameter'
SELECT #ResultForPos
declare #EventId int
CREATE TABLE #EventId (EventId int)
insert into #EventId exec rptInputEventId
set #EventId = (select * from #EventId)
drop table #EventId
From the documentation (assuming that you use SQL-Server):
USE AdventureWorks;
GO
DECLARE #returnstatus nvarchar(15);
SET #returnstatus = NULL;
EXEC #returnstatus = dbo.ufnGetSalesOrderStatusText #Status = 2;
PRINT #returnstatus;
GO
So yes, it should work that way.
I had the same question. While there are good answers here I decided to create a table-valued function. With a table (or scalar) valued function you don't have to change your stored proc. I simply did a select from the table-valued function. Note that the parameter (MyParameter is optional).
CREATE FUNCTION [dbo].[MyDateFunction]
(#MyParameter varchar(max))
RETURNS TABLE
AS
RETURN
(
--- Query your table or view or whatever and select the results.
SELECT DateValue FROM MyTable WHERE ID = #MyParameter;
)
To assign to your variable you simply can do something like:
Declare #MyDate datetime;
SET #MyDate = (SELECT DateValue FROM MyDateFunction(#MyParameter));
You can also use a scalar valued function:
CREATE FUNCTION TestDateFunction()
RETURNS datetime
BEGIN
RETURN (SELECT GetDate());
END
Then you can simply do
Declare #MyDate datetime;
SET #MyDate = (Select dbo.TestDateFunction());
SELECT #MyDate;
Here is solution for dynamic queries.
For example if you have more tables with different suffix:
dbo.SOMETHINGTABLE_ONE, dbo.SOMETHINGTABLE_TWO
Code:
DECLARE #INDEX AS NVARCHAR(20)
DECLARE #CheckVALUE AS NVARCHAR(max) = 'SELECT COUNT(SOMETHING) FROM
dbo.SOMETHINGTABLE_'+#INDEX+''
DECLARE #tempTable Table (TempVALUE int)
DECLARE #RESULTVAL INT
INSERT INTO #tempTable
EXEC sp_executesql #CheckVALUE
SET #RESULTVAL = (SELECT * FROM #tempTable)
DELETE #tempTable
SELECT #RESULTVAL
You can use a Table Variable for that
Code:
DECLARE #PreviousBusinessDay DATETIME
DECLARE #Temp TABLE(BusinessDay DATETIME)
INSERT INTO #Temp EXEC dbo.up_GetBusinessDay #Date, -1
SET #PreviousBusinessDay = (SELECT * FROM #Temp)
SELECT #PreviousBusinessDay
https://www.sqlservertutorial.net/sql-server-user-defined-functions/sql-server-table-variables/