Improve performance of SQL Server stored procedure - sql-server

I need to improve the performance of a stored procedure that is used by SSRS. When a very narrow set of parameters are passed, the stored procedure completes rather quickly. But when more than a hundred thousand results are returned, the stored procedure takes minutes to run.
For example, if I run this using the date range of 1982 - 2020, it takes 15 minutes to run and returns 180000 records.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[uspContractFundingbyYear2]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[uspContractFundingbyYear2]
GO
-- =============================================
-- Author: J. Dickens
-- Create date: April 2011
-- Description: This SP generates contract funding records for the selected parameters.
-- Each row should represent a single contract funding record.
-- In many cases, either 0 or 100 is used to designate an option that is not selected. The parameter should have no impact on the query.
-- Otherwise all values are passed to the parameter using the multivalue parameter function.
-- =============================================
CREATE PROCEDURE [dbo].[uspContractFundingbyYear2]
#StartYear datetime
,#EndYear datetime
,#BusinessEntityID nvarchar(max)
,#ContractTypeCode nvarchar(max)
,#ContractStatusCode nvarchar(max)
,#ContractOrgID nvarchar(max)
,#ResearchTypeCode nvarchar(max)
,#ExportControlLevelCode nvarchar(max)
,#ScienceAreaID nvarchar(max)
,#ThrustAreaID nvarchar (max)
,#ContractStartdate datetime
,#ContractEnddate datetime
,#ResearchBusID int
,#MasterContractOnlyFlag bit
,#StateProvinceCode nvarchar(max)
,#CountryID nvarchar(max)
,#FinalYearFlag int
,#CollaborativeDollars int
,#SubcontractsFlag int
,#FundingOrgID nvarchar(max)
,#FundingTypeCode nvarchar(max)
,#FundingStatusCode nvarchar(max)
,#FundingStartdate datetime
,#FundingEnddate datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--- Prepare a table of Year Numbers, for use with Cross Join
DECLARE #Dates TABLE (YearNo INT)
;WITH n(n) AS
(
SELECT YEAR(#StartYear)
UNION ALL
SELECT n+1
FROM n
WHERE n < YEAR(#EndYear)
)
INSERT INTO #Dates(YearNo)
SELECT n
FROM n
ORDER BY n
-- Now find all the contract funding records
select distinct
be.BusinessEntityName
, Q.ScienceArea
, r.ResearchBusID
, c.FormattedBusID
, o.Abbreviation as 'ContractOrg'
, c.StateProvinceCode
, cy.CountryName
, cf.ContractFundingID
, c.ContractTypeCode
, c.ContractID,
, [Thrust] = dbo.ufConcatenateThrustsforResTaskDate (r.ResearchID, NULL, NULL, NULL)
, [PI] = (select STUFF((select distinct(coalesce('; '+P.lastfirst,''))
from ResTaskParticipant rtp
join PersonOrg po on rtp.PersonOrgID = po.PersonOrgID
join Person p on po.PersonID = p.PersonID
where rtp.ResearchID = r.researchid
and rtp.ParticipantTypeCode = 'PI'
and (rtp.enddate > getdate() or rtp.enddate = r.enddate)
for XML path ('')),1,2,'')),
c.ContractStatusCode,
cf.Fundingstatuscode as 'FundingStatus', cf.FundingTypeCode as 'FundingType', cf.Funding,
o2.Abbreviation as 'FundingSourceOrg', cf.StartDate, cf.EndDate,
DATEDIFF(m,cf.StartDate, dateadd(d,2,cf.EndDate)) as 'ContractMonths', --using 2 days here to allow for leap years
[BurnRate] =
case
when DATEDIFF(m,cf.StartDate, dateadd(d,2,cf.EndDate)) = 0 --using 2 days here to allow for leap years
then convert(decimal(2,1), 0)
else
convert(decimal (15,5), convert(decimal(15,0),cf.Funding)/convert(decimal(2,0),(DATEDIFF(m,cf.StartDate, dateadd(d,2,cf.EndDate))))) --using 2 days here to allow for leap years
end
, [IsFirstOfMonth] =
case when cf.startdate = (select FirstDayofMonth from dbo.ufGetFirstLastDayofMonth(cf.StartDate, 0)) then 1 else 0 end
, [IsEndOfMonth] =
case when datepart(day,cf.EndDate) = datepart(day,(select LastDayofMonth from dbo.ufGetFirstLastDayofMonth(cf.EndDate, 0))) then 1 else 0 end
, [MonthsInYear] = Convert(decimal(2,0), (select dbo.ufmonthsincontractforyear (cf.startdate, cf.EndDate, d.YearNo)))
,[YearNo] = d.YearNo
from ContractFunding cf
join Contract c on cf.ContractID = c.ContractID
join Research r on c.ResearchID = r.ResearchID
join BusinessEntity be on r.BusinessEntityID = be.BusinessEntityID
join Organization o on c.OrganizationID = o.OrganizationID --Contract Org
join Country cy on c.CountryID = cy.CountryID
join Organization o2 on cf.SourceOrganizationID = o2.OrganizationID --Funding Org
left outer join TaskFunding tf on cf.ContractFundingID = tf.ContractFundingID
--Get the range of years for each Contract Funding row
cross join #Dates d
--get Science Area
cross apply (select [ScienceArea] = dbo.ufConcatenateScienceAreasforResTaskDate (r.ResearchID, NULL, NULL, NULL)) as Q
where
-- required parameters for running report are Start and End Years
cf.StartDate <= #EndYear and cf.EndDate >= #StartYear
-- now all the bells and whistles
and r.BusinessEntityID in (select val from [dbo].[ufStringToTable] (#BusinessEntityID,',',1))
and c.ContractTypeCode in (select val from [dbo].[ufStringToTable] (#ContractTypeCode,',',1))
and c.ContractStatusCode in (select val from [dbo].[ufStringToTable] (#ContractStatusCode,',',1))
and c.OrganizationID in (select val from dbo.ufStringToTable (#ContractOrgID,',',1))
and (#ResearchTypeCode = '0' or (r.ResearchTypeCode in (select val from dbo.ufStringToTable (#ResearchTypeCode,',',1))))
and (#ExportControlLevelCode = '100' or c.ExportControlLevelCode = #ExportControlLevelCode)
-- For Science Area Funding, of the set of Contract Funding records returned, select those that match the given Science Area
and (#ScienceAreaID = '0' or (cf.ScienceAreaID in (select val from dbo.ufStringToTable (#ScienceAreaID,',',1))))
-- For Thrust Area Funding, of the set of Contract Funding records returned, select those that match the given Thrust Area
and (#ThrustAreaID = '0' or (tf.ThrustAreaID in (select val from dbo.ufStringToTable (#ThrustAreaID,',',1))))
-- Find Contracts within given "Active" dates
and (
(#ContractStartdate is null and #ContractEnddate is null)
OR
(c.enddate > #ContractStartdate and c.StartDate <=#ContractEnddate)
or
(#ContractStartdate is null and c.StartDate <=#ContractEnddate)
or
(c.EndDate > #ContractStartdate and #ContractEnddate is null)
)
and (#ResearchBusID = '' or r.ResearchBusID = #ResearchBusID)
and (#MasterContractOnlyFlag = 0 or (#MasterContractOnlyFlag = 1 and c.MasterContractFlag = 1))
and (#StateProvinceCode = '0' or (c.StateProvinceCode in (select val from dbo.ufStringToTable (#StateProvinceCode,',',1))))
and c.CountryID in (select val from dbo.ufStringToTable (#CountryID,',',1)) --Not all contracts have a country assignment
and (#FinalYearFlag = 100
or
(#FinalYearFlag = 0 and (dbo.ufContractIsInFinalYear(c.contractid,getdate()) = 0))
or
(#FinalYearFlag = 1 and (dbo.ufContractIsInFinalYear(c.contractid,GETDATE()) = 1))
)
and (#CollaborativeDollars = 100
or
(#CollaborativeDollars = 0 and
not exists
(select * from ContractFunding cf2 where cf2.FundingTypeCode='Collaborative' and cf2.ContractID=c.ContractID)
)
or
(#CollaborativeDollars = 1 and
exists
(select * from ContractFunding cf2 where cf2.FundingTypeCode='Collaborative' and cf2.ContractID=c.ContractID)
)
)
and (#SubcontractsFlag = 100
or
(#SubcontractsFlag = 0 and c.SubContractFlag = 0)
or
(#SubcontractsFlag = 1 and c.SubContractFlag = 1)
)
and (cf.SourceOrganizationID in (select val from dbo.ufStringToTable (#FundingOrgID,',',1)))
and (cf.FundingTypeCode in (select val from dbo.ufStringToTable (#FundingTypeCode,',',1)))
and (cf.FundingStatusCode in (select val from dbo.ufStringToTable (#FundingStatusCode,',',1)))
and (
(#FundingStartdate is null and #FundingEnddate is null)
OR
(c.enddate > #FundingStartdate and c.StartDate <=#FundingEnddate)
or
(#FundingStartdate is null and c.StartDate <=#FundingEnddate)
or
(c.EndDate > #FundingStartdate and #FundingEnddate is null)
)
End
GO

Related

TSQL Dynamic Update of TOP(n) Rows

i have a table with one line per travel
second table has values of origin and destination and quantity for each combination
i need to run an update that run on the second table that update top N rows on the first table (n comes from the second table)
im using this update script inside a cursor in a procedure :
ALTER PROCEDURE [dbo].[SP_Travels_History_FromMissing]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #C_Day_Type_Code INT = NULL
DECLARE #C_PartOfDay_Code INT = NULL
DECLARE #C_Station_From INT = NULL
DECLARE #C_DestinationStation INT = NULL
DECLARE #C_Deploy INT = NULL
TRUNCATE TABLE [TCK_TMP_Fact_ALL_Travels_History_FromMissing]
INSERT
INTO [TCK_TMP_Fact_ALL_Travels_History_FromMissing]
SELECT TRV.Day_Type_Code,
TRV.PartOfDay_Code,
HIS.Station_From,
TRV.DestinationStation,
-- TRV.TotalTravels,
-- HIS.Percentage,
-- (TRV.TotalTravels * HIS.Percentage) / 100,
CAST(CEILING((TRV.TotalTravels * HIS.Percentage) / 100) AS INT) Deploy
FROM
(
SELECT DAT.Day_Type_Code,
-- DATEPART(HOUR,[EntranceDate]) EntranceHour,
POD.PartOfDay_Code,
DestinationStation,
COUNT(*) TotalTravels
FROM [Tickets].[dbo].[TCK_STG_Fact_Contract_Magnetic_Travels1] TRV
LEFT OUTER JOIN [Tickets].[dbo].[TCK_DWH_Dim_Date] DAT ON CAST(TRV.[ExitDate] AS DATE) = CAST(DAT.Date AS DATE)
LEFT OUTER JOIN [Tickets].[dbo].[TCK_DWH_Dim_Manual_PartOfDay] POD ON DATEPART(HOUR,TRV.[ExitDate]) = POD.PartOfDay_Hour
WHERE [EventID_Entrance] = -1
GROUP BY DAT.Day_Type_Code,
POD.PartOfDay_Code,
DestinationStation
) TRV
LEFT OUTER JOIN
(
SELECT *
FROM [dbo].[TCK_DWH_Fact_ALL_Travels_History_FromMissing]
WHERE [Total_Travels] IS NOT NULL
) HIS ON HIS.Day_Type_Code = TRV.Day_Type_Code
AND HIS.PartOfDay_Code = TRV.PartOfDay_Code
AND HIS.Station_To = TRV.DestinationStation
WHERE CAST(CEILING((TRV.TotalTravels * HIS.Percentage) / 100) AS INT) > 0
ORDER BY 1,2,4,5 DESC
DECLARE Missing_Cursor CURSOR FOR
SELECT [Day_Type_Code],
[PartOfDay_Code],
DestinationStation,
Station_From,
[Deploy]
FROM [Tickets].[dbo].[TCK_TMP_Fact_ALL_Travels_History_FromMissing]
ORDER BY [Day_Type_Code],
[PartOfDay_Code],
DestinationStation,
[Deploy] DESC,
Station_From
OPEN Missing_Cursor
FETCH NEXT
FROM Missing_Cursor
INTO #C_Day_Type_Code,
#C_PartOfDay_Code,
#C_DestinationStation,
#C_Station_From,
#C_Deploy
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE TOP (#C_Deploy) TRV
SET TRV.SourceStationID = #C_Station_From,
TRV.MissingStationSourceCode = 888
FROM [Tickets].[dbo].TCK_STG_Fact_Contract_Magnetic_Travels1 TRV
LEFT OUTER JOIN [Tickets].[dbo].[TCK_DWH_Dim_Date] DAT ON CAST(TRV.[ExitDate] AS DATE) = CAST(DAT.Date AS DATE)
LEFT OUTER JOIN [Tickets].[dbo].[TCK_DWH_Dim_Manual_PartOfDay] POD ON DATEPART(HOUR,TRV.[ExitDate]) = POD.PartOfDay_Hour
WHERE DAT.Day_Type_Code = #C_Day_Type_Code
AND POD.PartOfDay_Code = #C_PartOfDay_Code
AND TRV.DestinationStation = #C_DestinationStation
AND TRV.SourceStationID IS NULL
-- COMMIT;
-- ORDER BY TRV.ContractID
-- LIMIT #C_Deploy;
-------------------------
FETCH NEXT
FROM Missing_Cursor
INTO #C_Day_Type_Code,
#C_PartOfDay_Code,
#C_DestinationStation,
#C_Station_From,
#C_Deploy
END
CLOSE Missing_Cursor
DEALLOCATE Missing_Cursor
END
Problem is It Takes more then 30 minutes
how can i use a dynamic update without a cursor?
any other suggestions are welcome

Pros and Cons putting all the logic in Stored Procedure (SQL Server) [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I just join new organization. They like to put all the logic in stored procedure?
May I know is it a good practice in Software Development.
In addition, I found some of stored procedure having some performance issue when increase number of user. For example, it takes some time to run the following stored procedure.
Next, it it good to have multiples join in a stored procedure like following.
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[USP_230_GetMRPByBatch]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[USP_230_GetMRPByBatch]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[USP_230_GetMRPByBatch]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql #statement = N'CREATE PROCEDURE [dbo].[USP_230_GetMRPByBatch] AS'
END
GO
ALTER PROCEDURE [dbo].[USP_230_GetMRPByBatch]
#DIST_CD NVARCHAR(20),
#CUST_CD NVARCHAR(20),
#WHS_CD NVARCHAR(20),
#PRD_CD NVARCHAR(20),
#BILL_METHOD CHAR(1),
#INV_KEY NVARCHAR(50) = ''
AS
--2017-06-30 Chuah - Add Available stock summary from INVT_MASTER
--2018-01-02 Chuah - Add parameter #INV_KEY, need to consider allocated stock for invoice.
BEGIN
SET NOCOUNT ON;
DECLARE #COUNTRY_DT DATETIME
SET #COUNTRY_DT = dbo.GetCountryDate()
IF OBJECT_ID('tempdb..#BATCH_LOTEXP_PERIOD') IS NOT NULL
DROP TABLE #BATCH_LOTEXP_PERIOD
-- Get Batch Exclusion Expiry period
SELECT #PRD_CD AS PRD_CD, E.LOTEXP_IND, E.LOTEXP_MTH,
CASE B.REF_VALUE3 WHEN 0 THEN 0
ELSE ISNULL(J.LOTEXP_DAY,0) END 'LOTEXP_DAY'
INTO #BATCH_LOTEXP_PERIOD
FROM MST_CUSTOPTION AS E
INNER JOIN SET_REF B ON B.DIST_CD = #DIST_CD AND B.REF_TYPE = 'G_WAREHOUSE' AND B.REF_PARAM = #WHS_CD AND B.REF_VALUE2 = 'Y' AND B.REF_VALUE3 = '1'
OUTER APPLY (
SELECT TOP 1 C.LOTEXP_DAY
FROM MST_LOTEXP C
INNER JOIN MST_CUSTATTR D ON D.CUST_CD = #CUST_CD AND D.SYNCOPERATION <> 'D'
INNER JOIN VW_PRD I ON I.PRDCAT_CD = #PRD_CD
WHERE C.CUSTHIER_LEVEL = D.CUSTHIER_LEVEL AND C.CUSTHIER_CD = D.CUSTHIER_CD
AND C.PRDCAT_LEVEL = I.PRDCAT_LEVEL AND C.PRDCAT_CD = I.PRDCAT_CD
AND C.SYNCOPERATION <> 'D'
ORDER BY CAST(C.PRDCAT_LEVEL AS INT) DESC
) AS J
WHERE E.CUST_CD = #CUST_CD AND E.SYNCOPERATION <> 'D'
GROUP BY E.LOTEXP_IND, E.LOTEXP_MTH, B.REF_VALUE3, J.LOTEXP_DAY
-- Get Batch Expiry date
IF OBJECT_ID('tempdb..#BATCH_EXPIRY') IS NOT NULL
DROP TABLE #BATCH_EXPIRY
SELECT A.PRD_CD,
CASE A.LOTEXP_IND WHEN 1 THEN (CASE A.LOTEXP_MTH WHEN 0
THEN CONVERT(VARCHAR(10), DATEADD(DAY,A.LOTEXP_DAY,CONVERT(VARCHAR(10),#COUNTRY_DT,120)),120)
ELSE CONVERT(VARCHAR(10),DATEADD(MONTH,A.LOTEXP_MTH,CONVERT(VARCHAR(10),#COUNTRY_DT,120)),120) END)
ELSE CONVERT(VARCHAR(10),DATEADD(DAY,A.LOTEXP_DAY,CONVERT(VARCHAR(10),#COUNTRY_DT,120)),120) END EXPIRY_DT
INTO #BATCH_EXPIRY
FROM #BATCH_LOTEXP_PERIOD AS A
-- Get MRP by batch
SELECT A.MRP, A.STK_AVAILABLE + ISNULL(F.PRD_QTY, 0) AS STK_AVAILABLE, A.AVAILABLE_STK_MAIN + ISNULL(F.PRD_QTY, 0) AVAILABLE_STK_MAIN FROM (
SELECT A.MRP, SUM(A.AVAILABLE_STK) AS STK_AVAILABLE, MAX(E.AVAILABLE_STK) AVAILABLE_STK_MAIN
FROM INVT_BATCH A
INNER JOIN INVT_BIN B ON B.DIST_CD = A.DIST_CD AND B.WHS_CD = A.WHS_CD AND B.BIN = A.BIN AND B.SYNCOPERATION <> 'D'
LEFT OUTER JOIN #BATCH_EXPIRY AS C ON C.PRD_CD = A.PRD_CD
LEFT OUTER JOIN MST_PRDMRP AS D ON D.DIST_CD = A.DIST_CD AND D.PRD_CD = A.PRD_CD AND D.MRP = A.MRP
LEFT OUTER JOIN (
SELECT DIST_CD, WHS_CD, PRD_CD, SUM(AVAILABLE_STK) AVAILABLE_STK FROM INVT_MASTER
WHERE DIST_CD = #DIST_CD AND PRD_CD = #PRD_CD AND WHS_CD = #WHS_CD
GROUP BY DIST_CD, WHS_CD, PRD_CD
) AS E ON E.DIST_CD = A.DIST_CD AND E.PRD_CD = A.PRD_CD AND E.WHS_CD = A.WHS_CD
WHERE A.DIST_CD = #DIST_CD
AND A.WHS_CD = #WHS_CD
and A.PRD_CD = #PRD_CD
AND CONVERT(VARCHAR(10),A.EXPIRY_DT,120) > ISNULL(C.EXPIRY_DT, CONVERT(VARCHAR(10),#COUNTRY_DT,120))
--AND A.AVAILABLE_STK > 0
AND ISNULL(A.ONHOLD_IND,0) = 0
AND A.SYNCOPERATION <> 'D'
AND ISNULL(A.MRP,0) <> 0
AND (#BILL_METHOD = 'A' OR (#BILL_METHOD = 'L' AND D.PRD_CD IS NOT NULL))
GROUP BY A.DIST_CD, A.WHS_CD, A.PRD_CD, A.MRP
) A
LEFT JOIN (
SELECT TAXABLE_PRC, SUM(PRD_QTY * B.CONV_FACTOR_SML) PRD_QTY FROM TXN_INVDTL_BIN A
INNER JOIN MST_PRDUOM B ON A.PRD_CD = B.PRD_CD AND A.UOM_CD = B.UOM_CD AND B.SYNCOPERATION <> 'D'
WHERE INV_KEY = #INV_KEY AND A.PRD_CD = #PRD_CD AND WHS_CD = #WHS_CD
GROUP BY TAXABLE_PRC
) F ON F.TAXABLE_PRC = A.MRP
WHERE A.STK_AVAILABLE + ISNULL(F.PRD_QTY, 0) > 0
DROP TABLE #BATCH_LOTEXP_PERIOD
DROP TABLE #BATCH_EXPIRY
END
GO
I found some of stored procedure having some performance issue when increase number of user
when stored procedures/any code runs slow, you may need to find the cause by seeing wait stats and execution plan.
Also you said, this happens when load is high,then you need to further check which metric is high..Is it CPU,RAM,IO and start your troubleshooting from there

Comparison results between two select statements in a stored procedure

I want to start off by saying that I am brand new to Stored Procedures, and am basically teaching myself how to do them. Any suggestions or advice will be greatly appreciated. I would mail you chocolate if I could.
The Gist: My organization's clients take a survey on their initial visit and on each 6th subsequent visits. We need to know if the individual has shown improvement over time. The way we decided to do this is compare the 1st to the most recent. So if they have been to 18 sessions, it would be the 1st and 3rd surveys that are compared (because they would have completed the survey 3 times over 18 sessions).
I have been able to obtain the "first" score and the "recent" score with two complex, multiple layered-nested select statements inside of one stored procedure. The "first" one is a TOP(1) linking on unique id (DOCID) and then ordered by date. The "recent" one is a TOP(1) linking on unique id (DOCID) and then ordered by date descending. This gets me exactly what I need within each statement, but it does not output what I need correctly which is obviously to the ordering in the statements.
The end result will be to create a Crystal Report with it for grant reporting purposes.
Declare
#StartDate Date,
#EndDate Date,
#First_DOCID Int,
#First_Clientkey Int,
#First_Date_Screening Date,
#First_Composite_Score Float,
#First_Depression_Score Float,
#First_Emotional_Score Float,
#First_Relationship_Score Float,
#Recent_DOCID Int,
#Recent_Clientkey Int,
#Recent_Date_Screening Date,
#Recent_Composite_Score Float,
#Recent_Depression_Score Float,
#Recent_Emotional_Score Float,
#Recent_Relationship_Score Float,
#Difference_Composit_Score Float,
#Difference_Depression_Score Float,
#Difference_Emotional_Score Float,
#Difference_Relationship_Score Float
SET #StartDate = '1/1/2016'
SET #EndDate = '6/1/2016'
BEGIN
SELECT #First_DOCID = CB24_1.OP__DOCID, #First_Date_Screening = CB24_1.Date_Screening, #First_Clientkey = CB24_1.ClientKey, #First_Composite_Score = CB24_1.Composite_score, #First_Depression_Score = CB24_1.Depression_Results, #First_Emotional_Score = CB24_1.Emotional_Results, #First_Relationship_Score = CB24_1.Relationships_Results
FROM FD__CNSLG_BASIS24 AS CB24_1
WHERE (CB24_1.OP__DOCID =
(Select TOP(1) CB24_2.OP__DOCID
...
ORDER BY CB24_2.Date_Screening))
ORDER BY ClientKey DESC
END
BEGIN
SELECT #Recent_DOCID = CB24_1.OP__DOCID, #Recent_Date_Screening = CB24_1.Date_Screening, #Recent_Clientkey = CB24_1.ClientKey, #Recent_Composite_Score = CB24_1.Composite_score, #Recent_Depression_Score = CB24_1.Depression_Results, #Recent_Emotional_Score = CB24_1.Emotional_Results, #Recent_Relationship_Score = CB24_1.Relationships_Results
FROM FD__CNSLG_BASIS24 AS CB24_1
WHERE (CB24_1.OP__DOCID =
(Select TOP(1) CB24_2.OP__DOCID
...
ORDER BY CB24_2.Date_Screening DESC))
ORDER BY ClientKey
END
SET #Difference_Composit_Score = (#Recent_Composite_Score - #First_Composite_Score)
SET #Difference_Depression_Score = (#Recent_Depression_Score - #First_Depression_Score)
SET #Difference_Emotional_Score = (#Recent_Emotional_Score - #First_Emotional_Score)
SET #Difference_Relationship_Score = (#Recent_Relationship_Score - #First_Relationship_Score)
SELECT
#First_DOCID AS First_Docid,
#First_Clientkey AS First_Clientkey,
#First_Date_Screening AS First_Date_Screening,
#First_Composite_Score AS First_Composite_Score,
#First_Depression_Score AS First_Depression_Score,
#First_Emotional_Score AS First_Emotional_Score,
#First_Relationship_Score AS First_Relationship_Score,
#Recent_DOCID AS Recent_DOCID,
#Recent_Clientkey AS Recent_Clientkey,
#Recent_Date_Screening AS Recent_Date_Screening,
#Recent_Composite_Score AS Recent_Composite_Score,
#Recent_Depression_Score AS Recent_Depression_Score,
#Recent_Emotional_Score AS Recent_Emotional_Score,
#Recent_Relationship_Score AS Recent_Relationship_Score,
#Difference_Composit_Score AS Difference_Composit_Score,
#Difference_Depression_Score AS Difference_Depression_Score,
#Difference_Emotional_Score AS Difference_Emotional_Score,
#Difference_Relationship_Score AS Difference_Relationship_Score
In SQL you don't want unnecessary declared variables.
Here's a contrived but reproducible example which utilizes common table expressions and window functions that should get you in the right direction. I created the stored procedure from the template with the necessary input parameters (which in real life you'd like to avoid).
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE dbo.Client_Improvement_Results
(#StartDate DATETIME, #EndDate DATETIME)
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for procedure here
-- You would never do this in real-life but for a simple reproducible example...
DECLARE #Survey TABLE
(
Clientkey INT,
Date_Screening DATE,
Composite_Score FLOAT
)
INSERT INTO #Survey
VALUES
(1, '2014-04-01', 42.1),
(1, '2014-04-10', 46.1),
(1, '2014-04-20', 48.1),
(2, '2014-05-10', 40.1),
(2, '2014-05-20', 30.1),
(2, '2014-05-30', 10.1)
;
--Use Common Table Expression & Window Functions to ID first/recent visit by client
WITH CTE AS (
SELECT
S.Clientkey
,S.Composite_Score
,S.Date_Screening
,First_Date_Screening = MIN(S.Date_Screening) OVER(PARTITION BY S.Clientkey)
,Recent_Date_Screening = MAX(S.Date_Screening) OVER(PARTITION BY S.Clientkey)
FROM #Survey AS S
)
--Self join of CTE with proper filters
--applied allows you to return differences in one row
SELECT
f.Clientkey
,f.First_Date_Screening
,f.Recent_Date_Screening
,Difference_Score = r.Composite_Score - f.Composite_Score
FROM
CTE AS f --first
INNER JOIN CTE AS r --recent
ON f.Clientkey = r.Clientkey
WHERE
f.Date_Screening = f.First_Date_Screening
AND r.Date_Screening = r.Recent_Date_Screening
END
GO
Here is the solution I came up with after everyone amazing advice.
I want to go back and replace the TOP(1) with another new thing I learned at some point:
select pc.*
from (select pc.*, row_number() over (partition by Clientkey, ProgramAdmitKey order by Date_Screening) as seqnum
from FD__CNSLG_BASIS24 PC) pc
where seqnum = 1
I will have to play with the above script a bit first, however. It doesn't like to be inserted into the larger script below.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
BEGIN
SET NOCOUNT ON;
Declare
#StartDate Date,
#EndDate Date
SET #StartDate = '1/1/2016'
SET #EndDate = '6/1/2016'
WITH CNSL_Clients AS (
SELECT PC_CNT.Clientkey, PC_Cnt.ProgramAdmitKey, PC_Cnt.OP__DOCID
FROM FD__Primary_Client as PC_Cnt
INNER JOIN VW__Cnsl_Session_Count_IndvFamOnly as cnt
ON PC_Cnt.Clientkey = CNT.Clientkey AND PC_Cnt.ProgramAdmitKey = CNT.ProgramAdmitKey
WHERE ((pc_CNT.StartDate between #StartDate AND #EndDate) OR (pc_CNT.StartDate <= #StartDate AND pc_CNT.ENDDate >= #StartDate) OR (pc_CNT.StartDate <= #StartDate AND pc_CNT.ENDDate is null))
AND CNT.SessionCount>=6
),
FIRST_BASIS AS (
SELECT CB24_1.OP__DOCID, CB24_1.Date_Screening, CB24_1.ClientKey, CB24_1.ProgramAdmitKey, CB24_1.Composite_score, CB24_1.Depression_Results,CB24_1.Emotional_Results, CB24_1.Relationships_Results
FROM FD__CNSLG_BASIS24 AS CB24_1
WHERE (CB24_1.OP__DOCID =
(Select TOP(1) CB24_2.OP__DOCID
FROM FD__CNSLG_BASIS24 AS CB24_2
Inner JOIN CNSL_Clients
ON CB24_2.ClientKey = CNSL_Clients.ClientKey AND CB24_2.ProgramAdmitKey = CNSL_Clients.ProgramAdmitKey
WHERE (CB24_1.ClientKey = CB24_2.ClientKey) AND (CB24_1.ProgramAdmitKey = CB24_2.ProgramAdmitKey)
ORDER BY CB24_2.Date_Screening))
),
RECENT_BASIS AS (
SELECT CB24_1.OP__DOCID, CB24_1.Date_Screening, CB24_1.ClientKey, CB24_1.ProgramAdmitKey, CB24_1.Composite_score, CB24_1.Depression_Results,CB24_1.Emotional_Results, CB24_1.Relationships_Results
FROM FD__CNSLG_BASIS24 AS CB24_1
WHERE (CB24_1.OP__DOCID =
(Select TOP(1) CB24_2.OP__DOCID
FROM FD__CNSLG_BASIS24 AS CB24_2
Inner JOIN CNSL_Clients
ON CB24_2.ClientKey = CNSL_Clients.ClientKey AND CB24_2.ProgramAdmitKey = CNSL_Clients.ProgramAdmitKey
WHERE (CB24_1.ClientKey = CB24_2.ClientKey) AND (CB24_1.ProgramAdmitKey = CB24_2.ProgramAdmitKey)
ORDER BY CB24_2.Date_Screening DESC))
)
SELECT F.OP__DOCID AS First_DOCID,R.OP__DOCID as Recent_DOCID,F.ClientKey, F.ProgramAdmitKey, F.Composite_Score AS FComposite_Score, R.Composite_Score as RComposite_Score, Composite_Change = R.Composite_Score - F.Composite_Score, F.Depression_Results AS FDepression_Results, R.Depression_Results AS RDepression_Resluts, Depression_Change = R.Depression_Results - F.Depression_Results, F.Emotional_Results AS FEmotional_Resluts, R.Emotional_Results AS REmotionall_Reslu, Emotional_Change = R.Emotional_Results - F.Emotional_Results, F.Relationships_Results AS FRelationships_Resluts, R.Relationships_Results AS RRelationships_Resluts, Relationship_Change = R.Relationships_Results - F.Relationships_Results
FROM First_basis AS F
FULL Outer JOIN RECENT_BASIS AS R
ON F.ClientKey = R.ClientKey AND F.ProgramAdmitKey = R.ProgramAdmitKey
ORDER BY F.ClientKey
END
GO

sql stored procedure - i would like to change it to a job but it has parameters

I have a VERY long running, heavy stored procedure, so I would like to change it over to a job that runs every night and updates a table so the user can run it quickly during the day. My problem is that it takes parameters IN THE LEFT JOIN, so I don't see how I could do it. I tried to remove the parameters from the joins and just take all the records, but first problem with that is that these left joins are joins to table valued functions with group by clauses (so it's tons of records now) and also when I did it, it crashed my whole server. I will post part of my stored proc to give you an idea of what i'm doing for now.
--alternatively, i would take an answer that could change my query perfomance from 2 minutes to 10 seconds. :)
--btw, another thing that is slowing down this query is that the functions are querying a linked server...
create PROC [dbo].[mySP]
#FromDate DateTime,
#ToDate DateTime = NULL,
#Style NVARCHAR(max),
#ItemType NVARCHAR(300),
#ItemCode NVARCHAR(150)= null,
#ItemsNotSold INT,
#MultiplyUsage DECIMAL(18,4),
#SParts BIT,
#vendorId nvarchar(50),
#currentPage int,
#largeOrderSoNumbers nvarchar(300) = null,
#excludeprojTag BIT,
#hasOrderQty BIT = null,
#createPO BIT = null,
#excludeTestOrders BIT = null,
#refresh BIT = null,
#userid int = null,
#positiveOverUse BIT = null
AS
SELECT #FROMDATE = #FROMDATE
,#ToDate=#ToDate
,#Style=#Style
,#ItemType=#ItemType
,#ItemCode=#ItemCode
,#ItemsNotSold=#ItemsNotSold
,#MultiplyUsage=#MultiplyUsage
,#SParts=#SParts
,#vendorId=#vendorId
,#currentPage=#currentPage
,#largeOrderSoNumbers=#largeOrderSoNumbers
,#excludeprojTag=#excludeprojTag
,#hasOrderQty =#hasOrderQty
,#createPO =#createPO
,#excludeTestOrders =#excludeTestOrders
,#refresh =#refresh
,#userid =#userid
,#positiveOverUse=#positiveOverUse
DECLARE #amountPerPage int = 400;
DECLARE #startingPoint int = (#currentpage - 1) * #amountPerPage
SET #TODATE = NULLIF(#TODATE,'1/1/4000');
SET #ITEMCODE = NULLIF(#ITEMCODE,'');
SET #largeOrderSoNumbers = NULLIF(#LARGEORDERSONUMBERS,'');
BEGIN
IF #Style = '-1'
SET #Style = NULL
IF #ItemType = '-1'
SET #ItemType = NULL
END
SELECT
ISNULL(c.NewQB_ListID,iit.ListID) ListID,
(select salesdesc from iteminventory i where i.listid = ISNULL(c.NewQB_ListID,iit.ListID)) as SalesDesc,
isnull(inl.linename,'') + isnull(c.NewName,Name) as ItemCode ,
max(il.CubicMeterKD) as CubicMeterKD,
sum(CONVERT(decimal(18,4),inl.Quantity) )
- sum(CONVERT(decimal(18,4),inl.qtyRuleTag))
+ CONVERT(decimal(18,4),isnull(ai.qty,0))
-CONVERT(decimal(18,4),isnull(ai.QtyRuleTag,0))
AS TotalQtyUesd,
sum(convert(decimal(18,4),isnull(inl.QtyRuleTag,0)))
as QtyUsedWithProjectTag,
Convert(decimal(18,4),QuantityOnHand) as OnHand,
CONVERT(int,ISNULL(EzQtyOnSO.Qty,0)) as OnSalesOrder,
CONVERT(int,ISNULL(EzQtyAnyStatus.QtyLgO,0))as OnLgOrder,
Convert(decimal(18,4),QuantityOnOrder) as OnPO,
Convert(decimal(18,4),isnull(LargePO.lgOrderQtyOnPO,0)) as lgOrderQtyOnPO ,
fl.LineName
INTO #Q
FROM
iteminventory iit
LEFT JOIN Lines fl
ON iit.ParentRef_ListID = QB_LisiID
LEFT JOIN tb_ItemList il
ON iit.ListID = il.QBListID
LEFT JOIN InlSales(#FromDate,#ToDate, #excludeprojtag, #excludeTestOrders) INL
ON IIT.LISTID = INL.ITEMREF_LISTID
LEFT JOIN tb_CombinedItems c
ON iit.ListID = c.ListID
LEFT JOIN [QuantityInvoice](#FromDate, #ToDate,#excludeprojTag,#excludeTestOrders) as ai
ON ai.QBID = iit.ListID
LEFT JOIN fn_QuantityOnSalesOrder(#excludeTestOrders) AS EzQtyOnSO
ON iit.ListID = EzQtyOnSO.QBID
LEFT JOIN QuantityAnyStatus(#largeOrderSoNumbers, #excludeprojTag) AS EzQtyAnyStatus
ON iit.ListID = EzQtyAnyStatus.QBID
LEFT JOIN dbo.[FN_LargePO](#excludeTestOrders) LargePO --WITH (nolock)
ON LargePO.QBID = iit.ListID
WHERE
(#Style is null or ISNULL(c.ParentListID,ParentRef_ListID) in (SELECT Value From fn_MultiValueParameter(#Style))) AND
(#ItemType is null or ItemTypeCode in (SELECT Value From fn_MultiValueParameter(#ItemType))) AND
(#ItemCode is null or il.ItemCode like '%' + #ItemCode + '%') AND
(IsActive = 1 OR c.ListID IS NOT NULL)
GROUP BY
ISNULL(c.NewQB_ListID,iit.ListID),
ISNULL(c.NewName,Name),
inl.LineName,
QuantityOnHand,
iit.QuantityOnOrder,
CONVERT(int,ISNULL(EzQtyOnSO.Qty,0)),
fl.LineName,
CONVERT(decimal(18,4),isnull(ai.qty,0)),
Convert(decimal(18,4),isnull(LargePO.lgOrderQtyOnPO,0)),
CONVERT(int,ISNULL(EzQtyAnyStatus.QtyLgO,0)),
CONVERT(decimal(18,4),isnull(ai.QtyRuleTag,0))
IF #ItemsNotSold = 1
BEGIN
DELETE FROM #Q
WHERE isnull(TotalQtyUesd,0) <> 0
END
IF #ItemsNotSold = 2
BEGIN
DELETE FROM #Q
WHERE isnull(TotalQtyUesd,0) = 0
END
SELECT
q.ListID,
q.SalesDesc,
q.ItemCode,
MAX(isnull(q.CubicMeterKD,0)) as CubicMeterKD,
SUM(q.TotalQtyUesd)
TotalQtyUesd,
SUM(q.QtyUsedWithProjectTag) as QtyUsedWithProjectTag,
SUM(q.OnHand) OnHand,
SUM(q.OnSalesOrder) OnSalesOrder,
SUM(q.OnLgOrder) OnLgOrder,
SUM(q.OnPO)
- isnull(SUM(q.lgOrderQtyOnPO),0)
OnPO,
SUM(q.lgOrderQtyOnPO) as lgOrderQtyOnPO,
LineName
INTO #QTY
FROM
#Q q
GROUP BY
q.ListID,
q.salesdesc,
q.ItemCode,
q.LineName
;WITH Results AS
(
SELECT
isnull(P.listid,'') as ListID,
isnull(P.SalesDesc,'') as SalesDesc,
isnull(P.itemcode,'') as ItemCode,
ISNULL(CubicMeterKD,0) AS CubicMeterKD,
isnull(TotalQtyUesd,0) as TotalQtyUsed,
isnull(QtyUsedWithProjectTag,0) as QtyUsedWithProjectTag,
isnull(onhand,0) as OnHand,
isnull((OnHand - OnSalesOrder - OnLgOrder),0) as available,
isnull(OnLgOrder,0) as OnLgOrder,
isnull(OnPO,0) as OnPo,
isnull(lgOrderQtyOnPO,0) as lgOrderQtyOnPO,
isnull(((OnHand - OnSalesOrder) + OnPO) ,0) AS [AvailableAndOnPo],
isnull((TotalQtyUesd * #MultiplyUsage) - ((OnHand - OnSalesOrder) + OnPO),0) AS [AvlOnPOminusUsed],
isnull((((TotalQtyUesd * #MultiplyUsage) - ((OnHand - OnSalesOrder) + OnPO)) / CASE WHEN TotalQtyUesd > 0 THEN TotalQtyUesd END)*100,0) AS PctOver
,isnull(linename,0) as LineName,
isnull((select price from qbdb.dbo.tb_iteminfodetail where vendorlistid = #vendorid and itemcode =isnull(P.itemcode,'')),0.0) as price
,isnull(pod.qtytoReOrder,0) as qtyToReOrder
,isnull(pod.qtytoOrder,0) as qtyToOrder
,isnull(pod.includePO,0) as includePO
,ROW_NUMBER() OVER (ORDER BY ITEMCODE) AS RowNum
FROM #qty AS p
left join dbo.purchaseorderpreliminarydetails pod on pod.listid = P.listid and pod.deleteFlag = 0
and headerid = (select top 1 headerid from purchaseorderpreliminary where deleteFlag = 0)
WHERE
CASE WHEN #positiveOverUse = 1 THEN
isnull((((TotalQtyUesd * #MultiplyUsage) - ((OnHand - OnSalesOrder) + OnPO)) / CASE WHEN TotalQtyUesd > 0 THEN TotalQtyUesd END)*100,0)
ELSE 1 END >0
AND
case when #hasOrderQty = 1 then isnull(pod.QtyToReOrder,0) else 1 end > 0
and
CASE WHEN #SPARTS = 1 THEN CASE WHEN
ItemCode IN (
'APP',
'CD',
'-S',
'L0',
'L/42',
'L01',
'Lfrgs2',
'Lfad2',
'Sfasdf9',
'SdafdsA',
'Sfasdf3',
'Sasdf6',
'asdf0',
'Sf6',
'fasdfadf2',
'fasdfasdf',
'S2236',
'S12342',
'Sdf 30',
'SdfE 36',
'fgsfgs',
'fasdf-fdasf',
'fadf',
'fasdf-fasdf',
'sdaf',
'adf 11"',
'fda 14"',
'fdas 24"') THEN 1 ELSE 0 END ELSE 1 END = 1
)
SELECT (select count(rownum) from results) as totalItems,* FROM Results WHERE
rownum between #startingPoint + 1 and #startingPoint + #amountPerPage
Have a form the users can put their parameters into before they go home, and save them into a new table in the database. Change the stored procedure to either get the parameters from this new table or to use the new table in the joins &c. The SP now doesn't need the parameters to sent to it. Finally call the SP via a Job.

Old SQL Query Optimization

I have a pre written SQL Query, which takes 45 minutes to actually run and show the data. It returns total 80000+ records. But it takes hell lot of time to run and show the data.
SELECT
hq.QuoteHeaderId,
hq.HandsetQuoteId,
hq.QuoteDate,
t_PhoneAudit.PhoneModelId,
t_PhoneAudit.IMEI,
t_PhoneModel.ModelName,
t_PhoneBrand.Name As BrandName
, al.ActivityId
, par.Result
, al.CustomMessage,
al.[Description]
,thqai.Value AS AgentName
FROM [t_DynaGroupMissingRecordsProcessing]
INNER JOIN t_HandsetQuote hq ON t_DynaGroupMissingRecordsProcessing.HandsetQuoteId = hq.HandsetQuoteId
INNER JOIN t_PhoneAudit ON t_PhoneAudit.PhoneAuditId = hq.QuotePhoneAuditId
INNER JOIN t_PhoneModel ON t_PhoneModel.PhoneModelId = t_PhoneAudit.PhoneModelId
INNER JOIN t_PhoneBrand ON t_PhoneBrand.PhoneBrandId = t_PhoneModel.PhoneBrandId
INNER JOIN
(
SELECT par.HandsetQuoteId, txt_Value AS ActivityId, 'OK' AS Result FROM t_PhoneAuditRetail par CROSS APPLY dbo.fn_ParseText2Table(par.Ok, ',')
UNION
SELECT par.HandsetQuoteId, txt_Value AS ActivityId, 'Fault' AS Result FROM t_PhoneAuditRetail par CROSS APPLY dbo.fn_ParseText2Table(par.Fault, ',')
) par ON hq.HandsetQuoteId = par.HandsetQuoteId
INNER JOIN t_ActivityLocalization al ON par.ActivityId = al.ActivityId
INNER JOIN t_ContactChannel CC ON al.ContactChannelId = CC.ContactChannelId
INNER JOIN t_testingProfiledetails ON t_testingProfiledetails.ActivityId = al.ActivityId AND ( IsVisibleForRetail = '1' OR t_testingProfiledetails.ActivityId IN ('93','97')) AND t_testingProfiledetails.ProfileId IN (SELECT DefailtRetailProfileId FROM t_ContactChannel WHERE Name IN ('Holland Retail', 'BelCompanyNPEY Retail', 'Belcompany Retail', 'HollandNPEY Retail'))
LEFT OUTER JOIN t_HandsetQuoteAdditionalInfo thqai on thqai.HandsetQuoteId = hq.HandsetQuoteId and thqai.KeyName = 'AgentName'
WHERE [t_DynaGroupMissingRecordsProcessing].IsProcessed = 0
AND CC.Name IN ('Holland Retail', 'BelCompanyNPEY Retail', 'Belcompany Retail', 'HollandNPEY Retail')
ORDER BY hq.HandsetQuoteId
The main problem is with CC.Name IN ('Holland Retail', 'BelCompanyNPEY Retail', 'Belcompany Retail', 'HollandNPEY Retail') this statement.
If i keep on adding the name in the In Statement, the query becomes more and more slow.
Below is the defintion for the function i am using:
ALTER FUNCTION [dbo].[fn_ParseText2Table]
(
#p_SourceText VARCHAR(8000)
,#p_Delimeter VARCHAR(100) = ',' --default to comma delimited.
)
RETURNS #retTable TABLE
(
txt_value VARCHAR(2000)
)
AS
BEGIN
DECLARE #w_Continue int
,#w_StartPos int
,#w_Length int
,#w_Delimeter_pos int
,#w_tmp_txt VARCHAR(2000)
,#w_Delimeter_Len tinyint
SET #w_Continue = 1
SET #w_StartPos = 1
SET #p_SourceText = RTRIM( LTRIM( #p_SourceText))
SET #w_Length = DATALENGTH( RTRIM( LTRIM( #p_SourceText)))
SET #w_Delimeter_Len = len(#p_Delimeter)
WHILE #w_Continue = 1
BEGIN
SET #w_Delimeter_pos = CHARINDEX(#p_Delimeter,(SUBSTRING( #p_SourceText, #w_StartPos,((#w_Length - #w_StartPos)+#w_Delimeter_Len))))
IF #w_Delimeter_pos > 0 -- delimeter(s) found, get the value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,(#w_Delimeter_pos - 1)) ))
SET #w_StartPos = #w_Delimeter_pos + #w_StartPos + (#w_Delimeter_Len- 1)
END
ELSE -- No more delimeters, get last value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,((#w_Length - #w_StartPos) + #w_Delimeter_Len)) ))
SELECT #w_Continue = 0
END
INSERT INTO #retTable VALUES( #w_tmp_txt )
END
RETURN
END
Please help me to optimize this query.
you can try this
Insert the list of names to #table(name) and join it to cc
on cc.name=#table.name

Resources