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.
I am trying to show my PT.TotalAmount(money) field in decimal format upto 3 decimal places. I tried using
CAST(ROUND(123.4567, 3) AS MONEY)
But I get error:
An object or column name is missing or empty. For SELECT INTO
statements, verify each column has a name. For other statements, look
for empty alias names. Aliases defined as "" or [] are not allowed.
Change the alias to a valid name.
Here is my SP:
Select PT.[ID] 'TransactionID', PT.BatchNumber, PT.SequenceNumber, PT.TransactionDate,
PT.TerminalID ,CAST(ROUND(PT.TotalAmount, 2) AS MONEY), PT.TransactionTypeID, TT.TransactionType,
PT.PAN 'EmbossLine',PT.PreBalanceAmount, PT.PostBalanceAmount, RefTxnID,
SettlementDate,PaidCash, CreditAmount, DiscountAmount,
RefPAN, PT.Remarks, ' ' + CashierCard as 'SupervisorCard',St.StoreID
into #Temp
from POS_Transactions PT inner join TransactionType TT on TT.TransactionTypeID = PT.TransactionTypeID
inner join Staff St on St.CardNumber=PT.CashierCard
where
PT.[ID] not in (Select distinct isnull(TransactionID,0) from Testcards)
and (PT.TransactionDate >= #DateFrom)
and (PT.TransactionDate < #DateTo)
and (PT.TransactionTypeID = #TransactionTypeID or #TransactionTypeID = -999)
select T.*, ' '+ C.EmbossLine as 'EmbossLine', ' '+ C.EmbossLine as 'EmbossLine1',
isnull(C.FirstName,'') +' '+ isnull(C.LastName,'') 'EmbossName',C.FirstName,C.LastName,City.CityName,Country.CountryName,Country.CurrencyName, PM.MerchantID , PM.MerchantName1, C.AccountNumber, C.VehicleNumber
from #Temp T
inner join Card C on C.EmbossLine= T.EmbossLine
inner join Terminal on Terminal.TerminalID = T.TerminalID
inner join Merchant PM on PM.MerchantID = Terminal.MerchantID
inner join City on City.CityID = PM.CityID
inner join Country on Country.CountryID = PM.CountryID
where C.Status <>'E3'
and C.CardID not in (Select distinct isnull(CardID,0) from Testcards)
and (PM.MerchantID =#MerchantID or #MerchantID='-999')
and (C.EmbossLine like '%'+#EmbossLine+'%' or #EmbossLine like '-999')
and (C.FirstName like '%'+#FirstName+'%' or #FirstName like '-999')
and (C.LastName like '%'+#LastName+'%' or #LastName like '-999')
and (PM.CountryID = #CountryID or #CountryID ='-999')
and(PM.CityID = #CityID or #CityID ='-999')
order by T.TransactionDate, MerchantName1, T.BatchNumber, T.SequenceNumber
drop table #Temp
Why am I getting this error? Whats wrong in conversion? When I simply write PT.TotalAmount in my Select statement, the query is completed successfully
Just add any alias name for CAST(ROUND(PT.TotalAmount, 2) AS MONEY) as roundedamont(whatever column name you want) in your sql query.
Select * into statement require each column have distinct name
Select PT.[ID] 'TransactionID', PT.BatchNumber, PT.SequenceNumber, PT.TransactionDate,
PT.TerminalID ,CAST(ROUND(PT.TotalAmount, 2) AS MONEY)<----Give Alias HERE
Can you give me some pointers (or point in the right direction on what search terms for google)? In a stored procedure I have a parameter #TAG (string). I receive '(14038314,14040071)' (for example) from another application that cannot be altered. In the stored procedure, I need to split apart '(14038314,14040071)' to put quotes around each string value, rebuild it, strip out the outer quotes,strip out the parens and pass it to #TAG in the query below so that it looks like the line commented out below?
SELECT
V.NAME AS VARIETY, TAGID
FROM
mfinv.dbo.onhand h
INNER JOIN
mfinv.dbo.onhand_tags t on h.onhand_id = t.onhand_id
INNER JOIN
mfinv.dbo.onhand_tag_details d on t.onhand_tag_id = d.onhand_tag_id
INNER JOIN
mfinv.dbo.FM_IC_PS_VARIETY V ON V.VARIETYIDX = d.VARIETYIDX
LEFT JOIN
mfinv.dbo.FM_IC_TAG TG ON TG.TAGIDX = t.TAGIDX
WHERE
h.onhand_id = (SELECT onhand_id FROM mfinv.dbo.onhand
WHERE onhand_id = IDENT_CURRENT('mfinv.dbo.onhand'))
AND TG.ID IN (#TAG)
--AND TG.ID IN ('14038314','14040071')
You can Use Dynamic SQL Like This
DECLARE #TAG Nvarchar(MAX)='14038314,14040071'
set #TAG=''''+REPLACE(#TAG,',',''',''')+''''
--select #TAG
DECLARE #SQL NVARCHAR(MAX)=N'
Select V.NAME AS VARIETY, TAGID
FROM mfinv.dbo.onhand h
INNER JOIN mfinv.dbo.onhand_tags t on h.onhand_id = t.onhand_id
INNER JOIN mfinv.dbo.onhand_tag_details d on t.onhand_tag_id = d.onhand_tag_id
INNER JOIN mfinv.dbo.FM_IC_PS_VARIETY V ON V.VARIETYIDX = d.VARIETYIDX
LEFT JOIN mfinv.dbo.FM_IC_TAG TG ON TG.TAGIDX = t.TAGIDX
WHERE h.onhand_id = (SELECT onhand_id FROM mfinv.dbo.onhand
WHERE onhand_id = IDENT_CURRENT (''mfinv.dbo.onhand''))
AND TG.ID IN ('+#TAG+')'
PRINT #SQL
EXEC (#SQL)
Here's what I did. Thank you all for responding. Thanks to dasblinkenlight for answering "How to replace first and last character of column in sql server?". Thanks to SQLMenace for answering "How Do I Split a Delimited String in SQL Server Without Creating a Function?".
Here's how I removed parenthesis:
#Tag nvarchar(256)
SET #Tag = SUBSTRING(#Tag, 2, LEN(#Tag)-2)
Here's how I split and rebuilt #Tag:
AND TG.ID in
(
SELECT SUBSTRING(',' + #Tag + ',', Number + 1,
CHARINDEX(',', ',' + #Tag + ',', Number + 1) - Number -1)AS VALUE
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + #Tag + ',') - 1
AND SUBSTRING(',' + #Tag + ',', Number, 1) = ','
)
I have a search form that is allowing you to enter any information you want into it and perform the search on that data.
Here is what the form looks like:
My goal is to allow them to enter a start and end date. However, they could chose to only enter one or the other. In this case, I need my stored procedure to handle it correctly.
If the end date is empty, the end date will just become today's date. If the start date is empty, we could do any date (sqls default date for example which would include everything.
The awardDate is that field that is being searched. If both dates are entered, then it would need to be the range between start and end
Below is my current stored procedure:
SELECT DISTINCT A.[awardID],
A.[awardDescription],
convert(varchar,cast(A.[awardValue] as money),1) as awardValue,
CONVERT (VARCHAR (10), A.[awardDate], 101) AS awardDate,
CONVERT (VARCHAR (10), A.[timestamp], 101) AS timestamp,
B.[FirstName] + ' ' + B.[LastName] as empName,
B.[ntid] AS empNTID,
C.[awardType] as awardTypeName,
D.[locationName],
E.[awardStatusName]
FROM taxTracker AS A
INNER JOIN
empTable AS B
ON A.[employee] = B.[empID]
INNER JOIN
taxTrackerAwardTypes AS C
ON A.[awardType] = C.[awardTypeID]
INNER JOIN taxTrackerLocations as D
ON A.[awardLocation] = D.[id]
INNER JOIN taxTrackerStatuses as E
ON A.[awardStatus] = E.[awardStatusID]
WHERE ((A.[employee] IN (SELECT ParamValues.x2.value('empID[1]', 'VARCHAR(60)')
FROM #employees.nodes('/employees/employee') AS ParamValues(x2)))
OR (ISNULL(#awardType, '') <> ''
AND A.[awardType] LIKE '%' + #awardType + '%')
OR (ISNULL(#awardStatus, '') <> ''
AND A.[awardStatus] LIKE '%' + #awardStatus + '%'))
FOR XML PATH ('details'), TYPE, ELEMENTS, ROOT ('root');
where (awarddate >= #startdate or #stardate is null)
and (awarddate <= isnull(#enddate, getdate())
Awarddate between isnull (#startdate, '19000101') and isnull (#enddate, getdate ())