How to cache stored procedure results to retrieve the results faster? - sql-server

I have a stored procedure which takes a lot of time (around 5 minutes) to execute. This stored procedure fills up a table. Now I am retrieving the data from that table. I have created a job to execute this stored procedure every 15 minutes. But while the stored procedure is in execution, my table is empty and the front end shows no results at that time. My requirement is to show data at the front end side all the time.
Is there a way to cache the stored procedure results and use that result while the stored procedure executes?
Here is my stored procedure,
BEGIN
declare #day datetime
declare #maxdate datetime
set #maxdate = getdate()
set #day = Convert(Varchar(10),DATEADD(week, DATEDIFF(day, 0, getdate())/7, 0),110)
truncate table tblOpenTicketsPerDay
while #day <= #maxdate
begin
insert into tblOpenTicketsPerDay
select convert(varchar(20),datename(dw,#day)) day_name, count(*) Open_Tickets from
(select [status], createdate, closedate
FROM OPENROWSET('MSDASQL','DSN=SQLite','Select * from tickets') AS a
where createdate <= #day
except
select [status], createdate, closedate
FROM OPENROWSET('MSDASQL','DSN=SQLite','Select * from tickets') AS a
where closedate <= #day and [status] = 'closed') x
set #day = #day + 1
end
END
Any ideas will be helpful.
Thanks.

If I have understood correct then your main concern is: your stored procedure empties the table and then fills it up and since it takes time, your application have no data show.
In that case, you can have a secondary/auxiliary clone table; say tblOpenTicketsPerDay_clone and have your stored procedure fill that table instead like
insert into tblOpenTicketsPerDay_clone
select convert(varchar(20),datename(dw,#day)) day_name,
count(*) Open_Tickets from
That way your application will always have data to display since main table has the data. Once, the clone table is done filling up then transfer the same data to main table saying
delete from tblOpenTicketsPerDay;
insert into tblOpenTicketsPerDay
select * from tblOpenTicketsPerDay_clone;

No, but the problem is not caching, it isa totally bad approach to generate the data.
Generate new data into a temporary table, then MERGE The results (using the merge keyword) into the original table.
No sense in deleting the data first. That is a terrible design approach.

Related

Replace Getdate() with stored procedure

We have a job with couple of steps and almost all of the steps use getdate(), but instead we want to get the date from a specific table and column. The table includes only two columns status as ready (doesn't change) and statusdate (dynamic). The plan is to create a stored procedure and replace the getdate() with that stored procedure.
How do I write the stored procedure? How do I declare a variable?
CREATE PROCEDURE SP_DATE
#StatusDate DATETIME
AS
BEGIN
SELECT StatusDate
FROM [DB_Name].[dbo].[Table_Name]
WHERE status = ready
END
Thank you!
Your jobs use getdate() function therefore in order to replace it with custom programmatic object you should use function as well and not a stored procedure. With a function like this
CREATE FUNCTION dbo.StatusDate ()
RETURNS DATETIME
AS
BEGIN
RETURN (SELECT
StatusDate
FROM Table_Name
WHERE status = 'ready')
END
you can replace getdate directly
SELECT
id
FROM Your_Job_List yjl
WHERE yjl.aDate < dbo.StatusDate()--getdate()
yet there are some questions to the design. One biggest single task of RDBMS is joining tables and perhaps a query similar to next one might be better
SELECT
id
FROM Your_Job_List yjl
,Table_Name tn
WHERE yjl.aDate < tn.StatusDate
AND tn.status = 'ready'
CREATE PROCEDURE spRunNextDate
AS
BEGIN
--SET NOCOUNT ON
declare #runDate datetime
select #runDate = MIN(StatusDate)
from [DB_Name].[dbo].[Table_Name]
where [status] = 'ready'
IF (#runDate IS NULL)
BEGIN
PRINT 'NO Dates in Ready State.'
RETURN 0
END
PRINT 'Will Run Date of ' + CAST(#runDate as varchar(20))
-- Notice the MIN or MAX usage above to get only one date per "run"
END
GO
There are huge holes and questions raised in my presumptuous sp above, but it might get you to thinking about why your question implies that there is no parameter. You are going to need a way to mark the day "done" or else it will be run over and over.

Concatenating Time and DateTime in where clause

I am trying to combine a SmallDateTime field and a Time value (result of a scalar-valued function) into a DateTime and I keep getting the following error:
Conversion failed when converting date and/or time from character
string.
Here are the variables used throughout:
DECLARE #STARTDATETIME AS DATETIME
DECLARE #ENDDATETIME AS DATETIME
SELECT #STARTDATETIME = '8/29/2016 12:00:00'
SELECT #ENDDATETIME = '8/30/2016 12:00:00'
Column definitions:
FT_START_DATE SmallDateTime
FT_END_DATE SmallDateTime
FT_START_TIME Int
FT_END_TIME Int
The date fields do not contain timestamps. The time fields are basically 24 hour time without the colon dividers. (Example: 142350 = 14:23:50)
Here's the function that is called in my queries:
USE [PWIN171]
GO
/****** Object: UserDefinedFunction [dbo].[dbo.IPC_Convert_Time] Script Date: 9/13/2016 4:50:49 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[dbo.IPC_Convert_Time]
(
#time int
)
RETURNS time
AS
BEGIN
DECLARE #Result time
SELECT #Result = CONVERT(time
, STUFF(
STUFF(
RIGHT('000000' + CONVERT(varchar(6), #time), 6)
, 5, 0, ':')
, 3, 0, ':')
)
RETURN #Result
END
Example 1 - Fails:
This is what I'm after in general.
SELECT * FROM FT WITH (NOLOCK)
WHERE
CAST(FT_END_DATE AS DATETIME) + DBO.[dbo.IPC_Convert_Time](FT_END_TIME) BETWEEN #STARTDATETIME AND #ENDDATETIME;
Example 2 - Works:
This one runs, but it won't get records from 8/29 because the end dates will be before 12:00:00 on 8/29.
SELECT * FROM FT WITH (NOLOCK)
WHERE
FT_END_DATE BETWEEN #STARTDATETIME AND #ENDDATETIME
AND CAST(FT_END_DATE AS DATETIME) + DBO.[dbo.IPC_Convert_Time](FT_END_TIME) BETWEEN #STARTDATETIME AND #ENDDATETIME;
I suppose I could do one where I split apart my paramters and check that the end time is between the time portion of the parameters as well, but that seems to be a step in the wrong direction. The error seems to only appear when there is no other usage of FT_START_DATE or FT_END_DATE in the where clause.
The time converting function works fine in every scenario I have created. I have even tried Example 2 with parameters that would give it 100% overlap with the data covered by Example 1 in case there was bad data causing the error, but it runs fine.
I also don't know exactly where the error is occurring, because it only references the line the select statement begins on, and not the actual location in the code.
Why does it behave like this?
UPDATE:
TIMEFROMPARTS is not available because this is on SQL Server 2008
If I understand this correctly, this can be done much simpler:
Try this:
DECLARE #d DATE=GETDATE();
DECLARE #t TIME=GETDATE();
SELECT #d;
SELECT #t;
SELECT CAST(#d AS datetime)+CAST(#t AS datetime);
A pure date and a pure time can simply be added to combine them...
UPDATE Read your question again...
Try this
SELECT FT_END_DATE
,FT_END_TIME
,CAST(FT_END_DATE AS DATETIME) + DBO.[dbo.IPC_Convert_Time](FT_END_TIME) AS CombinedTime
,*
FROM FT
to see if your attempt is doing the right thing.
If yes, it might help to create a CTE and do the filter on the named column.
Sometimes the engine does not work the order you would expect this.
As CTEs are fully inlined it is quite possible, that this will not help...
SQL Server is well knwon for bringing up such errors, because a type check happens before a conversion took place...
It might be an idea to use the given SELECT with INTO #tbl to push the result set into a new table and do your logic from there...

Force recalculation of GetDate() for each row

When running this command in SQL Server Management Studio
UPDATE LogChange
SET Created = GETDATE()
WHERE id > 6000
something strange happens.. :)
All rows get the same value in the Created column.
How do I "force" SQL Server to recalculate the GetDate for every row?
A cursor solved it
declare #id int;
Declare toLoop cursor for
select Id from LogChange where id > 6000
OPEN toLoop
FETCH NEXT FROM toLoop INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
Update LogChange set Created = GETDATE() where id = #id
FETCH NEXT FROM toLoop INTO #id
END
close toLoop
DEALLOCATE toLoop
I have found the following “getdate()” pattern extremely useful over the years. Especially when updating multiple tables in the same transaction. Having the time values exactly match had more benefit to me then being accurate to the millisecond on individual rows.
declare #dtNow datetime
SET #dtNow = GETDATE()
UPDATE LogChange
SET Created = #dtNow
WHERE id > 6000
I don't think there is a way to do them in a single query because the operations in the same clause of a query are evaluated all-at-once.
If you want to guarantee that separate values are applied to each row, write a query that definitely does that:
declare #dt datetime
set #dt = GETDATE()
;With Diffs as (
select
*,
ROW_NUMBER() OVER (ORDER BY id) * 10 as Offset
from
LogChange
WHERE id > 6000
)
UPDATE Diffs
SET Created = DATEADD(millisecond,Offset,#dt)
We know that the above will generate separate offsets for each row, and that we're definitely adding those offsets to a fixed value. It has often been observed that GETDATE() will return the same value for all rows but I cannot find any specific documentation that guarantees this, which is why I've also introduced a #dt variable so that I know it's only assigned once.
You can create a scalar function like this one:
CREATE FUNCTION dbo.sf_GetDate(#num int)
RETURNS DateTime
AS
BEGIN
RETURN GETDATE()
END
GO
Then use it like this:
UPDATE LogChange
SET Created = dbo.GetDate(Id)
WHERE id > 6000

how to create a table name dynamically based on the week

I am trying to create a backup table called pipelinebackupWk##, where ## denotes the current week looked up from a fiscal calendar table. For example: this week the backup table will be called pipelinebackup10, next week it will be called pipelinebackup12. I need the weeks to change automatically so that my SSIS package runs without me having to change it manually every week.
I am currently using this:
select * into (
select 'Pipelinebackup'+'WK'+ RIGHT(CAST(WEEK_OF_QUARTER AS VARCHAR(2)),2)
From [dbo].[Fiscal_calendar]
Where ACTUAL_DATE = Convert(Datetime, Convert( nvarchar(20), getdate()-5,101))
)
from table12
You can create the table with a stored procedure. there you write dynamically the create table string and execute it. the double exec(sp_execute) maybe not necessary, if you stay in one DB. something like this works for me, creating dynamic tables:
create proc [make_new_tab]
as begin
declare #wk as NVARCHAR(2);
declare #SQL as NVARCHAR(MAX);
select #wk = CAST(WEEK_OF_QUARTER AS NVARCHAR(2)) from
[dbo].[Fiscal_calendar]
Where ACTUAL_DATE = (
select Convert( Datetime, Convert( nvarchar(20), getdate()-5,101)))
from table12)
set #SQL = N'exec YOUR_DB..sp_executesql N''CREATE TABLE PipelinebackupKW'+ #wk + ' (Col1 INT NOT NULL, Col2 INT NOT NUL) ON PRIMARY ''
EXEC(#SQL)
end
If you wanted to do the table creation in SSIS, you could:
Use a Execute SQL Task to run the look up week number in fiscal year table query, storing the result in a variable (called, say, WeekNumber).
Use an Expression Task to build a CREATE TABLE statement that is stored in another variable. This expression should reference your #[User::WeekNumber] variable where you need the week number inserted. For example: #[User::CreateTableQuery] = "CREATE TABLE pipelinebackupWk" + #[User::WeekNumber] + " (c1 int PRIMARY KEY, ....."
Use a second Execute SQL Task to run the query defined in your create table query variable.

Exec Base procedure in SSRS with Parameter

I have an SSRS report that is calling on one Stored Procedure to Execute, which updates a table in the database. Then I run a report on the data in the table utilizing another stored procedure.
What I would like to do be able to pass a parameter from the report to the Stored Procedure. Here is a sample of what I am doing.
StoredProcedure
Select
HospCode
TxnCode
TxnAmt
into
TxnData
From
Data
Where
TxnDate Between #Start and #End
Report-
Exec StoredProcedure
Select
HospCode
Sum(TxnAmt) Over(Partition By HospCode)
From
TxnData
Order By HospCode
Yes the Parameters are declared in the SP. I would like the User running the report to be able to choose the date range, then have that passed onto the Stored Procedure.
I can do this with individual Procedures very easily but The Stored Procedure is a building block that quite a few reports will use- so rather than have to copy and paste or retype, I would just like to call it up.
Is this possible.
Thanks,
Scott
Here is the logic but ensure the stored procedures are maintained if changes are required.
In SSRS you would pass Start date and End Date to base stored procedure which will be passed to sub stored procedure.
Test data generated for testing ..
create table Data (HospCode int, TxnCode int, TxnAmt money, TxnDate date)
insert into Data (HospCode, TxnCode, TxnAmt, TxnDate)
select 1, 1, 10, '2014-09-11'
union all
select 1, 1, 10, '2014-09-12'
union all
select 1, 2, 10, '2014-09-11'
union all
select 2, 2, 10, '2014-09-11'
Base Stored procedure: You will need to create a temp table to return data from the sub stored procedure. Ensure the temp table must have the same field and data types returned from the sub stored procedure.
CREATE PROC dbo.someSPbase(#StartDate DATE, #EndDate DATE)
AS
BEGIN
IF OBJECT_ID('TempDB.dbo.#TempData') IS NOT NULL
DROP TABLE #TempData;
CREATE TABLE #TempData(
HospCode int
, TxnCode int
, TxnAmt money
);
INSERT INTO #TempData(HospCode, TxnCode, TxnAmt)
EXEC dbo.someSPSub1 #StartDate, #EndDate
SELECT HospCode
, SUM(TxnAmt) OVER (Partition By HospCode) AS TxnAmtTotal
FROM #TempData;
END
Sub stored procedure called from base stored procedure above:
CREATE PROC dbo.someSPSub1(#StartDate DATE, #EndDate DATE)
AS
BEGIN
Select
HospCode
,TxnCode
,TxnAmt
From Data
Where TxnDate Between #StartDate and #EndDate
END

Resources