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.
Related
I have a very slow procedule in mssql 2016, I dont think it should be that slow.
## the code ##
select #count = count(UID) from TABLE1 where IsProcess = 0
while #count > 0
begin
Declare #Name nvarchar(100)
Declare #UID int
select Top 1 #UID = UID, #Name = Name from Table1 where IsProcess = 0
set #UID2 = ISNULL((select TOP 1 UID from TABLE2 where Name like N'%' + #Name + N'%'), 0)
if #UID2 = 0
begin
insert into table2 (Name) values(#Name)
set #UID2 = ##IDENTITY
end
insert into tablerelation (UID1, UID2) values(#UID, #UID2)
update TABLE1 set IsProcess = 1 where UID = #UID
select #count = count(UID) from TABLE1 where IsProcess = 0
end
there are about 5 million rows in table1
the server cpu was less than 10%
memory arround 4G (and 8G on server)
no other application run on server
it is now about 3 to 4 row per second
I thought it should be 200 rows and up per second at least.
I had used cursor , it was the same
please help.
thanks a lot.
modified by code, this is my first post here, thanks for your patient
Why are you using a loop instead of set-based operations? From what I can tell, you want:
insert into tablerelation (uid1, uid2)
select t1.UID, t2.UID
from Table1 t1 cross apply
(select top 1 t2.*
from table2 t2
where t2.name like N'%' + t1.Name + N'%'
) t2
where IsProcess = 0;
This should be a bit faster, but it won't be an incredible speed-up. The speed gain is from the reduction in database operations.
However, the real problem is the join using like. With wildcards, it is not very easy to speed it up.
I am using MSSQL. I have a stored procedure which works fine for couple of days and later on it becomes slow. I came to know that Parameter Sniffing will work for it. How ever after implementing it it became slow for ever. I also tried to Recompiling job. I faced the same slowness issue immediately.
Can some one please help me with this ?
Below is the structure of my Stored Procedure.
#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
To make the query optimized for parameter sniffing, declare dummy variables and use them in your query, as opposed to using the original parameters as such.
CREATE PROCEDURE test_proc
#START_VALUE INT=NULL,
#END_VALUE INT=NULL,
#UID NVARCHAR(max)=NULL
as
BEGIN
DECLARE #START_VALUE_SNIFF INT=NULL,
#END_VALUE_SNIFF INT=NULL,
#UID_SNIFF NVARCHAR(max)=NULL
SET #START_VALUE_SNIFF = #START_VALUE
SET #END_VALUE_SNIFF = #END_VALUE
SET #UID_SNIFF = #UID
select * from
FROM dbo.TABLE1
WHERE ((#UID_SNIFF is null OR dbo.TABLE1.ID = #UID_SNIFF )
ORDER BY TABLE1.UPDATED DESC
END
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.
I've got a SQL Server db with quite a few dupes in it. Removing the dupes manually is just not going to be fun, so I was wondering if there is any sort of sql programming or scripting I can do to automate it.
Below is my query that returns the ID and the Code of the duplicates.
select a.ID, a.Code
from Table1 a
inner join (
SELECT Code
FROM Table1 GROUP BY Code HAVING COUNT(Code)>1)
x on x.Code= a.Code
I'll get a return like this, for example:
5163 51727
5164 51727
5165 51727
5166 51728
5167 51728
5168 51728
This snippet shows three returns for each ID/Code (so a primary "good" record and two dupes). However this isnt always the case. There can be up to [n] dupes, although 2-3 seems to be the norm.
I just want to somehow loop through this result set and delete everything but one record. THE RECORDS TO DELETE ARE ARBITRARY, as any of them can be "kept".
You can use row_number to drive your delete.
ie
CREATE TABLE #table1
(id INT,
code int
);
WITH cte AS
(select a.ID, a.Code, ROW_NUMBER() OVER(PARTITION by COdE ORDER BY ID) AS rn
from #Table1 a
)
DELETE x
FROM #table1 x
JOIN cte ON x.id = cte.id
WHERE cte.rn > 1
But...
If you are going to be doing a lot of deletes from a very large table you might be better off to select out the rows you need into a temp table & then truncate your table and re-insert the rows you need.
Keeps the Transaction log from getting hammered, your CI getting Fragged and should be quicker too!
It is actually very simple:
DELETE FROM Table1
WHERE ID NOT IN
(SELECT MAX(ID)
FROM Table1
GROUP BY CODE)
Self join solution with a performance test VS cte.
create table codes(
id int IDENTITY(1,1) NOT NULL,
code int null,
CONSTRAINT [PK_codes_id] PRIMARY KEY CLUSTERED
(
id ASC
))
declare #counter int, #code int
set #counter = 1
set #code = 1
while (#counter <= 1000000)
begin
print ABS(Checksum(NewID()) % 1000)
insert into codes(code) select ABS(Checksum(NewID()) % 1000)
set #counter = #counter + 1
end
GO
set statistics time on;
delete a
from codes a left join(
select MIN(id) as id from codes
group by code) b
on a.id = b.id
where b.id is null
set statistics time off;
--set statistics time on;
-- WITH cte AS
-- (select a.id, a.code, ROW_NUMBER() OVER(PARTITION by code ORDER BY id) AS rn
-- from codes a
-- )
-- delete x
-- FROM codes x
-- JOIN cte ON x.id = cte.id
-- WHERE cte.rn > 1
--set statistics time off;
Performance test results:
With Join:
SQL Server Execution Times:
CPU time = 3198 ms, elapsed time = 3200 ms.
(999000 row(s) affected)
With CTE:
SQL Server Execution Times:
CPU time = 4197 ms, elapsed time = 4229 ms.
(999000 row(s) affected)
It's basically done like this:
WITH CTE_Dup AS
(
SELECT*,
ROW_NUMBER()OVER (PARTITIONBY SalesOrderno, ItemNo ORDER BY SalesOrderno, ItemNo)
AS ROW_NO
from dbo.SalesOrderDetails
)
DELETEFROM CTE_Dup WHERE ROW_NO > 1;
NOTICE: MUST INCLUDE ALL FIELDS!!
Here is another example:
CREATE TABLE #Table (C1 INT,C2 VARCHAR(10))
INSERT INTO #Table VALUES (1,'SQL Server')
INSERT INTO #Table VALUES (1,'SQL Server')
INSERT INTO #Table VALUES (2,'Oracle')
SELECT * FROM #Table
;WITH Delete_Duplicate_Row_cte
AS (SELECT ROW_NUMBER()OVER(PARTITION BY C1, C2 ORDER BY C1,C2) ROW_NUM,*
FROM #Table )
DELETE FROM Delete_Duplicate_Row_cte WHERE ROW_NUM > 1
SELECT * FROM #Table
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 !!