Function with in clause in where condition of SP decreasing the procedure performance - sql

I have a procedure that has following functions in where condition:
select col1,col2,col3...
from table1
where
(dbo.GetFilStatus(et.SgDate,et.Speed,(SELECT COUNT(J.JId) FROM tbl_Nt J
inner JOIN tbl_NAssign JN ON JN.NNo =J.NNo
inner JOIN dbo.tbl_CStatus JS ON JS.CStatusID=J.CStatusID
INNER JOIN dbo.tbl_SStatus ss ON ss.SStatusID=JS.SStatusID
WHERE JN.DriID=et.DriID AND ss.SStatusID !=9),et.IgStatus)
in (Select val from Split('A,B,C,D,E',',')))
)
getfilstatus status contains the following code:-
if (#ServerDatetime <= DATEADD(MI,-10, GETDATE()))
BEGIN
IF(#xIgStatus = 'ON')
BEGIN
set #FilStatus= 'NoSignal'
END
ELSE
BEGIN
set #FilStatus= 'Stopped'
end
End
else IF(#xIgStatus = 'ON')
Begin
if(#Speed>5)
begin
if(#JCount<=0)
set #FilStatus='Moving'
else
set #FilStatus='Working'
end
else
begin
set #FilStatus= 'Idle'
end
End
else
Begin
set #FilStatus= 'Stopped'
end
RETURN #FilStatus
GetFilStatus always returns more than 10000 records. Sometimes its more than 100000. Its slowing the final output of query. Currently its taking more than 2 mins to return the output.
I am searching for any other option or any other trick using which the query performance can be increased and I could get the output in seconds.
Any suggestions? Any ideas?

It is better to put the split items in a temp table, as the split function needs to execute each time in the query iterations.
The third parameter has a complex inline query, i have created a temp table for the subset data and filtered necessary data inline.
SELECT S.items AS value
INTO #splited_items
FROM Split('A,B,C,D,E', ',') S;
SELECT Count(J.jid) AS Count_JId,
JN.driid AS DriID
INTO #joined_table
FROM tbl_nt J
INNER JOIN tbl_nassign JN
ON JN.nno = J.nno
INNER JOIN dbo.tbl_cstatus JS
ON JS.cstatusid = J.cstatusid
INNER JOIN dbo.tbl_sstatus ss
ON ss.sstatusid = JS.sstatusid
WHERE ss.sstatusid != 9
GROUP BY JN.driid
SELECT col1,
col2,
col3... from table1
WHERE ( dbo.Getfilstatus(et.sgdate, et.speed, (SELECT count_jid
FROM #joined_table
WHERE driid = et.driid),
et.igstatus)
IN (SELECT value
FROM #splited_items) )

Related

How to remove null values in lat long in sql

I am using this below query and it is giving me an error. The error that I am getting is at the very bottom. The error is caused by lat and long having null values in a few of my transactions. I have put the code in to remove all null from lat and long in each of the 3 tables.
What can I do to get the query working. Query works fine and outputs about 100 rows, till the point that it comes across the first null values and then throws up the error.
SELECT DISTINCT
a.region,
c.Market_zone,
c.delivery_center_zone,
a.period,
a.E3Location_num,
a.dad_name,
a.dad_latitude,
a.dad_longitude,
c.lat,
c.long,
z.latitude,
z.longitude,
a.event_date,
a.units,
a.driver_num,
d.driver_name,
a.delivery_reason,
b.description,
c.wsensor,
a.autowillstatus,
a.delivery_exception,
a.phone_order,
a.cancel,
c.lob,
a.zone,
a.keyindex,
f.exception_reasons,
ACOS(COS(RADIANS(90-a.dad_latitude)) *COS(RADIANS(90-c.lat)) +SIN(RADIANS(90-a.dad_latitude)) *SIN(RADIANS(90-c.lat)) *COS(RADIANS(a.dad_longitude-c.long))) *6371 as kms_from_tank,
CASE
WHEN ACOS(COS(RADIANS(90-a.dad_latitude)) *COS(RADIANS(90-c.lat)) +SIN(RADIANS(90-a.dad_latitude)) *SIN(RADIANS(90-c.lat)) *COS(RADIANS(a.dad_longitude-c.long))) *6371 > 5 then 'N'
ELSE 'Y'
END as Cancelled_on_Tanksite,
ACOS(COS(RADIANS(90-a.dad_latitude)) *COS(RADIANS(90-z.latitude)) +SIN(RADIANS(90-a.dad_latitude)) *SIN(RADIANS(90-z.latitude)) *COS(RADIANS(a.dad_longitude-z.longitude))) *6371 as kms_from_yard,
CASE
WHEN ACOS(COS(RADIANS(90-a.dad_latitude)) *COS(RADIANS(90-z.latitude)) +SIN(RADIANS(90-a.dad_latitude)) *SIN(RADIANS(90-z.latitude)) *COS(RADIANS(a.dad_longitude-z.longitude))) *6371 > 5 then 'N'
ELSE 'Y'
END as Cancelled_at_Yard,
a.E3Location_num + '-' + CAST(a.event_date AS varchar (50)) AS unique_id,
a.size,
CASE
WHEN a.size = '2222' THEN 'MMT'
WHEN a.size = '22222' THEN 'MMT'
WHEN a.size = '0' THEN 'Cyl'
ELSE 'BULK'
END AS Department
FROM
e3.dbo.E3table_1 (nolock)a
LEFT JOIN
(SELECT * FROM openquery(suppro_supw, 'SELECT reason_id, description
FROM DELIVERY_REASON
AT isolation 0')) b ON a.delivery_reason = b.reason_id
LEFT JOIN
(SELECT * FROM [SPPWEBPDNJ04].[Delivery_Customer_Master].[dbo].[Del_Customer_Master]
WHERE lat IS NOT NULL AND long IS NOT NULL) c ON c.Location = a.E3Location_num
LEFT JOIN
(SELECT * FROM openquery(suppro_supw, 'SELECT driver_id, driver_name
FROM DRIVER_HEADER
AT isolation 0')) d ON d.driver_id = a.driver_num
LEFT JOIN
(SELECT * FROM openquery(SUPPRO_SUPW, 'SELECT
cc.full_account
,ee.tank_num
,aa.name
,ee.driver_num
,aa.delivery_hold
,aa.department_code
,dd.size
,bb.exception_reasons
,dd.delivery_type
,ee.event_date
FROM
ACCOUNTS aa,
DELIVERY_EXCEPTION bb,
FULL_ACCOUNT cc,
TRANS_DELIVERY dd,
TRANS_MAIN ee
WHERE
aa.account_num = bb.account_num
AND aa.account_num = cc.account_num
AND aa.account_num = dd.account_num
AND aa.account_num = ee.account_num
AND bb.account_num = cc.account_num
AND bb.account_num = dd.account_num
AND bb.trx_unique_key = dd.trx_unique_key
AND bb.trx_unique_key = ee.trx_unique_key
AND cc.account_num = dd.account_num
AND ee.event_date > "2020-01-01"
and bb.exception_reasons like ''%SECOND%''
AT ISOLATION 0'))f
on rtrim(f.full_account)+'-'+cast(f.tank_num as varchar (3)) = a.E3Location_num
and f.event_date = a.event_date
left join
(Select * FROM e3.dbo.ADDs_Delivery_centers_info (nolock) where latitude is not null and longitude is not null) z
on z.Delivery_center_num = a.cost_center
where
a.driver_num > 0
and truck_num > 0
and a.units >= 0
and a.event_date > '2019-01-01'
and a.Posting_code in (1,2,10,99)
and a.region in ('AB1','BC1','PR1')
and a.dad_latitude is not null
and a.dad_longitude is not null
I'm getting an error:
Msg 3623, Level 16, State 1, Line 1
A domain error occurred
LEFT JOIN allows nulls to come through when the JOIN statement fails to find a match on the right side of the table.
For example, when you
left join
(Select * FROM e3.dbo.ADDs_Delivery_centers_info (nolock)
where latitude is not null and longitude is not null) z
on z.Delivery_center_num = a.cost_center
you will have a null z.latitude when there is no matching z.Delivery_center_num. Same with the other tables.
Either INNER JOIN those tables to guarantee values every time, or handle null possibilities inside every single math operation.
EDIT:
After reviewing the BOL (since my trig is dismal), it looks as if ACOS will throw that exact error if the value is outside the range of -1 to 1. We need to put error handling within the ACOS statements, but it will look horrific within your query, so I recommend you create a UDF to handle it for you cleanly.
For example,
-- Test:
-- if this statement generates an error, replace the NULL in the function
-- with 1. I don't have an old enough server to confirm whether it could handle NULL.
select ACOS(null)
go
create function myACOS (#inp float)
returns float
as begin
declare #ret float
select #ret =
acos( /* check whether our input exceeds the boundaries of ACOS; if it does, replace it with something harmless */
case when #inp < -1 or #inp > 1 then null
else #inp end)
return #ret
end
Then, you update your SELECT statement to use this function instead of the built in ACOS everywhere, so it does your error handling behind the scenes:
SELECT
dbo.myACOS(COS(blah blah blah...)) *6371 as kms_from_tank

SQL Server stored procedure store multiple rows of SELECT statement result into single variable

I have a query with a SELECT statement that will return 2 or more rows as a result. How can I store these rows of data into a variable? Because I need the variable to check whether any of the rows is empty/null. How can I achieve this?
So far I've done this:
BEGIN
SELECT
#AINum = ISNULL(so.U_SI7_DPDocNum, 0), #soDocNum = so.DocNum
FROM
DLN1 doline
INNER JOIN
ORDR so ON doline.BaseRef = so.DocNum
WHERE
doline.DocEntry = #docEntry
WHILE(#AINum IS NOT NULL)
BEGIN
IF(#AINum <= 0)
BEGIN
SELECT #errCode = 003;
RETURN;
END
END
END
UPDATED query using EXISTS
SELECT #errCode = 003
WHERE NOT EXISTS (SELECT so.U_SI7_DPDocNum
FROM DLN1 doline
INNER JOIN ORDR so ON doline.BaseRef = so.DocNum
WHERE doline.DocEntry = #docEntry)
RETURN;
The #AINum will have to store multiple rows of data from the SELECT statement result. #errCode is an output variable.
Thank you.
-- initialize to 0
SELECT #errCode = 0;
-- assign value of 003 if it the DPDocNum is NULL or < 0
SELECT #errCode = 003
FROM DLN1 doline
INNER JOIN ORDR so ON doline.BaseRef = so.DocNum
WHERE doline.DocEntry = #docEntry
AND (so.U_SI7_DPDocNum IS NULL OR so.U_SI7_DPDocNum <= 0)

Temp table throws invalid object name after adding 'SET SHOWPLAN_ALL' Sql Server MS17

I'm fairly new to SQL Server and I've been assigned the task of optimizing some SQL Queries generated by a CMS. After I add the code
SET SHOWPLAN_ALL ON
GO;
and execute the query, the local temp table #tempSecondLevel throws an 'invalid object name' exception in my INSERT INTO statement. You will see a SELECT INTO #tmpSecondLevel statement and then an INSERT INTO #tmpSecondLevel statement. The next statements are for #tmpFirstLevel, and I'm not sure if those are relevant to this question.
use Kentico8_2;
go
set showplan_all on;
go
-- Write revised query here.
DECLARE #ProductNodeGuid uniqueidentifier = '6F6F733D-AE4F-47DC-9BE9-52B967E9F41D'
IF OBJECT_ID('tempdb..#tmpSecondLevel') IS NOT NULL
DROP TABLE #tmpSecondLevel
IF OBJECT_ID('tempdb..#tmpFirstLevel') IS NOT NULL
DROP TABLE #tmpFirstLevel
-- Get all of the items(that go on the secondLevel) that go underneath a heading(on the firstLevel)
-- Get all of the materials that belong to this node that go on the second level
SELECT
NodeLevel = 1
,NodeParentID = CASE WHEN MaterialTypeSelectByMaterial = 0 THEN M.NodeID ELSE M.NodeParentID END
,M.NodeID
,M.MaterialName
,MaterialImage = MV.MaterialVariantImage
,M.NodeAliasPath
,M.Published
,M.NodeGUID
,M.ClassName
,M.NodeOrder
INTO
#tmpSecondLevel
FROM
View_NOF_Material_Joined M
JOIN View_NOF_Product_Joined P
ON P.ProductMaterialNodeGuidList LIKE '%' + CONVERT(nvarchar(36), M.NodeGUID) + '%'
JOIN View_NOF_MaterialType_Joined MT
ON MT.NodeID = M.NodeParentID
LEFT JOIN View_NOF_MaterialVariant_Joined MV
ON MV.NodeParentID = M.NodeID AND MV.NodeOrder = 1 -- always the first
WHERE
P.NodeGUID = #ProductNodeGuid AND MaterialTypeSelectByMaterial = 1
-- Get all of the material variants that belong to this node that go on the second level
INSERT INTO
#tmpSecondLevel
SELECT
NodeLevel = 1
,NodeParentID = CASE WHEN MaterialTypeSelectByMaterial = 0 THEN M.NodeID ELSE M.NodeParentID END
,MV.NodeID
,MaterialName = MV.MaterialVariantName
,MaterialImage = MV.MaterialVariantImage
,MV.NodeAliasPath
,MV.Published
,MV.NodeGUID
,MV.ClassName
,MV.NodeOrder
FROM
View_NOF_MaterialVariant_Joined MV
JOIN View_NOF_Product_Joined P
ON P.ProductMaterialNodeGuidList LIKE '%' + CONVERT(nvarchar(36), MV.NodeGUID) + '%'
JOIN View_NOF_Material_Joined M
ON M.NodeID = MV.NodeParentID
JOIN View_NOF_MaterialType_Joined MT
ON MT.NodeID = M.NodeParentID
WHERE
P.NodeGUID = #ProductNodeGuid
AND MaterialTypeSelectByMaterial = 0
-- Get all of the headings( for the firstLevel) that go above a list of items(on the secondLevel)
-- Get all of the material types that are used as headings
SELECT DISTINCT
NodeLevel = 0
,NodeParentID = NULL
,MT.NodeID
,MaterialName = MT.MaterialTypeName
,MaterialImage = ''
,MT.NodeAliasPath
,MT.Published
,MT.NodeGUID
,MT.ClassName
,MT.NodeOrder
INTO
#tmpFirstLevel
FROM
View_NOF_MaterialType_Joined MT
INNER JOIN #tmpSecondLevel M
ON MT.NodeID = M.NodeParentID
-- Get all of the materials that are used as headings
INSERT INTO
#tmpFirstLevel
SELECT DISTINCT
NodeLevel = 0
,NodeParentID = NULL
,MJ.NodeID
,MaterialName = MJ.MaterialName
,MaterialImage = ''
,MJ.NodeAliasPath
,MJ.Published
,MJ.NodeGUID
,MJ.ClassName
,MTJ.NodeOrder
FROM
View_NOF_Material_Joined MJ
INNER JOIN View_NOF_MaterialType_Joined MTJ
ON MTJ.NodeID = MJ.NodeParentID
INNER JOIN #tmpSecondLevel M
ON MJ.NodeID = M.NodeParentID
-- Put all of the second level items (the details) and first level items (the headings) in the same table
SELECT * FROM #tmpSecondLevel
UNION
SELECT * FROM #tmpFirstLevel
ORDER BY NodeOrder
IF OBJECT_ID('tempdb..#tmpSecondLevel') IS NOT NULL
DROP TABLE #tmpSecondLevel
IF OBJECT_ID('tempdb..#tmpFirstLevel') IS NOT NULL
DROP TABLE #tmpFirstLevel
I predict this is a simple question to ask since I only added two lines of code, but the help is much appreciated. If you have any tips for me about optimizing these queries, that is greatly appreciated as well.
Thanks in advance.
You are getting the error because SET SHOWPLAN_ALL is the command to show Estimated execution plans. Therefore no TSQL commands are actually executed, and thus the #tmpSecondLevel table is never created. So you get an error. This would be the same thing as clicking the "Display Estimated Execution Plan" in SSMS.
You can read about that command here: https://learn.microsoft.com/en-us/sql/t-sql/statements/set-showplan-all-transact-sql
If you want to show the Actual execution replace SHOWPLAN_ALL line with SET STATISTICS XML ON. This will display the actual execution plan when you run the query.

Query taking too much

I am trying to execute updates while my condition returns results, the problem is that when i am testing the query it never finishes.
Here is the query;
While(select COUNT(*) from Agreement as agr where agr.Id in (
select toa.Id from Agreement_TemporaryOnceAgreement as toa where toa.Executed =1)
and agr.EndingDate is null) > 0
begin
DECLARE #AgreementID int;
SET #AgreementID =
(
select top 1 agr.id from Agreement as agr where agr.Id in (
select toa.Id from Agreement_TemporaryOnceAgreement as toa where toa.Executed =1)
and agr.EndingDate is null
)
update Agreement SET EndingDate = (
select tado.Date from TemporaryAgreementsDateOfExecution tado
where tado.AgreementId = CAST(#AgreementID AS INT))
where Agreement.Id = CAST(#AgreementID AS INT);
end;
You don't need a loop. A single update query resembling this should get the job done.
update a
set EndingDate = tado.date
from Agreement a join TemporaryAgreementsDateOfExecution tado
on a.AgreementId = tado.AgreementId
join Agreement_TemporaryOnceAgreement toa
on a.Id = toa.id
where EndingDate is null
and toa.Executed = 1
There might be slight variations depending on the RDBMS you are using.

UDF not returning the same value as select contained in UDF

I created this UDF
CREATE FUNCTION [dbo].[HasExtendedRetentionSamples] (#BoxNumber varchar(20))
RETURNS int
AS
BEGIN
declare #cnt int
set #cnt = 0
select #cnt = (select count(*)
from tFreezerBoxInfo bi
inner join tFreezerDetails fd on fd.boxTrayId = bi.boxTrayId
inner join tncDrugTestListNew dt on dt.labnumber = fd.labnumber
inner join ExtendedRetentionSites a on dt.number = a.number
where boxnumber = 'ND011811001'
and
case isnull([retention],0)
when 0 then proposedDestructionDate
else dateadd(dd,abs([retention]),proposedDestructionDate)
end <> proposedDestructionDate)
return #cnt
END
When I execute the UDF
select dbo.[HasExtendedRetentionSamples] ('ND011811001')
The value 0 is returned, which is incorrect,
When I execute the SQL statement contained in the UDF (replacing #BoxNumber with 'ND011811001')...
select count(*)
from tFreezerBoxInfo bi
inner join tFreezerDetails fd on fd.boxTrayId = bi.boxTrayId
inner join tncDrugTestListNew dt on dt.labnumber = fd.labnumber
inner join ExtendedRetentionSites a on dt.number = a.number
where boxnumber = 'ND011811001'
and
case isnull([retention],0)
when 0 then proposedDestructionDate
else dateadd(dd,abs([retention]),proposedDestructionDate)
end <> proposedDestructionDate
The value 5 is returned, which is correct.
So the big question is WHY????
All the datatype in the joins & case statement are the same.
I would change the function to this, take the count out of the subquery:
CREATE FUNCTION [dbo].[HasExtendedRetentionSamples] (#BoxNumber varchar(20))
RETURNS int
AS
BEGIN
declare #cnt int
set #cnt = 0
select #cnt = count(*)
from tFreezerBoxInfo bi
inner join tFreezerDetails fd on fd.boxTrayId = bi.boxTrayId
inner join tncDrugTestListNew dt on dt.labnumber = fd.labnumber
inner join ExtendedRetentionSites a on dt.number = a.number
where boxnumber = 'ND011811001'
and
case isnull([retention],0)
when 0 then proposedDestructionDate
else dateadd(dd,abs([retention]),proposedDestructionDate)
end <> proposedDestructionDate
return #cnt
END
try changing your CASE statement to:
case
when isnull([retention],0) = 0 then proposedDestructionDate
else dateadd(dd,abs([retention]),proposedDestructionDate)
end <> proposedDestructionDate
I am embarrassed to say, I have found out why this was happening.....
When I created the table ExtendedRetentionSites, it was created & populated with me as the owner. I realized that and recreated the table with DBO as the owner, populating this table, but never dropping the table with the same name that I owned. I ran an insert statement & it inserted into ExtendedRetentionSites that I owned, but this data never made it to the table that DBO owned.
Soooooo when I ran the select script, it used ExtendedRetentionSites that I owned, that had the new row, that would give me the results I was looking for. When I ran the UDF, it used the table that DBO owned, without the new row thus returning nothing or a zero count.
Thanks to everyone who helped me out with this, I will now pull my head out of my ass & get back to work.
Thanks again to all!!!!