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
Related
I am trying to rewrite the "Query" part by removing the function.
Since the table has more than 800k records, the function is called thrice for all 800k records.
I want to eliminate function and replace with JOIN?
--Function
CREATE FUNCTION [dbo].[MapValue]
(
#ObjCode AS INT,
#ObjName AS VARCHAR(50),
#ObjValue AS INT
)
RETURNS VARCHAR(100)
AS
BEGIN
DECLARE #returnValue AS VARCHAR(100)
SELECT #returnValue = Value FROM dbo.tblMap
WHERE ObjCode = #ObjCode AND ObjName = #ObjName AND ObjValue = #ObjValue
-- Return the result of the function
RETURN #returnValue
END
--Query
SELECT ObjectId, ObjectTypeCode,
dbo.MapValue(4, 'ACode', ACode) AS AType,
dbo.MapValue(4, 'SCode', SCode) AS SCode,
dbo.MapValue(4, 'PCode', PCode) AS PCode
FROM dbo.APoint
WHERE ObjectTypeCode = 1
Here is the query that I came up using JOIN.
Is there any better way to do this? Can we use just ONE join instead of three?
--Modified query
SELECT ObjectId, ObjectTypeCode,
A.Value,
s.Value,
p.Value
FROM dbo.APoint ap
LEFT JOIN tblMap A ON A.ObjCode = 4 AND A.ObjName = 'ACode' AND A.ObjValue = ap.TypeCode
LEFT JOIN tblMap s ON s.ObjCode = 4 AND s.ObjName = 'SCode' AND s.ObjValue = ap.TypeCode
LEFT JOIN tblMap p ON p.ObjCode = 4 AND p.ObjName = 'PCode' AND p.ObjValue = ap.TypeCode
WHERE ObjectTypeCode = 1
You could use case and a single left join:
SELECT ObjectId, ObjectTypeCode,
CASE WHEN map.ObjName = 'ActivityTypeCode' THEN map.Value END AS AType,
CASE WHEN map.ObjName = 'statecode' THEN map.Value END As SCode,
CASE WHEN map.ObjName = 'PriorityCode' THEN map.Value END As PCode
FROM dbo.APoint ap
LEFT JOIN tblMap map ON map.ObjCode = 4
AND map.ObjName IN('ActivityTypeCode', 'statecode', 'PriorityCode')
AND map.ObjValue = ap.ActivityTypeCode
WHERE ObjectTypeCode = 1
GROUP BY ObjectId, ObjectTypeCode
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.
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
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
Stored procedure is taking 5 minutes to execute, but if query of which I have written procedure then it will take on 4 sec. I did google and find parameters sniffing for improving procedures performance so implemented that and now it still taking 3 minutes.
What should I do?
Thanks
Atul yadav
Here is my stored procedure code:
CREATE PROCEDURE [dbo].[get_all]
#para_factor_id int,
#para_factor_client_id int,
#para_factor_customer_id int,
#para_invoice_date_from date,
#para_invoice_date_to date,
#para_invoice_amount_from decimal(18, 2),
#para_invoice_amount_to decimal(18, 2),
#para_invoice_id int,
#para_schedule_number varchar(400),
#para_invoice_number varchar(400),
#para_schedule_id int,
#para_invoice_status_id varchar(50),
#para_marked_for_printing bit,
#para_user_id int,
#para_role_id int,
#para_current_date date,
#para_approval_status_id int = 0,
#para_is_verified int = 0,
#para_load_number varchar(400) = ''
WITH EXEC AS CALLER
AS
BEGIN
DECLARE #var_invoice_order INT
= (SELECT invoice_order
FROM factor_default_settings fds
WHERE fds.factor_id = #para_factor_id);
DECLARE #v_factor_id INT;
DECLARE #v_factor_client_id INT;
DECLARE #v_factor_customer_id INT;
DECLARE #v_invoice_date_from DATETIME;
DECLARE #v_invoice_date_to DATETIME;
DECLARE #v_invoice_amount_from DECIMAL (18, 2);
DECLARE #v_invoice_amount_to DECIMAL (18, 2);
DECLARE #v_invoice_id INT;
DECLARE #v_schedule_number VARCHAR (400);
DECLARE #v_invoice_number VARCHAR (400);
DECLARE #v_schedule_id INT;
DECLARE #v_invoice_status_id VARCHAR (50);
DECLARE #v_marked_for_printing BIT;
DECLARE #v_user_id INT;
DECLARE #v_role_id INT;
DECLARE #v_current_date DATETIME;
DECLARE #v_approval_status_id INT;
DECLARE #v_is_verified INT;
DECLARE #v_load_number VARCHAR (400);
SET #v_factor_id = #para_factor_id;
SET #v_factor_client_id = #para_factor_client_id;
SET #v_factor_customer_id = #para_factor_customer_id;
SET #v_invoice_date_from = #para_invoice_date_from;
SET #v_invoice_date_to = #para_invoice_date_to;
SET #v_invoice_amount_from = #para_invoice_amount_from;
SET #v_invoice_amount_to = #para_invoice_amount_to;
SET #v_invoice_id = #para_invoice_id;
SET #v_schedule_number = #para_schedule_number;
SET #v_invoice_number = #para_invoice_number;
SET #v_schedule_id = #para_schedule_id;
SET #v_invoice_status_id = #para_invoice_status_id;
SET #v_marked_for_printing = #para_marked_for_printing;
SET #v_user_id = #para_user_id;
SET #v_role_id = #para_role_id;
SET #v_current_date = #para_current_date;
SET #v_approval_status_id = #para_approval_status_id;
SET #v_is_verified = #para_is_verified;
SET #v_load_number = #para_load_number;
SET NOCOUNT ON;
SELECT TOP 300
inv.factor_id,
inv.invoice_id,
inv.schedule_id,
inv.term_id,
inv.factor_client_id,
inv.factor_customer_id,
inv_trn.client_name,
SUBSTRING (inv_trn.client_name, 1, 10) AS client_name_10,
inv_trn.customer_name,
SUBSTRING (inv_trn.customer_name, 1, 10) AS customer_name_10,
sch.schedule_number,
sch.payment_method_id,
inv.invoice_number,
inv.po_number,
inv.invoice_date,
inv.est_days_out,
(CASE
WHEN inv.manually_flagged = 1
THEN
1
ELSE
CASE
WHEN inv.flag_days > 0
AND inv.discount_calculation_date >
CONVERT (DATE, '1900/01/01')
THEN
CASE
WHEN DATEDIFF (DAY,
inv.discount_calculation_date,
#v_current_date) > inv.flag_days
THEN
1
ELSE
0
END
ELSE
0
END
END)
AS flag_days_icon,
inv.flag_days,
inv.float_days,
DATEADD (DAY, inv.est_days_out, inv.invoice_date) AS due_date,
inv.invoice_amount,
inv.credit_limit_exceeded_by_client,
inv.po_duplication_status,
inv.advance_amount,
inv.advance_calculation_decision_id,
inv.advance_percent,
inv.advance_calculation_formula,
(CASE inv.deduct_discount_from_advance
WHEN 1 THEN 'Yes'
ELSE 'No'
END)
AS deduct_discount_from_advance,
deduct_discount_from_advance AS deduct_discount,
inv.verified,
inv.estimated_advance_date,
inv_trn.notes_count,
inv.net_term,
inv_trn.total_adjustment_amount,
inv.approval_status_id,
inv_apr_stat.approval_status,
sch.submitted,
sch.made_by_client,
sch.submitted_by_client,
sch.authorized_by_client,
inv.invoice_status_id,
inv_st.invoice_status,
inv.verified,
inv.client_customer_id,
CAST (inv_trn.attachment_count AS INT) AS attachment_count,
ROUND ( (inv.advance_amount - inv_trn.advance_discount), 2)
AS payment_amount,
(inv_trn.escrow_advance - pmt.total_escrow_amount)
AS escrow_amount,
inv_trn.dilution_type_id,
inv_trn.dilution,
inv_trn.dilution_amount,
inv_trn.dilution_term_rate_id,
inv_trn.po_deduction_type_id,
inv.po_id,
inv.fuel_advance,
inv.fuel_adv_with_adj,
inv.dispute
FROM invoices inv
JOIN dbo.fun_get_client_list_profile_management (#v_factor_id,
#v_user_id,
#v_role_id,
1) fun_cl
ON inv.factor_client_id = fun_cl.factor_client_id
JOIN factor_customers fact_cust
ON inv.factor_customer_id = fact_cust.factor_customer_id
AND ( inv.factor_customer_id = #v_factor_customer_id
OR #v_factor_customer_id = 0)
JOIN factor_clients fact_cl
ON inv.factor_client_id = fact_cl.factor_client_id
JOIN client_customers cl_cust
ON inv.client_customer_id = cl_cust.client_customer_id
JOIN invoice_transaction_values inv_trn
ON inv.invoice_id = inv_trn.invoice_id
JOIN schedules sch
ON inv.schedule_id = sch.schedule_id
JOIN invoice_total_payments pmt
ON inv.invoice_id = pmt.invoice_id
AND pmt.payment_type_id = 1
JOIN invoice_approval_status inv_apr_stat
ON inv.approval_status_id = inv_apr_stat.approval_status_id
JOIN invoice_status inv_st
ON inv.invoice_status_id = inv_st.invoice_status_id
LEFT JOIN (SELECT inv_prop.invoice_id,
inv_prop.mark_for_printing
FROM invoice_properties inv_prop
JOIN invoices inv
ON inv_prop.invoice_id = inv.invoice_id
JOIN factor_clients fact_cl
ON inv.factor_client_id =
fact_cl.factor_client_id
JOIN factor_customers fact_cust
ON inv.factor_customer_id =
fact_cust.factor_customer_id
WHERE inv.factor_id = #v_factor_id
AND ( inv.factor_client_id =
#v_factor_client_id
OR #v_factor_client_id = 0)
AND ( inv.factor_customer_id =
#v_factor_customer_id
OR #v_factor_customer_id = 0)
AND fact_cl.active = 1
AND fact_cust.active = 1) AS inv_prop
ON inv_prop.invoice_id = inv.invoice_id
LEFT JOIN invoice_custom_field_values inv_cust_fld_val
ON inv.invoice_id = inv_cust_fld_val.invoice_id
AND inv_cust_fld_val.custom_field_id = 4
WHERE inv.factor_id = #v_factor_id
AND ( inv.factor_client_id = #v_factor_client_id
OR #v_factor_client_id = 0)
AND ( inv.factor_customer_id = #v_factor_customer_id
OR #v_factor_customer_id = 0)
AND fact_cl.active = 1
AND fact_cust.active = 1
AND cl_cust.active = 1
AND inv.invoice_amount BETWEEN #v_invoice_amount_from
AND #v_invoice_amount_to
AND inv.invoice_date BETWEEN #v_invoice_date_from
AND #v_invoice_date_to
AND (inv.invoice_id = #v_invoice_id OR #v_invoice_id = 0)
AND (inv.schedule_id = #v_schedule_id OR #v_schedule_id = 0)
AND sch.schedule_number LIKE '%' + #v_schedule_number + '%'
AND inv.invoice_number LIKE '%' + #v_invoice_number + '%'
AND ( inv.invoice_status_id IN
(SELECT items
FROM dbo.fun_split_string (#v_invoice_status_id,
','))
OR #v_invoice_status_id = '')
-- AND ( ISNULL (inv_prop.mark_for_printing,
-- #v_marked_for_printing) = 0
-- OR #v_marked_for_printing = 0)
AND ( ISNULL (inv_prop.mark_for_printing, 0) =
#v_marked_for_printing
OR #v_marked_for_printing = 0)
AND ( ( #v_role_id IN (2, 6)
AND ( sch.submitted_by_client = 1
OR sch.made_by_client = 0))
OR (#v_role_id IN (3, 10)))
AND ( inv.approval_status_id = #v_approval_status_id
OR #v_approval_status_id = 0)
AND (#v_is_verified = 0 OR inv.verified = 1)
AND ( (inv_cust_fld_val.value LIKE '%' + #v_load_number + '%')
OR #v_load_number = '')
AND inv.approval_status_id IN (1,2)
ORDER BY CASE WHEN #var_invoice_order = 1 THEN inv.invoice_date END ASC,
CASE WHEN #var_invoice_order = 1 THEN inv.invoice_id END ASC,
CASE WHEN #var_invoice_order = 2 THEN inv.invoice_date END DESC,
CASE WHEN #var_invoice_order = 2 THEN inv.invoice_id END DESC
END
There are few things you need to change in the stored proc first.
1) implement indexes (if not there).
2) Do not use function in join "dbo.fun_get_client_list_profile_management"
Instead of that you need to create a temp table/table variable and populate that with dbo.fun_get_client_list_profile_management and then join the temp table/table variable with the actual query. Because when you use function instead of table the function is called for each row results.
3) Same thing you need to do for "inv_prop". Populate a temp table/ table variable with that subquery and then use that table in actual query.
4) Same thing goes for
inv.invoice_status_id IN
(SELECT items
FROM dbo.fun_split_string (#v_invoice_status_id,
','))
OR #v_invoice_status_id = ''
Change it to table variables and instead of "IN" use join
Change those things and let us know the improvement.
It could be that you have a bad plan in the cache for that stored procedure. Recompile it and see if the performance is comparable to the query.
EXEC sp_recompile N'dbo.get_all';