Create a scheduler to re-compile stored procedure - sql

I am facing an issue on SQL Server in which my stored procedure becomes slow after couple of days.
I came to know that recompiling the stored procedure will work. However, I do not want to recompile that stored procedure every time it gets called.
Is it a good way to create a job on SQL Server which will execute following statement?
EXEC sp_recompile N'SP_NAME';
Will this cause any performance issues?
Below is my SP Structure.
#START_VALUE int=null,
#END_VALUE int=null`enter code here`
#UID NVARCHAR(MAX)=null,
AS
BEGIN
SELECT dbo.TABLE1.ID,
ROW_NUMBER() OVER (ORDER BY TABLE1.UPDATED_ON desc) AS RN,
CONVERT(VARCHAR(10), dbo.TABLE1.DATE, 101) AS TDATE,
CATEGORY
=(
SELECT TOP 1 COLUMN1 FROM TABLE5 CT1 WHERE
TABLE1.CATEGORY = CT1.CATEGORY_ID
)
,
TYPETEXT
=(
SELECT TOP 1 COLUMN1 FROM TABLE6 CT1 WHERE
TABLE1.TYPE = CT1.TYPE_ID
),
IMAGE = STUFF(( SELECT DISTINCT ',' + CAST(pm.C1 AS varchar(12))
FROM TABLE2 pm WHERE
pm.ID = TABLE1.ID AND pm.C1 IS NOT NULL AND pm.C1 <> ''
FOR XML PATH('')), 1, 1, '' )INTO #tempRecords
FROM dbo.TABLE1
WHERE
((#UID is null OR dbo.TABLE1.ID = #UID )
ORDER BY TABLE1.UPDATED DESC
SELECT #count = COUNT(*) FROM #tempRecords;
SELECT *,CONVERT([int],#count) AS 'TOTAL_RECORDS' FROM #tempRecords
WHERE #tempRecords.RN BETWEEN CONVERT([bigint],#START_VALUE) AND CONVERT([bigint],#END_VALUE)
END
GO

At very first: Replace Sub-Queries in the SELECT statement by Sub-Queries in FROM clause.
Here are some options to resolve 'slowing':
1. If procedure is executed not frequently try to use RECOMPILE option.
2. Look at statistics. Maybe it would be easier to UPDATE STATISTICS then recompile SP.
3. If statistics are the problem, you may try to use filtered statistics.
4. If none of these options works, you can just delete query plan for that procedure in automated manner from Query Cache and procedure will be daily recompiled itself.
However, at first, rebuild the main query.

Related

Use column if it exists, another if doesn't in SQL Server

I have a number of SQL Server databases (different versions from 2012 to 2019). The schema in each one is very similar but not exactly the same. For example, there's table ORDERS, which has about 50 columns - and one column is called differently in two different databases:
in DB1: select p_user from orders
in DB2: select userpk from orders
Note that I showed two databases above, but there are actually more than 20 - some are DB1 type, the others are DB2 type
I can't do much about these differences - they are historic - and changing the schema to match is not an option.
I want to be able to run the same SQL statement against all of these databases at once. I'd like to write the query in such a way that it would use one column if it exists and another if it doesn't. For example:
select
case
when COL_LENGTH('orders', 'p_user') IS NOT NULL
then
orders.p_user
else
orders.userpk
end
from orders
This unfortunately doesn't work, as SQL server seems to try to evaluate both results regardless of whether the condition is true or false. The same thing happens if I use IIF function.
If I simply run
select
case
when COL_LENGTH('orders', 'p_user') IS NOT NULL
then
'orders.p_user'
else
'orders.userpk'
end
then I do get the correct string, which means my condition is correct.
How can I formulate the SQL statement to use one or the other column based on whether the first one exists?
If you can't change anything then your best (and maybe only) option is to use dynamic SQL. A query will only compile if all parts can be resolved at compile time (before anything runs) - which is why e.g. this will not compile:
IF COL_LENGTH('orders', 'p_user') IS NOT NULL THEN
select p_user from orders
ELSE
select userpk as p_user from orders
END
But this will work:
DECLARE #SQL NVARCHAR(MAX)
IF COL_LENGTH('orders', 'p_user') IS NOT NULL THEN
SET #SQL = 'select p_user from orders'
ELSE
SET #SQL = 'select userpk as p_user from orders'
END
EXEC (#SQL)
Fix your tables by adding a computed column:
alter table db1..orders
add statuspk as (p_status);
(Or choose the other name.)
Then, your queries will just work without adding unnecessary complication to queries.
create table orders1(colA int, colB int, colABC int);
insert into orders1 values(1, 2, 3);
go
create table orders2(colA int, colB int, colKLM int);
insert into orders2 values(5, 6, 7);
go
create table orders3(colA int, colB int, colXYZ int);
insert into orders3 values(10, 11, 12);
go
select colA, colB, vcolname as [ABC_KLM_XYZ]
from
(
select *,
(select o.* for xml path(''), elements, type).query('
/*[local-name() = ("colABC", "colKLM", "colXYZ")][1]
').value('.', 'int') as vcolname
from orders1 as o
) as src;
select colA, colB, vcolname as [ABC_KLM_XYZ]
from
(
select *,
(select o.* for xml path(''), elements, type).query('
/*[local-name() = ("colABC", "colKLM", "colXYZ")][1]
').value('.', 'int') as vcolname
from orders2 as o
) as src;
select colA, colB, vcolname as [ABC_KLM_XYZ]
from
(
select *,
(select o.* for xml path(''), elements, type).query('
/*[local-name() = ("colABC", "colKLM", "colXYZ")][1]
').value('.', 'int') as vcolname
from orders3 as o
) as src;
go
drop table orders1
drop table orders2
drop table orders3
go
I ended up using dynamic sql like so:
declare #query nvarchar(1000)
set #query = concat(
'select count(distinct ', (case when COL_LENGTH('orders', 'p_user') IS NOT NULL then 'orders.p_user' else 'orders.userpk' end), ')
from orders'
);
execute sp_executesql #query
This solved my immediate issue.

Stored procedure becomes slow every couple of days

I am facing an issue on SQL Server in which my stored procedure becomes slow after couple of days.
Below is the sample of my stored procedure.
Could this be a caching issue on the server side? Can I increase the server's cache size to resolve the problem?
Normally the stored procedure returns data in one second.
#START_VALUE int=null,
#END_VALUE int=null
#UID NVARCHAR(MAX)=null,
AS
BEGIN
SELECT
dbo.TABLE1.ID,
ROW_NUMBER() OVER (ORDER BY TABLE1.UPDATED_ON desc) AS RN,
CONVERT(VARCHAR(10), dbo.TABLE1.DATE, 101) AS TDATE,
CATEGORY = (
SELECT TOP 1 COLUMN1
FROM TABLE5 CT1
WHERE TABLE1.CATEGORY = CT1.CATEGORY_ID
),
TYPETEXT = (
SELECT TOP 1 COLUMN1
FROM TABLE6 CT1
WHERE TABLE1.TYPE = CT1.TYPE_ID
),
IMAGE = STUFF(( SELECT DISTINCT ',' + CAST(pm.C1 AS varchar(12))
FROM TABLE2 pm
WHERE pm.ID = TABLE1.ID AND pm.C1 IS NOT NULL AND pm.C1 <> ''
FOR XML PATH('')),
1, 1, '' ) INTO #tempRecords
FROM dbo.TABLE1
WHERE ((#UID is null OR dbo.TABLE1.ID = #UID )
ORDER BY TABLE1.UPDATED DESC
SELECT #count = COUNT(*) FROM #tempRecords;
SELECT *, CONVERT([int],#count) AS 'TOTAL_RECORDS'
FROM #tempRecords
WHERE #tempRecords.RN BETWEEN CONVERT([bigint], #START_VALUE) AND CONVERT([bigint], #END_VALUE)
END
GO
'
A few performance tips:
1) #UID is null OR dbo.TABLE1.ID = #UID --> this is bad because you'll have one execution plan when UID is null and when it's not. Build a dynamic sql query and you'll get 2 execution plans.
2) Update stats in a maintenance plan.
3) Check index fragmentation.
4) Try to do the same thing without using a temp table.
5) Try to avoid castings.

How to execute SQL statements saved in a table with T-SQL

Is it possible to execute a SQL statement Stored in a Table, with T-SQL?
DECLARE #Query text
SET #Query = (Select Query FROM SCM.dbo.CustomQuery)
The statements that are stored in the table are ad-hoc statements which could be SELECT TOP 100 * FROM ATable to more complex statements:
Select
J.JobName As Job,
JD.JobDetailJobStart AS StartDate,
JD.JobDetailJobEnd AS EndDate,
(
SELECT (DATEDIFF(dd, JD.JobDetailJobStart, JD.JobDetailJobEnd) + 1) -(DATEDIFF(wk, JD.JobDetailJobStart, JD.JobDetailJobEnd) * 2) -(CASE WHEN DATENAME(dw, JD.JobDetailJobStart) = 'Sunday' THEN -1 ELSE 0 END) -(CASE WHEN DATENAME(dw, JD.JobDetailJobEnd) = 'Saturday' THEN -1 ELSE 0 END)
) AS NumberOfWorkingDays,
JD.JobDetailDailyTarget AS DailyTarget,
JD.JobDetailWeeklyTarget AS WeeklyTarget,
JD.JobDetailRequiredQTY AS RequiredQuantity,
(
Select SUM(sJL.JobLabourQuantityEmployees) From JobLabour sJL
) AS NumberOfEmployees,
(
Select
SUM((sEM.EmployeeDesignationDefaultRate * sJL.JobLabourQuantityEmployees)*8)*(SELECT (DATEDIFF(dd, JD.JobDetailJobStart, JD.JobDetailJobEnd) + 1) -(DATEDIFF(wk, JD.JobDetailJobStart, JD.JobDetailJobEnd) * 2) -(CASE WHEN DATENAME(dw, JD.JobDetailJobStart) = 'Sunday' THEN -1 ELSE 0 END) -(CASE WHEN DATENAME(dw, JD.JobDetailJobEnd) = 'Saturday' THEN -1 ELSE 0 END))
from EmployeeDesignation sEM
Inner join JobLabour sJL on sJL.EmployeeDesignationID = sEM.EmployeeDesignationID
) AS FullEmployeeRate
from Job J
Inner Join JobDetail JD on JD.JobID = J.JobID
Inner Join JobLabour JL on JL.JobID = J.JobID
WHERE J.JobActive = 0
I want to execute the #Query Variable that I declared from T-SQL. Is this possible? (I am running a MSSQL 2005 enviroment)
You can use
EXECUTE sp_executesql #Query
to run your T-SQL
Here's a link to the MS docn for SQL Server 2005
http://msdn.microsoft.com/en-us/library/ms188001%28v=sql.90%29.aspx
The previous answer allows you to run one statement, and is valid. The question was on how to run SQL Statements stored in a table, which I took as more than one statement being executed. For this extra step, there is a while loop involved to iterate through each statement that need to be run.
-- Author: Chad Slagle
DECLARE #Table table (RID BIGINT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
SQLText NVARCHAR(MAX) )
DECLARE #StatementMax INT
,#statementMin INT
,#isTest TINYINT = 1
,#SQLStatement NVARCHAR(MAX)
-- Insert SQL Into Temp Table
INSERT INTO #table (SQLText)
VALUES ('SELECT ##Version');
INSERT INTO #table (SQLText)
VALUES ('SELECT SERVERPROPERTY(''ProductVersion'')')
-- Get your Iterator Values
SELECT #statementMAX = MAX(RID), #statementMIN = MIN(RID) FROM #table
IF #isTest = 1 BEGIN SELECT *, #statementMax AS MaxVal, #StatementMin AS MinVal FROM #Table END
-- Start the Loop
WHILE #StatementMax >= #statementMin
BEGIN
SELECT #SQLStatement = SQLText FROM #table WHERE RID = #statementMin -- Get the SQL from the table
IF #isTest = 1 BEGIN SELECT 'I am executing: ' + #SQLStatement AS theSqlBeingRun, GETDATE(), #statementMin, #StatementMax END
ELSE
BEGIN
EXECUTE sp_ExecuteSQL #SQLStatement -- Execute the SQL
END
DELETE FROM #table WHERE RID = #statementMin -- Delete the statement just run from the table
SELECT #statementMIN = MIN(RID) FROM #Table -- Update to the next RID
IF #isTest = 1 BEGIN SELECT * FROM #table END
END
In Summary, I created a temp table and put some SQL in it, using a IDENTITY (RID) field to provide an iterator for the while loop. Then ran the while loop. In the example, you should return two views of your SQL Version. I built this on 2k8, and I hope it helps someone out of a jam one day..
We use a much simpler approach. Store the scripts (raw sql or stored procedure calls) in a table with a column containing an identifying code for said script. Use placeholders in your script for parameters. Any scripts that are used a lot can be "keyed" in your app or web config file. If scripts need to be executed in a specific order, put an ordinal column in the table. The actual "script" can then be pulled into a c# list or array, passed to a database class library and executed accordingly. This gives you dynamic control over your SQL and allows you to make changes on the database side for said scripts without recompiling your main application.
TRUNCATE TABLE AllTableUnion
DECLARE #Query2 Nvarchar(MAX)
SET #Query2='SELECT * FROM #UnionString t1)A'
INSERT INTO AllTableUnion
EXEC(#Query2)
DECLARE #Query4 Nvarchar(MAX)=(SELECT Query FROM AllTableUnion)
EXECUTE sp_ExecuteSQL #Query4
I have a similar situation I just can't get to work. I'd like to create a table, where we have a customer number, and the business rules used to determine codes for a variety of items. The table psudo looks like this:
| CustomerNumber | RuleName | Rule |
----------------------------------------------------------------------
| 12345 |ShippingCharged | iif(id.ItemID in (1,2,3,4,5,6) AND
cod.Code IN (5,6,7,8),1,0)
| 99999 |ShippingCharged | iif(id.ItemID in (1,2,3,7,9,10) AND
cod.Code NOT IN (5,7,8),1,0)
I want to run a SELECT in the form:
SELECT CustomerNumber, RuleName, Rule as Value
FROM CustomerRules cr
JOIN CustomerData cd
ON cd.CustomerNumber = cd.CustomerNumber
JOIN ItemsData id
ON cd.ItemID = id.ItemID
JOIN CodesData cod
ON cd.Code = cod.Code
WHERE cr.RuleName = 'ShippingCharged'
To return The Customer Number, the name of the Rule used and the calculated value of the IIF statement. I am getting the text of the Rule calculation, rather than the calculated value.
I've tried various forms of DSQL and TSQL but can't seem to get the column to be treated as a function, just a text value from the table.
Any ideas?

How to optimize stored procedures?

Following is my Stored Proc.
ALTER PROCEDURE [GetHomePageObjectPageWise]
#PageIndex INT = 1
,#PageSize INT = 10
,#PageCount INT OUTPUT
,#AccountID INT
,#Interests Varchar(3000)
AS
BEGIN
SET NOCOUNT ON;
SELECT StoryID
, AlbumID
, StoryTitle
, CAST(NULL as varchar) AS AlbumName
, (SELECT URL FROM AlbumPictures WHERE (AlbumID = Stories.AlbumID) AND (AlbumCover = 'True')) AS AlbumCover
, Votes
, CAST(NULL as Int) AS PictureId
, 'stories' AS tableName
, (SELECT CASE WHEN EXISTS (
SELECT NestedStories.StoryID FROM NestedStories WHERE (StoryID = Stories.StoryID) AND (AccountID=#AccountID)
)
THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT) END) AS Flag
, (SELECT UserName FROM UserAccounts WHERE Stories.AccountID=UserAccounts.AccountID) AS Username
INTO #Results1
FROM Stories WHERE FREETEXT(Stories.Tags,#Interests) AND AccountID <> #AccountID AND IsActive='True' AND Abused < 10
I have 7 more SELECT Statements (not included in the question for brevity) in the Stored Proc similar to SELECT StoryID statement, which i UNION ALL like this
SELECT * INTO #Results9 FROM #Results1
UNION ALL
SELECT * FROM #Results2
UNION ALL
SELECT * FROM #Results3
UNION ALL
SELECT * FROM #Results4
UNION ALL
SELECT * FROM #Results5
UNION ALL
SELECT * FROM #Results6
UNION ALL
SELECT * FROM #Results7
UNION ALL
SELECT * FROM #Results8
SELECT ROW_NUMBER() OVER
(
ORDER BY [tableName] DESC
)AS RowNumber
, * INTO #Results
FROM #Results9
DECLARE #RecordCount INT
SELECT #RecordCount = COUNT(*) FROM #Results
SET #PageCount = CEILING(CAST(#RecordCount AS DECIMAL(10, 2)) / CAST(#PageSize AS DECIMAL(10, 2)))
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
DROP TABLE #Results
DROP TABLE #Results1
DROP TABLE #Results2
DROP TABLE #Results3
DROP TABLE #Results4
END
This takes around 6 seconds to return the result. How can i improve this stored proc? I have very little knowledge about stored procedures.
Raise a nonclustered index on columns in where clause, IsActive, AccountID and Abused.
Well, you can only optimize it by getting rid of the temporary tables. Your approach sucks not because it is a stored procedure (so the SP part is simply totally irrelevant) but because you do a lot of temporary table stuff that forces linear execution and makes it hard for the query optimizer to find a better day to go forward.
In this particular case, it may be that your db design may be horrifically bad (why #result 1 to #result 8 to start with) and then you have tons of "copy into temp table" on every stored procedure.
Query Optimization in SQL works "statement by statement" and execution is never paralleled between statements - so the temp table stuff really gets into your way here. Get rid of the temp tables.
Never ever use directly SELECT * INTO #temp
INSTEAD
Always create #temp tables then INSERT INTO #temp
this will reduce query execution time by 70%
Though it might be frustration to create #temp table with exact structures,
so here is a short cut for that:This will be once performed
CREATE dbo.tableName by using SELECT * INTO tableName from Your calling query
then
sp_help TableName will provide structures.
Then create #temp table in Store Procedure.
I have optimized query for one of our client which was taking 45 minutes to execute, just replaced with this logic It worked !!!
Now it takes 5 Minutes !!

TSQL Number of reads significantly different after query and stored procedure execution

After query optimization I got results that are ok and I wanted to alter stored procedure, but got much worst results after SP execution, than it was after query execution!
Firstly, I think at number of reads. What can be reason for so different results?
Query is identical like in SP, only difference is that in query I declared parameter, but in SP that was input parameter. Value that is set to parameter is also same. To avoid 'recorded data' first I recompiled SP and after that done DROP and CREATE, but results were also much different.
Query is like this (table and column names are changed because of simplification, and number of columns is reduced):
DECLARE #Var1 varchar(20)
SET #Var1 = #Var1 + '%'
DECLARE #Var2 TIMESTAMP
SELECT #Var2 = CONVERT(TIMESTAMP, ID, 0)
FROM
X_TIMESTAMPS (NOLOCK)
WHERE
TABLE = 'T1'
declare #Var3 varbinary(8)
SELECT #Var3 = max(IdTimeStamps)
FROM
T1 (NOLOCK)
SELECT o.c1
, o.c2
, o.c3
, v.c4
, v.c5
, p.c6
, p.c7
, va.c8
, isnull(s.c9, '') AS c9
, CASE o.c10
WHEN 1 THEN
0
ELSE
1
END c10
, o.c11
FROM
T1 o (NOLOCK)
JOIN T2 p (NOLOCK)
ON o.c1 = p.c12
JOIN T3 i (NOLOCK)
ON (o.c13 = i.c14)
JOIN T4 v (NOLOCK)
ON (v.c4 = i.c15)
LEFT JOIN T5 s (NOLOCK)
ON (o.c16 = s.c17)
JOIN T6 va (NOLOCK)
ON o.c11 = va.c18
WHERE
o.c1 LIKE #Var1
AND o.c2 > #Var2
And procedure is like this:
CREATE PROCEDURE [dbo].[SP1] #Var1 varchar(20) =''
WITH RECOMPILE
AS
BEGIN
PREVIOUS QUERY WITHOUT DECLARATION FOR #Var1
END
TnX in advance!
Nemanja
It's because different execution plans are used for query with constants and sp with parameeters. You can try a few tricks
Create inline table function and try it
create function sf_test
(
#param1 int
)
returns table
as
return
your query using #in_param1
or
declare additional parameters in your procedure like this
create procedure sp_test
(
#param1 int
)
as
begin
declare #in_param1 int
select #in_param1 = #param1
your query using #in_param1
end
you can also try using option with recompile in your procedure, or use dynamic SQL
This is almost certainly a parameter sniffing issue. Personally, I liked the dummy variable option to work around this issue and (only when I run into this problem) create variable(s) that are set to the value of the incoming parameter(s).