Related
I have the following stored procedure. It uses a common table expression and a table valued function. It takes 600 ms to execute this sp. I used dynamic query to avoid the use of a table valued function and the the execution time reduced to around 300 ms. Is it possible to further tune this select query so that the execution time comes down to around 100 ms.
--EXEC Sp_GetTripListByBusinesses 1,2,3,4,5,6,7,8,9,10,11,12,13,14,93', 1, 3, 1
CREATE PROCEDURE [dbo].[Sp_GetTripListByBusinesses]
(
#BusinessID varchar (max),
#TripFlag int,
#TripFrom INT,
#IsAllTrip INT
)
AS
BEGIN
DECLARE #Query nvarchar(max) = '';
SET #Query= '
;With CTE
AS
(
select TripID, count(TripNotesID) as NoteCount from tripnotes WHERE
TripNoteTypeID != 13 group by TripID
)
SELECT TRP.[TripID]
,isnull(TRP.[AssignedTruckerID],0) as AssignedTruckerID
,TRP.[BusinessID] AS BusinessID
,isnull(TRP.[TruckRate],''0.0'') as TruckRate
,TRP.[RequestedDatetime]
,TRP.[ArrivedDatetime]
,TRP.[DepartedDatetime]
,TRP.[TripStatusID]
,isnull(TRP.[IsDelayed] ,0) as IsDelayed
,isnull(TRP.[IsStopped] ,0) as IsStopped
,isnull(TRP.[RepeatUntilStop],'''') AS RepeatUntilStop
,TRP.[ArrivedAtBusinessDatetime]
,TRP.[OrgSiteID]
,TRP.[OriginCustomerID] AS OrgCustomerID
,OS.[SiteName] + '' / '' + ISNULL(OS.[AS400_ID],'''') AS OrgSiteName
,isnull(OS.[SiteLat],''0.0'') as OrgSiteLat
,isnull(OS.[SiteLong],''0.0'') as OrgSiteLong
,isnull(OS.[WaypointsAvl],0) as OrgWaypointsAvl
,isnull(OS.[NearestRoadwayLat],''0.0'') as OrgNearestRoadwayLat
,isnull(OS.[NearestRoadwayLong],''0.0'') as OrgNearestRoadwayLong
,OS.[SiteStatus] AS OrgSiteStatus
,OS.[AS400_ID] AS OrgAS400_ID
,TRP.[DestSiteID]
,TRP.[DestCustomerID] AS DestCustomerID
,DS.[SiteName] + '' / '' + ISNULL(DS.[AS400_ID],'''') AS DestSiteName
,isnull(DS.[SiteLat],''0.0'') as DestSiteLat
,isnull(DS.[SiteLong],''0.0'') as DestSiteLong
,isnull(TRK.FirstName,'''') + '' '' + ISNULL(TRK.[LastName],'''') AS
TruckerName
,isnull(TRK.[TruckType],''A'') as TruckType
,TRK.[Mobile] AS TruckerMobile
,B.[Name] AS BusinessName
,isnull(B.BusinessLat,''0.0'') as BusinessLat
,isnull(B.BusinessLong,''0.0'') as BusinessLong
,B.ContactNo AS BusinessContactNo
,B.Address1 AS BusinessAddress1
,B.Address2 AS BusinessAddress2
,B.City AS BusinessCity
,B.[State] AS BusinessState
,B.Zip AS BusinessZip
,TS.[StatusText]
,CASE WHEN TA.[BillOfLading] IS NULL THEN ''NA'' ELSE ''BOL'' END AS
BillOfLading
,CASE WHEN TA.[ScaleTicket] IS NULL THEN ''NA'' ELSE ''ST'' END AS
ScaleTicket
,CASE WHEN TA.[OriginScaleTicket] IS NULL THEN ''NA'' ELSE ''OST'' END AS
OriginScaleTicket
,TRP.OriginGrossBushels
,TRP.DestGrossBushels
,(SELECT count(TripNotesID) FROM dbo.[TripNotes]) as Notes
,TRP.MainTripID
,Convert(DATE, TRP.[RequestedDatetime]) AS TripDate
,1 as TripOrderNo
,isnull(TRP.IsSiteDelayed ,0) as IsSiteDelayed
,TRP.CommodityID
,C.CommodityName
,CTE.NoteCount
,TRP.InvoiceId
,TRP.OneWayMiles as OneWayMile
,OS.SiteType AS OriginSiteType
,DS.SiteType AS DestSiteType
FROM dbo.[Trip] AS TRP
INNER JOIN dbo.[Site] AS OS ON TRP.OrgSiteID = OS.SiteID
INNER JOIN dbo.[Site] AS DS ON TRP.DestSiteID = DS.SiteID
LEFT JOIN dbo.[Trucker] AS TRK ON TRP.AssignedTruckerID = TRK.TruckerID AND ISNULL(TRK.TruckerStatus,''A'') = ''A''
LEFT JOIN dbo.[Commodity] as C ON TRP.CommodityID = C.CommodityID AND C.RecordStatus = ''A''
INNER JOIN dbo.[Business] AS B ON B.BusinessID = TRP.BusinessID
LEFT JOIN dbo.[TripStatus] AS TS ON TS.TripStatusID = TRP.TripStatusID
LEFT JOIN dbo.[TripAsset] AS TA ON TA.TripID = TRP.TripID
LEFT JOIN CTE ON CTE.TripID = TRP.TripID
WHERE
TRP.RecordStatus=''A'' and
TRP.BusinessID in (' +#BusinessID + ')
ORDER BY TRP.RequestedDatetime ASC';
execute sp_executesql #Query;
END
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.
In my stored procedure I have a case statement that is supposed to test whether a field is numeric and handle that but I get the same error no matter what. I have tried below:
CASE WHEN ISNUMERIC(subScores.ScoreValue) = 1 THEN CAST(subScores.ScoreValue AS REAL) ELSE -1 END
I also used a custom convert function because I have read that isnumeric isn't 100% reliable but that received the same error. I also tried using CONVERT but no success there. I even replaced the isnumeric with 0=1 which should always fail but I still get the same error which I find really strange. This is my error:
Error converting data type nvarchar to real
The values that it is checking look like:
A0, A10, A23, B9, B51, C90, C100, etc
Any ideas for this behavior?
Here is the full procedure. The issue happening on the first case in the where clause after the THEN instead of using -1 its using the actual field. It also contain my usage of ISNUMERIC and my custom function:
#strDemoCodeIds nvarchar(100),
#strYearIds nvarchar(100),
#intRosterSetId int,
#intSchoolId int,
#intTeachId int,
#intGradeId int,
#intDeptId int,
#intCourseId int,
#intPeriodId int,
#strTestInstIds nvarchar(100),
#intTestTypeId int,
#intSubjectId int,
#intScoreTypeId int,
#strSuppScoreTypeId nvarchar(20),
#intClusterScoreTypeId int,
#intClusterColorScoreTypeId int,
#intLocalReportId int,
#strSortMethod nvarchar(20),
#intAnchorYear int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #topYearID INT
DECLARE #subs TABLE (subId int)
IF #intSubjectId < 100
BEGIN
INSERT INTO #subs
SELECT MM_Test_Subjects.pkTestSubjectID
FROM MM_Test_Subjects
WHERE MM_Test_Subjects.fkCSTStrandID = #intSubjectId
END
ELSE
BEGIN
INSERT INTO #subs VALUES (#intSubjectId)
END
DECLARE #years TABLE (yearId int)
INSERT INTO #years
SELECT number FROM itot(#strYearIds, N',')
SET #topYearID = #intAnchorYear
SELECT s.pkStudentID,
s.LastName,
s.FirstName,
s.StudentNumber,
st.fkTestInstanceID,
bands.Description AS 'PL',
bands.Color,
subScoresSupp.scoreValue AS 'SuppScore',
clusters.pkTestClusterID,
clusterScores.ScoreValue,
colors.ScoreValue,
ti.TestInstanceName,
tp.TestPeriodTitle,
subScores.ScoreValue AS 'PLScore',
ScoreType.pkScoreTypeID,
bands.StackPosition AS 'StackPosition'
FROM Students s
INNER JOIN StudentTests st ON s.pkStudentID = st.fkStudentID
INNER JOIN StudentTests baseSt ON s.pkStudentID = baseSt.fkStudentID
AND baseSt.fkGradeID = CASE #intGradeId WHEN 99 THEN baseSt.fkGradeID ELSE #intGradeId END
AND EXISTS (SELECT * FROM StudentScores_Subject baseSubScores WHERE baseSubScores.fkStudentTestID = baseSt.pkStudentTestID
AND baseSubScores.fkTest_SubjectID IN (SELECT subId FROM #subs)
AND baseSubScores.fkScoreTypeID = #intScoreTypeId)
INNER JOIN StudentTestDemographics d ON d.fkStudentTestID = baseSt.pkStudentTestID
INNER JOIN itot(#strDemoCodeIds, N',') tblDemoCodes ON d.fkDemographicCodeID = CASE #strDemoCodeIds WHEN '0' THEN d.fkDemographicCodeID ELSE tblDemoCodes.number END
INNER JOIN StudentScores_Subject subScores ON subScores.fkStudentTestID = st.pkStudentTestID
AND subScores.fkTest_SubjectID IN (SELECT subId FROM #subs)
AND subScores.fkScoreTypeID = #intScoreTypeId
LEFT JOIN PerformanceLevelReportBands bands ON bands.fkPerformanceLevelReportID = #intLocalReportId
LEFT JOIN MMARS_Web_TestInfo_California.dbo.PerfLevelReportBandCutScores cutScores ON cutScores.fkPerformanceLevelReportBandID = bands.pkPerformanceLevelReportBandID
AND cutScores.fkGradeID = #intGradeId
AND cutScores.fkTestSubjectID IN (SELECT subId FROM #subs)
RIGHT JOIN PerfLevelReportBandComponents bandComponents ON bandComponents.fkPerformanceLevelReportBandID = bands.pkPerformanceLevelReportBandID
LEFT JOIN StudentScores_Subject subScoresSupp ON subScoresSupp.fkStudentTestID = st.pkStudentTestID
AND subScoresSupp.fkTest_SubjectID IN (SELECT subId FROM #subs)
AND subScoresSupp.fkScoreTypeID IN (SELECT tblsubScoresSupp.number FROM itot(#strSuppScoreTypeId, N',') tblsubScoresSupp)
LEFT JOIN MM_ScoreTypes ScoreType ON ScoreType.pkScoreTypeID = subScoresSupp.fkScoreTypeID
LEFT JOIN StudentScores_Cluster clusterScores ON clusterScores.fkStudentTestID = st.pkStudentTestID
AND clusterScores.fkTest_SubjectID IN (SELECT subId FROM #subs)
AND clusterScores.fkScoreTypeID = #intClusterScoreTypeId
LEFT JOIN StudentScores_Cluster colors ON colors.fkStudentTestID = st.pkStudentTestID
AND colors.fkTest_SubjectID IN (SELECT subId FROM #subs)
AND colors.fkScoreTypeID = #intClusterColorScoreTypeId
AND colors.fkTest_ClusterID = clusterScores.fkTest_ClusterID
LEFT JOIN MM_Test_Clusters clusters ON clusters.pkTestClusterID = clusterScores.fkTest_ClusterID
INNER JOIN TestInstances ti ON ti.pkTestInstanceID = st.fkTestInstanceID
INNER JOIN TestInstances baseTi ON baseTi.pkTestInstanceID = baseSt.fkTestInstanceID
AND baseTi.fkSchoolYearID = #topYearID
INNER JOIN CAHSEE_TestPeriods tp on tp.pkTestPeriodID = ti.fkTestPeriodID
INNER JOIN CAHSEE_TestPeriods baseTp on baseTp.pkTestPeriodID = baseTi.fkTestPeriodID
INNER JOIN Roster_Students rs ON rs.fkStudentID = s.pkStudentID
INNER JOIN Roster_Groups rg ON rg.fkTestInstanceID = baseTi.pkTestInstanceID
AND ti.pkTestInstanceID IN (SELECT tblTestInstances.number FROM itot(#strTestInstIds, N',') tblTestInstances)
AND baseTi.pkTestInstanceID IN (SELECT tblTestInstances.number FROM itot(#strTestInstIds, N',') tblTestInstances)
AND rg.fkRosterSetID = #intRosterSetId
AND rg.pkRosterGroupID = rs.fkRosterGroupID
AND (rg.fkSchoolID = #intSchoolId OR #intSchoolId = 0)
AND (rg.fkTeacherID = #intTeachId OR #intTeachId = 0)
AND (rg.fkGradeID = baseSt.fkGradeID)
AND (rg.fkDepartmentID = #intDeptId OR #intDeptId = 0)
AND (rg.fkCourseID = #intCourseId OR #intCourseId = 0)
AND (rg.fkPeriodID = #intPeriodId OR #intPeriodId = 0)
WHERE (bandComponents.ScoreValue = subScores.ScoreValue)
OR
((CASE WHEN ISNUMERIC(subScores.ScoreValue) = 1 THEN CAST(subScores.ScoreValue AS REAL) ELSE subScores.ScoreValue END)
BETWEEN (CASE WHEN dbo.TryConvertInt(bandComponents.minScore) = 1 THEN CAST(bandComponents.minScore AS REAL) ELSE bandComponents.minScore END) and
(CASE WHEN dbo.TryConvertInt(bandComponents.maxScore) = 1 THEN CAST(bandComponents.maxScore AS REAL) ELSE bandComponents.maxScore END)/*CAST(ISNULL(bandComponents.maxScore,'0') AS INT)*/)
OR
((CASE WHEN ISNUMERIC(subScores.ScoreValue) = 1 THEN CAST(subScores.ScoreValue AS REAL) ELSE -1 END)
BETWEEN ISNULL(cutScores.minScore,0) and ISNULL(cutScores.maxScore,0))
GROUP BY s.pkStudentID, s.LastName, s.FirstName, s.StudentNumber, st.fkTestInstanceID, bands.Description, bands.Color, subScoresSupp.scoreValue,
clusters.pkTestClusterID, clusterScores.ScoreValue, colors.ScoreValue, ti.TestInstanceName, tp.TestPeriodTitle, subScores.ScoreValue,
ScoreType.pkScoreTypeID, bands.StackPosition
ORDER BY
CASE WHEN #strSortMethod = 'ScoreDesc'
THEN bands.StackPosition END DESC,
CASE WHEN #strSortMethod = 'ScoreAsc'
THEN bands.StackPosition END ASC,
CASE WHEN #strSortMethod = 'ScoreDesc' AND ISNUMERIC(subScores.ScoreValue) = 1
THEN CONVERT(int, subScores.ScoreValue) END DESC,
CASE WHEN #strSortMethod = 'ScoreAsc' AND ISNUMERIC(subScores.ScoreValue) = 1
THEN CONVERT(int, subScores.ScoreValue) END ASC,
s.LastName, s.FirstName, s.pkStudentID, st.fkTestInstanceID
END
The problem is that the case statement is trying to return only one type of value so if the THEN is trying to convert to real and return that back then the ELSE is going to try and do the same so even if there is no cast it is going to try to convert it implicitly. This explains why this works:
DECLARE #scoreValue nvarchar(10)
SET #scoreValue = N'A53'
SELECT CASE WHEN ISNUMERIC(#scoreValue) = 1 THEN CAST(#scoreValue AS REAL) ELSE 1.0 END
But this does not:
DECLARE #scoreValue nvarchar(10)
SET #scoreValue = N'A53'
SELECT CASE WHEN ISNUMERIC(#scoreValue) = 1 THEN CAST(#scoreValue AS REAL) ELSE #scoreValue END
So the key is to use sql_variant so this works:
CASE WHEN ISNUMERIC(#scoreValue) = 1 THEN CAST(#scoreValue AS REAL) ELSE CAST(#scoreValue AS sql_variant) END
I will try for your stuff then find this iss
select CASE WHEN ISNUMERIC(CONVERT(nvarchar,'- 5')) = 1 THEN CAST(CONVERT(nvarchar,'- 5') AS REAL) ELSE -1 END
Isnumeric function should be return of this value true, and convert function should not convert value into real because space in value.
find data of this type in your column like scorevalue.
The problem is not the line you are pointing !
This line is ok :
CASE WHEN ISNUMERIC(subScores.ScoreValue) = 1 THEN CAST(subScores.ScoreValue AS REAL) ELSE -1 END
But in the first case, just after the WHERE clause you have this line
CASE WHEN ISNUMERIC(subScores.ScoreValue) = 1 THEN CAST(subScores.ScoreValue AS REAL) ELSE subScores.ScoreValue END
It cannot work as if ScoreValue is not numeric, you have a case with real datas and varchar data (I guess ScoreValue is varchar)
You have to put once again -1 instead of ScoreValue to make it work
/*Show how isnumeric differs from TRY_CONVERT*/
declare #temp table (dec int, chr char(1) , isnum int,tryconvert int)
declare #loop int = 1
declare #i int
while #loop < 127
begin
set #i = #loop
insert into #temp (dec,chr,isnum,tryconvert)
select #i,char(#i) charac, isnumeric(char(#i)), TRY_CONVERT(int,char(#i))
set #loop = #loop + 1
end
select * from #temp where isnum = 1
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
Our DBA has changed a function to be a procedure, so I am amending some of my procedures to cater for this. I've come across a problem with this in one of my procedures where I have a while loop.
I populate my temp table from the new procedure (#DGContol), and then have the following while loop:
SELECT #MinRcd = MIN(RcdNum) FROM #PortfolioDisclosure
SELECT #MaxRcd = MAX(RcdNum) FROM #PortfolioDisclosure
SET #RcdNum = #MinRcd
WHILE #RcdNum <= #MaxRcd
BEGIN
-- Temporarily assign values to variables
SELECT
#PortfolioGroup = PortfolioCode
, #EndDateTime = MaxPositionDate_DetailedDisclosure
FROM #PortfolioDisclosure
WHERE RcdNum = #RcdNum
INSERT INTO #PositionsTable
SELECT
fi.ID_ISIN AS [Fund_ISIN]
, RTRIM(a.acct_id) AS [Internal_Portfolio_Code]
, a.acct_desc AS [Portfolio/Fund_Name]
, CONVERT(CHAR(11),p.as_of_tms,103) AS [Portfolio_Date]
, a.alt_curr_cde AS [Portfolio_Base_Ccy]
, i.iss_desc AS [Security_Description]
, RTRIM(i.pref_iss_id) AS [Security_ID SEDOL/Internal]
, RTRIM(ia.iss_id) AS [Security_ID ISIN]
, i.denom_curr_cde AS [Denomination_Ccy]
, SUM(p.valval_alt_cmb_amt) OVER (PARTITION BY RTRIM(a.acct_id))
AS [Total_Fund_Value]
, p.orig_quantity AS [Shares/Par_Value]
, p.valval_alt_cmb_amt AS [Market_Value]
, p.fld5_rte AS [Pct_of_NAV]
, SUM(CASE WHEN i.issue_cls1_cde = '010' THEN p.valval_alt_cmb_amt ELSE 0 END) OVER (PARTITION BY a.acct_id)
AS [Cash/Cash_Equivalents]
, i.inc_proj_cmb_rte AS [Coupon_Rate]
, CONVERT(CHAR(11),i.mat_exp_dte,103) AS [Maturity_Date]
FROM dw_position_dg AS p
INNER JOIN #DGControl AS dgc -- [M]onthly, [M]ost recent position
ON dgc.DataGrpCtlNum = p.data_grp_ctl_num
INNER JOIN dw_ivw_acct AS a WITH (NOLOCK)
ON a.acct_id = p.acct_id
INNER JOIN dw_issue_dg AS i WITH (NOLOCK)
ON i.instr_id = p.instr_id
LEFT OUTER JOIN dw_issue_alt_id AS ia WITH (NOLOCK)
ON ia.instr_id = i.instr_id
AND ia.id_ctxt_typ = 'ISIN'
INNER JOIN #PortfolioDisclosure AS fi
ON fi.PortfolioCode = p.acct_id
and fi.MaxPositionDate_DetailedDisclosure >= p.as_of_tms
WHERE RTRIM(a.acct_id) NOT IN ( SELECT xref.internal_value FROM dbo.DP_CrossReference as xref
WHERE xref.Codeset_type_id = 10401
AND xref.Originator_id = 'DataVendorPortfolioExclusion')
-- Clear down variable values
SET #PortfolioGroup = NULL
--SET #StartDateTime = NULL
SET #EndDateTime = NULL
-- Move to next record
SET #RcdNum = #RcdNum + 1
END -- END WHILE LOOP
However this returns lots of duplicate records. If I replace the temp table #DGControl with the original function then I get the correct number of records.
I don't really know what the issue would be or how I could re code this while loop so that using the table #DGControl I get the correct number of records. Can anyone help?
Have you compared the output of SELECT * FROM OldFunction(args..) with EXEC NewStoredProcedure args...? If so, does the data returned look the same or has the duplication crept in when the DBA refactored the function to a proc.
If so you may need to de dupe the output from the sp first then run it through your remaining code. In short, if you are using the same arguments for both but they give you different results then you'll need to go back to the DBA.
You aren't using either of your local variables (#PortfolioGroup, #EndDateTime) in this code. It is just inserting the same record set N number of times. Also, I think you can write this as a single select query without using temp tables or while loops and it will make it less confusing.
Thank you for your feedback. i worked out the issue. My code looks like the following now:
BEGIN
SELECT #MinRcd = MIN(RcdNum) FROM #PortfolioDisclosure
SELECT #MaxRcd = MAX(RcdNum) FROM #PortfolioDisclosure
SET #RcdNum = #MinRcd
WHILE #RcdNum <= #MaxRcd
BEGIN
-- Temporarily assign values to variables
SELECT
#PortfolioGroup = PortfolioCode
, #EndDateTime = MaxPositionDate_DetailedDisclosure
FROM #PortfolioDisclosure
WHERE RcdNum = #RcdNum
-- Insert DGControl record into table based on the MaxPositionDate_DetailedDisclosure from Portfolio Disclosure function
INSERT INTO #DGControl
EXEC InfoPortal..usp_Generic_DGControl '', '', #PortfolioGroup, '1', #EndDateTime, #EndDateTime, #PeriodType, 'M', 'POSITION' -- [M]onthly, [M]ost recent position
-- Insert into #PositionsTable
INSERT INTO #PositionsTable
SELECT
fi.ID_ISIN AS [Fund_ISIN]
, RTRIM(a.acct_id) AS [Internal_Portfolio_Code]
, a.acct_desc AS [Portfolio/Fund_Name]
, CONVERT(CHAR(11),p.as_of_tms,103) AS [Portfolio_Date]
, a.alt_curr_cde AS [Portfolio_Base_Ccy]
, i.iss_desc AS [Security_Description]
, RTRIM(i.pref_iss_id) AS [Security_ID SEDOL/Internal]
, RTRIM(ia.iss_id) AS [Security_ID ISIN]
, i.denom_curr_cde AS [Denomination_Ccy]
, SUM(p.valval_alt_cmb_amt) OVER (PARTITION BY RTRIM(a.acct_id))
AS [Total_Fund_Value]
, p.orig_quantity AS [Shares/Par_Value]
, p.valval_alt_cmb_amt AS [Market_Value]
, p.fld5_rte AS [Pct_of_NAV]
, SUM(CASE WHEN i.issue_cls1_cde = '010' THEN p.valval_alt_cmb_amt ELSE 0 END) OVER (PARTITION BY a.acct_id)
AS [Cash/Cash_Equivalents]
, i.inc_proj_cmb_rte AS [Coupon_Rate]
, CONVERT(CHAR(11),i.mat_exp_dte,103) AS [Maturity_Date]
FROM dw_position_dg AS p
INNER JOIN #DGControl AS dgc
ON dgc.DataGrpCtlNum = p.data_grp_ctl_num
INNER JOIN dw_ivw_acct AS a WITH (NOLOCK)
ON a.acct_id = p.acct_id
INNER JOIN dw_issue_dg AS i WITH (NOLOCK)
ON i.instr_id = p.instr_id
LEFT OUTER JOIN dw_issue_alt_id AS ia WITH (NOLOCK)
ON ia.instr_id = i.instr_id
AND ia.id_ctxt_typ = 'ISIN'
INNER JOIN #PortfolioDisclosure AS fi
ON fi.PortfolioCode = p.acct_id
-- Clear down variable values
SET #PortfolioGroup = NULL
--SET #StartDateTime = NULL
SET #EndDateTime = NULL
-- Clear down #DGControl table to allow new record to be inserted
DELETE FROM #DGControl
-- Move to next record
SET #RcdNum = #RcdNum + 1
END -- END WHILE LOOP
Adding the execution of the stored proc usp_Generic_DGControl at the beginning and then clearing it down after each loop stopped the duplication of the records.