How to compare smallint -1 in SQL - sql

I'm trying to find out if status_id field value of SQL smallint is -1 and and get the records that doesn't have -1 for that field. My stored proc content is as follows:
SELECT DISTINCT TOP 7
cs.case_id,
cs.status_id,
cm.company_name,
cs.created_time,
cs.severity_id,
cs.last_updated_time,
COALESCE(NULLIF(cs.priority,''), 'Medium') AS case_priority
FROM
tblcase cs with (nolock)
INNER JOIN tblcompany cm with (nolock) ON (cs.company_id=cm.company_id)
WHERE
CONVERT(INT, cs.status_id) <> -1 AND
(cs.cas_case_owner = #userId AND cs.is_notify_co = 1) OR
(cs.activity_owner = #userId AND cs.is_notify_ao = 1)
ORDER BY cs.severity_id DESC, cs.case_id ASC
I have tried CONVERT(int, cs.status_id) <> -1), cs.status_id <> CONVERT(smallint, -1), cs.status_id != CAST('-1' AS smallint) and more, but I still keep getting records with -1 as the status_id. Please help me understand what I'm doing wrong before downvoting.

I think you forgot to use brackets for OR operation
cs.status_id <> -1 AND
( -- open
(cs.cas_case_owner = #userId AND cs.is_notify_co = 1)
OR
(cs.activity_owner = #userId AND cs.is_notify_ao = 1)
) -- close
See my another answer about it here - SQL Server Left Join Counting

Try this
SELECT DISTINCT TOP 7
cs.case_id,
cs.status_id,
cm.company_name,
cs.created_time,
cs.severity_id,
cs.last_updated_time,
COALESCE(NULLIF(cs.priority,''), 'Medium') AS case_priority
FROM
tblcase cs with (nolock)
INNER JOIN tblcompany cm with (nolock) ON (cs.company_id=cm.company_id)
WHERE ISNULL(CAST(cs.status_id AS INT),0)<> -1
AND cs.is_notify_co = 1
AND
(
cs.cas_case_owner = #userId
OR
cs.activity_owner = #userId
)
ORDER BY cs.severity_id DESC, cs.case_id ASC

Try this:
SELECT DISTINCT TOP 7
cs.case_id,
cs.status_id,
cm.company_name,
cs.created_time,
cs.severity_id,
cs.last_updated_time,
COALESCE(NULLIF(cs.priority,''), 'Medium') AS case_priority
FROM
tblcase cs with (nolock)
INNER JOIN tblcompany cm with (nolock) ON (cs.company_id=cm.company_id)
WHERE
(
( cs.status_id <> -1 AND cs.cas_case_owner = #userId) AND
( (cs.is_notify_co = 1) OR (cs.is_notify_ao = 1))
)
ORDER BY cs.severity_id DESC, cs.case_id ASC

Related

Update with ROW_NUMBER() OVER

I'm getting error "Windowed functions can only appear in the SELECT or ORDER BY clauses." Can you please help me?
Iknow there are already some solutions to this problem but i don't know how to change it for my case. I am a beginner in SQL.
EDIT: I need to update column [BREAG_GUEST_SEQ] with correct sequence at UPDATE case. Every [BREAG_BGL_ID] have own sequence..
UPDATE G1
SET
G1.[BREAG_BGL_ID] = CASE WHEN G1.[BREAG_BGL_ID] <> G2.GROUP_ID THEN G2.GROUP_ID ELSE G1.[BREAG_BGL_ID] END
,G1.[BREAG_GUEST_SEQ] = CASE WHEN G1.[BREAG_BGL_ID] <> G2.GROUP_ID THEN ROW_NUMBER() OVER(ORDER BY G1.[BREAG_BRE_ID] DESC) + (SELECT ISNULL(MAX(BREAG_GUEST_SEQ), 0) FROM BOS_RESADDGUEST
WHERE DATEPART(YEAR, BREAG_DATEFROM) = DATEPART(YEAR, GETDATE()) AND BREAG_BGL_ID = G2.GROUP_ID)
ELSE G1.[BREAG_GUEST_SEQ] END
FROM
BOS_RESADDGUEST G1
INNER JOIN (
SELECT
[BRE_ID]
,MAX([BRE_DATEFROM]) AS DATEFROM
,MAX([BRE_DATETO]) AS DATETO
,MAX(BGL_ID) AS GROUP_ID
FROM
BOS_RESERVATION
LEFT JOIN BOS_UNIT_LIST ON BUL_ID = BRE_UNIT_ID
LEFT JOIN BOS_UNITTYPE_LIST ON BUL_UNITTYPE_ID = BUT_ID
LEFT JOIN BOS_GROUP_LIST ON BGL_ID = BUT_GROUP_ID
WHERE
BRE_ID = #DIALOG_BRE_ID
GROUP BY [BRE_ID]
) AS G2
ON G2.[BRE_ID] = G1.[BREAG_BRE_ID]
This isnt tested nor do you provide expected results or data structure. However, the issue is quite clear (as SMor has pointed out in the comments). This isnt the best code and the indentation is awful (i want it to be readable with scrolling left or right here on SO).
You most likely have to edit what Im joining on in the update statement at the end, because without further information from you i have to guess what the join predicate ought to be. Im sure you can figure it out from this point forward.
; WITH cte AS
(
SELECT
[BREAG_BRE_ID]
, CASE
WHEN x.[BREAG_BGL_ID] <> x.GROUP_ID THEN x.GROUP_ID
ELSE x.[BREAG_BGL_ID]
END As NewBREAGBGLID
, CASE
WHEN x.[BREAG_BGL_ID] <> x.GROUP_ID THEN ROW_NUMBER()
OVER (ORDER BY x.[BREAG_BRE_ID] DESC) +
(
SELECT ISNULL(MAX(BREAG_GUEST_SEQ), 0)
FROM BOS_RESADDGUEST
WHERE DATEPART(YEAR, BREAG_DATEFROM) = DATEPART(YEAR, GETDATE())
AND BREAG_BGL_ID = x.GROUP_ID
)
ELSE x.[BREAG_GUEST_SEQ]
END AS NewBREAGGUESTSEQ
FROM
(
SELECT g2.*, g1.[BREAG_BGL_ID], g1.[BREAG_BRE_ID], g1.[BREAG_GUEST_SEQ]
FROM
BOS_RESADDGUEST G1
INNER JOIN
(
SELECT
[BRE_ID]
,MAX([BRE_DATEFROM]) AS DATEFROM
,MAX([BRE_DATETO]) AS DATETO
,MAX(BGL_ID) AS GROUP_ID
FROM
BOS_RESERVATION
LEFT JOIN BOS_UNIT_LIST ON BUL_ID = BRE_UNIT_ID
LEFT JOIN BOS_UNITTYPE_LIST ON BUL_UNITTYPE_ID = BUT_ID
LEFT JOIN BOS_GROUP_LIST ON BGL_ID = BUT_GROUP_ID
WHERE
BRE_ID = #DIALOG_BRE_ID
GROUP BY [BRE_ID]
) AS G2
ON G2.[BRE_ID] = G1.[BREAG_BRE_ID]
) AS x
)
UPDATE
[BREAG_BGL_ID] = c.NewBREAGBGLID
, [BREAG_GUEST_SEQ] = c.NewBREAGGUESTSEQ
FROM BOS_RESADDGUEST br
INNER JOIN cte c ON br.[BREAG_BRE_ID] =c.[BREAG_BRE_ID]
You need to move the ROW_NUMBER into a derived table or CTE. You can update this directly, no need to rejoin.
You can also change the subquery to a windowed MAX
Note also that date comparisons should not use functions against the column, as this can affect performance
UPDATE G1
SET
G1.[BREAG_BGL_ID] = CASE WHEN G1.[BREAG_BGL_ID] <> G2.GROUP_ID THEN G2.GROUP_ID ELSE G1.[BREAG_BGL_ID] END
,G1.[BREAG_GUEST_SEQ] = CASE WHEN G1.[BREAG_BGL_ID] <> G2.GROUP_ID THEN G1.rn + G1.MaxSeq ELSE G1.[BREAG_GUEST_SEQ] END
FROM (
SELECT *,
ROW_NUMBER() OVER(ORDER BY G1.[BREAG_BRE_ID] DESC) AS rn,
ISNULL(MAX(CASE WHEN WHERE G1.BREAG_DATEFROM >= DATEFROMPARTS(YEAR(GETDATE()), 1, 1) THEN G1.BREAG_GUEST_SEQ END)
OVER (PARTITION BY G1.BREAG_BGL_ID), 0) AS MaxSeq
FROM
BOS_RESADDGUEST G1
) AS G1
INNER JOIN (
SELECT
[BRE_ID]
,MAX([BRE_DATEFROM]) AS DATEFROM
,MAX([BRE_DATETO]) AS DATETO
,MAX(BGL_ID) AS GROUP_ID
FROM
BOS_RESERVATION
LEFT JOIN BOS_UNIT_LIST ON BUL_ID = BRE_UNIT_ID
LEFT JOIN BOS_UNITTYPE_LIST ON BUL_UNITTYPE_ID = BUT_ID
LEFT JOIN BOS_GROUP_LIST ON BGL_ID = BUT_GROUP_ID
WHERE
BRE_ID = #DIALOG_BRE_ID
GROUP BY [BRE_ID]
) AS G2
ON G2.[BRE_ID] = G1.[BREAG_BRE_ID];

Why is this SQL Server query so slow?

This query takes 4 seconds to run, and it returns about 62000 rows. #c, #p and #a are strings made up of ints, separated by commas or empty, based on what needs to be filtered... unfortunately this can't be changed.
I have tried to run this without the filters and it does not make a difference.
I am fairly new to SQL.
The 3 common table expressions are there to take extra lines of code out.
I would like this to be able to run under a second if at all possible
DECLARE #UId int = '817',
#c varchar(max) = '',
#p varchar(max) = '',
#a varchar(max) = ''
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
IF OBJECT_ID('tempdb..#FilteredData') IS NOT NULL
DROP TABLE #FilteredData;
WITH cteuag AS
(
SELECT
uag.CId,
uag.AuthorIsatIonGroupId,
rn = ROW_NUMBER() OVER (PARTITION BY uag.[UId], uag.CId, uag.AuthorIsatIonGroupId ORDER BY uag.CId)
FROM
uag
WHERE
uag.[UId] = #UId
AND uag.IsDeleted = 0
AND (#c = '' OR (#c LIKE CONVERT(varchar(4), uag.CId)
OR #c LIKE '%,' + CONVERT(varchar(4), uag.CId) + ',%'
OR #c LIKE CONVERT(varchar(4), uag.CId) + ',%'
OR #c LIKE '%,' + CONVERT(varchar(4), uag.CId))
)
),
cteuc AS
(
SELECT
uc.CId,
cteuag.AuthorIsatIonGroupId,
uc.[UId],
i.FirstName + Iif(i.MiddleName IS NOT NULL, ' ' + i.MiddleName + ' ', ' ') + i.Surname AS [AName],
rn = ROW_NUMBER() OVER (PARTITION BY uc.CId, uc.[UId] ORDER BY uc.ModifiedAt)
FROM
uc
INNER JOIN
cteuag ON cteuag.CId = uc.CId
AND uc.IsDeleted = 0
AND (#a = '' OR (#a LIKE CONVERT(varchar(4), uc.[UId])
OR #a LIKE '%,' + CONVERT(varchar(4), uc.[UId]) + ',%'
OR #a LIKE CONVERT(varchar(4), uc.[UId]) + ',%'
OR #a LIKE '%,' + CONVERT(varchar(4), uc.[UId]))
)
INNER JOIN
ui ON ui.[UId] = uc.[UId]
AND ui.IsDeleted = 0
INNER JOIN
i ON i.PId = ui.IndividualId
),
ctel AS
(
SELECT
la.LId,
p.CId,
la.[UId],
rn = ROW_NUMBER() OVER (PARTITION BY la.LId ORDER BY la.DateUpdated DESC)
FROM
la
INNER JOIN
p ON p.Id = la.PId
AND p.IsDeleted = 0
AND la.IsDeleted = 0
AND la.[UId] IS NOT NULL
INNER JOIN
l ON la.LId = l.Id
AND l.IsDeleted = 0
)
SELECT
c.Id AS CId,
c.[Name] AS CName,
u.Id AS AId,
cteuc.AName AS AName,
ctel.LId AS LId,
qh.Id AS QhId,
q.Id AS QId,
q.IsTrue AS isTrue,
p.Id AS PId,
p.[Name] AS PName,
qi.FinalCalculation AS Calculation
INTO
#FilteredData
FROM
c
INNER JOIN
cteuc ON cteuc.CId = c.Id
AND cteuc.Rn = 1
AND c.IsDeleted = 0
LEFT OUTER JOIN
u ON u.Id = Iif(cteuc.AuthorIsatIonGroupId = 1, #UId, cteuc.[UId])
AND u.IsDeleted = 0
LEFT OUTER JOIN
ctel ON ctel.CId = c.Id
AND ctel.Rn = 1
AND ctel.[UId] = u.Id
LEFT OUTER JOIN
(la
INNER JOIN
qla ON qla.LeadActivityId = la.Id
AND la.IsDeleted = 0
AND la.ActivityTypeId = 4
INNER JOIN
qh ON qla.QhId = qh.Id
AND qh.IsDeleted = 0
AND qh.IsReRated = 0
LEFT OUTER JOIN
(q
INNER JOIN
p ON p.Id = q.PId
AND q.IsDeleted = 0
AND p.IsDeleted = 0
) ON q.QhId = qh.Id) ON la.[UId] = u.Id
AND la.LId = ctel.LId
AND
(
#p = ''
OR
(
#p LIKE CONVERT(varchar(4), p.Id)
OR #p LIKE '%,' + CONVERT(varchar(4), p.Id) + ',%'
OR #p LIKE CONVERT(varchar(4), p.Id) + ',%'
OR #p LIKE '%,' + CONVERT(varchar(4), p.Id)
)
)
LEFT OUTER JOIN
(
SELECT
qi1.QId,
SUM(Iif(qi1.Calculation = 0, 0, qi1.Calculation + qi1.Extra)) AS FinalCalculation
FROM qi
WHERE qi1.IsDeleted = 0
GROUP BY QId
) AS qi ON qi.QId = q.Id
WHERE
(
#p = ''
OR
(
p.Id IS NOT NULL
OR qh.Id IS NULL
)
)
SELECT * FROM #FilteredData
EDIT:
just to be clear, the above query does not run any slower than this one:
DECLARE #UId int = '817'
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
IF OBJECT_ID('tempdb..#FilteredData') IS NOT NULL DROP TABLE #FilteredData;
WITH cteuag AS
(
SELECT
uag.CId,
uag.AuthorIsatIonGroupId,
rn = ROW_NUMBER() OVER (PARTITION BY uag.[UId], uag.CId, uag.AuthorIsatIonGroupId ORDER BY uag.CId)
FROM uag
WHERE uag.[UId] = #UId
AND uag.IsDeleted = 0
),
cteuc AS
(
SELECT
uc.CId,
cteuag.AuthorIsatIonGroupId,
uc.[UId],
i.FirstName + Iif(i.MiddleName IS NOT NULL, ' ' + i.MiddleName + ' ', ' ') + i.Surname AS [AName],
rn = ROW_NUMBER() OVER (PARTITION BY uc.CId, uc.[UId] ORDER BY uc.ModifiedAt)
FROM uc
INNER JOIN cteuag ON cteuag.CId = uc.CId
AND uc.IsDeleted = 0
INNER JOIN ui ON ui.[UId] = uc.[UId]
AND ui.IsDeleted = 0
INNER JOIN i ON i.PId = ui.IndividualId
),
ctel AS
(
SELECT
la.LId,
p.CId,
la.[UId],
rn = ROW_NUMBER() OVER (PARTITION BY la.LId ORDER BY la.DateUpdated DESC)
FROM la
INNER JOIN p ON p.Id = la.PId
AND p.IsDeleted = 0
AND la.IsDeleted = 0
AND la.[UId] IS NOT NULL
INNER JOIN l ON la.LId = l.Id
AND l.IsDeleted = 0
)
SELECT
c.Id AS CId,
c.[Name] AS CName,
u.Id AS AId,
cteuc.AName AS AName,
ctel.LId AS LId,
qh.Id AS QhId,
q.Id AS QId,
q.IsTrue AS isTrue,
p.Id AS PId,
p.[Name] AS PName,
qi.FinalCalculation AS Calculation
INTO #FilteredData
FROM c
INNER JOIN cteuc ON cteuc.CId = c.Id
AND cteuc.Rn = 1
AND c.IsDeleted = 0
LEFT OUTER JOIN u ON u.Id = Iif(cteuc.AuthorIsatIonGroupId = 1, #UId, cteuc.[UId])
AND u.IsDeleted = 0
LEFT OUTER JOIN ctel ON ctel.CId = c.Id
AND ctel.Rn = 1
AND ctel.[UId] = u.Id
LEFT OUTER JOIN
(
la
INNER JOIN qla ON qla.LeadActivityId = la.Id
AND la.IsDeleted = 0
AND la.ActivityTypeId = 4
INNER JOIN qh ON qla.QhId = qh.Id
AND qh.IsDeleted = 0
AND qh.IsReRated = 0
LEFT OUTER JOIN
(
q
INNER JOIN p ON p.Id = q.PId
AND q.IsDeleted = 0
AND p.IsDeleted = 0
) ON q.QhId = qh.Id) ON la.[UId] = u.Id
AND la.LId = ctel.LId
LEFT OUTER JOIN
(
SELECT
qi1.QId,
SUM(Iif(qi1.Calculation = 0, 0, qi1.Calculation + qi1.Extra)) AS FinalCalculation
FROM qi
WHERE qi1.IsDeleted = 0
GROUP BY QId
) AS qi ON qi.QId = q.Id
SELECT * FROM #FilteredData
so I know it is not the comma delimited values used as filters, or the leading wild cards.
So it turns out it runs fine on another laptop, which means it was something to do with my laptop. Regardless, looking at the comments, this query could use a lot of refinement

SQL query optimization - make only one join on table

I have a large SQL query, where I need to select some data.
SELECT p.Id, p.UserId, u.Name AS CreatedBy, p.JournalId, p.Title, pt.Name AS PublicationType, p.CreatedDate, p.MagazineTitle, /*ps.StatusId,*/ p.Authors, pb.Name AS Publisher, p.Draft,jns.Name AS JournalTitle,
ISNULL(
ISNULL(
(SELECT StatusId FROM [PublicationsStatus] WHERE StatusDate=
(SELECT MAX(StatusDate) FROM [PublicationsStatus] AS ps WHERE ps.PublicationId = p.Id )),--AND ps.UserId = #UserId ORDER BY StatusDate DESC),
(SELECT TOP(1) ActionId + 6 FROM [PublicationsQuoteSaleLines] AS pqsl WHERE pqsl.PublicationId = p.Id ORDER BY pqsl.Id)
),
1
)AS StatusId
,ISNULL(
(SELECT MAX(StatusDate) FROM [PublicationsStatus] AS ps WHERE ps.PublicationId = p.Id ),--AND ps.UserId = #UserId),
p.CreatedDate
) AS StatusDate
,ISNULL(
(cast((SELECT MAX(StatusDate) FROM [PublicationsStatus] AS ps WHERE ps.PublicationId = p.Id) as date) ),--AND ps.UserId = #UserId),
p.CreatedDate
) AS StDate
,CASE
WHEN ISNULL(
ISNULL(
(SELECT StatusId FROM [PublicationsStatus] WHERE StatusDate=
(SELECT MAX(StatusDate) FROM [PublicationsStatus] AS ps WHERE ps.PublicationId = p.Id )),--AND ps.UserId = #UserId ORDER BY StatusDate DESC),
(SELECT TOP(1) ActionId + 6 FROM [PublicationsQuoteSaleLines] AS pqsl WHERE pqsl.PublicationId = p.Id ORDER BY pqsl.Id)
),
1 ) IN (1, 7, 8) THEN 0
ELSE 1 END AS OrderCriteria
,(SELECT COUNT(*) FROM SentEmails AS se WHERE se.PublicationId = p.Id AND se.EmailType = 1 AND se.UserId = #UserId) AS NumberOfAlerts
,(SELECT COUNT(*) FROM SentEmails AS se WHERE se.PublicationId = p.Id AND se.EmailType = 3 AND se.UserId = #UserId) AS NumberOfReminders
FROM Publications AS p
LEFT JOIN PublicationTypes AS pt ON p.PublicationTypeId = pt.Id
LEFT JOIN Publishers AS pb ON p.PublisherId = pb.Id
LEFT JOIN Journals As jns ON p.JournalId = jns.Id
LEFT JOIN Users AS u ON u.Id = p.UserId
The problem is that the query is slow. AS you can see I have the same thing at OrderCriteria and the StatusId. The StatusDate I'm getting from the same table.
I thought that I could resolve the performance to make only one \
LEFT JOIN
something like this:
LEFT JOIN (
SELECT
PublicationId,
StatusId AS StatusId,
StatusDate AS StatusDate
FROM [PublicationsStatus] WHERE StatusDate=
(
SELECT MAX(StatusDate) FROM PublicationsStatus
)
) AS ps ON ps.PublicationId = p.Id
but I did not get the same results this way.
Can you please advise?
I tried to simplify your query using a few CTE to avoid doing the same subquery multiple times. You can try this out and see if it's still slow.
;WITH MaxStatusDateByPublication AS
(
SELECT
PublicationId = ps.PublicationId,
MaxStatusDate = MAX(ps.StatusDate)
FROM
[PublicationsStatus] AS ps
GROUP BY
PS.PublicationId
),
StatusForMaxDateByPublication AS
(
SELECT
StatusId = PS.StatusId,
M.PublicationId,
M.MaxStatusDate
FROM
MaxStatusDateByPublication AS M
INNER JOIN [PublicationsStatus] AS PS ON
M.PublicationId = PS.PublicationId AND
M.MaxStatusDate = PS.StatusDate
),
SentEmailsByPublicationAndType AS
(
SELECT
S.PublicationID,
S.EmailType,
AmountSentEmails = COUNT(1)
FROM
SentEmails AS S
WHERE
S.EmailType IN (1, 3) AND
S.UserID = #UserId
GROUP BY
S.PublicationID,
S.EmailType
)
SELECT
p.Id,
p.UserId,
u.Name AS CreatedBy,
p.JournalId,
p.Title,
pt.Name AS PublicationType,
p.CreatedDate,
p.MagazineTitle,
p.Authors,
pb.Name AS Publisher,
p.Draft,
jns.Name AS JournalTitle,
COALESCE(MS.StatusId, SL.StatusId, 1) AS StatusId,
ISNULL(MS.MaxStatusDate, P.CreatedDate) AS StatusDate,
ISNULL(CONVERT(DATE, MS.MaxStatusDate), P.CreatedDate) AS StDate,
CASE
WHEN COALESCE(MS.StatusId, SL.StatusId, 1) IN (1, 7, 8) THEN 0
ELSE 1
END AS OrderCriteria,
ISNULL(TY1.AmountSentEmails, 0) AS NumberOfAlerts,
ISNULL(TY3.AmountSentEmails, 0) AS NumberOfReminders
FROM
Publications AS p
LEFT JOIN PublicationTypes AS pt ON p.PublicationTypeId = pt.Id
LEFT JOIN Publishers AS pb ON p.PublisherId = pb.Id
LEFT JOIN Journals As jns ON p.JournalId = jns.Id
LEFT JOIN Users AS u ON u.Id = p.UserId
LEFT JOIN StatusForMaxDateByPublication AS MS ON P.Id = MS.PublicationId
LEFT JOIN SentEmailsByPublicationAndType AS TY1 ON
P.Id = TE.PublicationID AND
TY1.EmailType = 1
LEFT JOIN SentEmailsByPublicationAndType AS TY3 ON
P.Id = TE.PublicationID AND
TY1.EmailType = 3
OUTER APPLY (
SELECT TOP 1
StatusId = ActionId + 6
FROM
[PublicationsQuoteSaleLines] AS pqsl
WHERE
pqsl.PublicationId = P.Id
ORDER BY
pqsl.Id ASC) AS SL
Try to avoid writing the same expression several times (and specially if it involes subqueries inside a column!). Using a few CTEs and proper identing will help readability.
This is a complex query and involves several tables. If your query runs slow it might be for many different reasons. Try executing each subquery on it's own to check if they are slow or not, then try joining them 1 by 1. Indexes by the join columns will probably increase your performance if they don't exist already. Posting the full query execution plan might help.

How can I optimize the SQL query?

I have a query an SQL query as follows, can anybody suggest any optimization for this; I think most of the effort is being done for the Union operation - is there anything else can be done to get the same result ?
Basically I wanna query first portion of the UNION and if for each record there is no result then the second portion need to be run. Please help.
:
SET dateformat dmy;
WITH incidentcategory
AS (
SELECT 1 ord, i.IncidentId, rl.Description Category FROM incident i
JOIN IncidentLikelihood l ON i.IncidentId = l.IncidentId
JOIN IncidentSeverity s ON i.IncidentId = s.IncidentId
JOIN LikelihoodSeverity ls ON l.LikelihoodId = ls.LikelihoodId AND s.SeverityId = ls.SeverityId
JOIN RiskLevel rl ON ls.RiskLevelId = rl.riskLevelId
UNION
SELECT 2 ord, i.incidentid,
rl.description Category
FROM incident i
JOIN incidentreportlikelihood l
ON i.incidentid = l.incidentid
JOIN incidentreportseverity s
ON i.incidentid = s.incidentid
JOIN likelihoodseverity ls
ON l.likelihoodid = ls.likelihoodid
AND s.severityid = ls.severityid
JOIN risklevel rl
ON ls.risklevelid = rl.risklevelid
) ,
ic AS (
SELECT ROW_NUMBER() OVER (PARTITION BY i.IncidentId ORDER BY (CASE WHEN incidentTime IS NULL THEN GETDATE() ELSE incidentTime END) DESC,ord ASC) rn,
i.incidentid,
dbo.Incidentdescription(i.incidentid, '',
'',
'', '')
IncidentDescription,
dbo.Dateconverttimezonecompanyid(closedtime,
i.companyid)
ClosedTime,
incidenttime,
incidentno,
Isnull(c.category, '')
Category,
opencorrectiveactions,
reportcompleted,
Isnull(classificationcompleted, 0)
ClassificationCompleted,
Cast (( CASE
WHEN closedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
IncidentClosed,
Cast (( CASE
WHEN investigatorfinishedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigationFinished,
Cast (( CASE
WHEN investigationcompletetime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigationComplete,
Cast (( CASE
WHEN investigatorassignedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigatorAssigned,
Cast (( CASE
WHEN (SELECT Count(*)
FROM incidentinvestigator
WHERE incidentid = i.incidentid
AND personid = 1588
AND tablename = 'AdminLevels') = 0
THEN 0
ELSE 1
END ) AS BIT)
IncidentInvestigator,
(SELECT dbo.Strconcat(osname)
FROM (SELECT TOP 10 osname
FROM incidentlocation l
JOIN organisationstructure o
ON l.locationid = o.osid
WHERE incidentid = i.incidentid
ORDER BY l.locorder) loc)
Location,
Isnull((SELECT TOP 1 teamleader
FROM incidentinvestigator
WHERE personid = 1588
AND tablename = 'AdminLevels'
AND incidentid = i.incidentid), 0)
TeamLeader,
incidentstatus,
incidentstatussearch
FROM incident i
LEFT OUTER JOIN incidentcategory c
ON i.incidentid = c.incidentid
WHERE i.isdeleted = 0
AND i.companyid = 158
AND incidentno <> 0
--AND reportcompleted = 1
--AND investigatorassignedtime IS NOT NULL
--AND investigatorfinishedtime IS NULL
--AND closedtime IS NULL
),
ic2 AS (
SELECT * FROM ic WHERE rn=1
)
SELECT * FROM ic2
--WHERE rownumber >= 0
-- AND rownumber < 0 + 10
--WHERE ic2.incidentid in(53327,53538)
--WHERE ic2.incidentid = 53338
ORDER BY incidentid DESC
Following is the execution plan I got:
https://www.dropbox.com/s/50dcpelr1ag4blp/Execution_Plan.sqlplan?dl=0
There are several issues:
1) use UNION ALL instead of UNION ALL to avoid the additional operation to aggregate the data.
2) try to modify the numerous function calls (e.g. dbo.Incidentdescription() ) to be an in-lie table valued function so you can reference it using CROSS APPLY or OUTER APPLY. Especially, if those functions referencing a table again.
3) move the subqueries from the SELECT part of the query to the FROM part using CROSS APPLY or OUTER APPLY again.
4) after the above is done, check the execution plan again for any missing indexes. Also, run the query with STATISTICS TIME, IO on to verify that the number of times a table
is referenced is correct (sometimes the execution plan put you in the wrong direction, especially if function calls are involved)...
Since the first inner query produces rows with ord=1 and the second produces rows with ord=2, you should use UNION ALL instead of UNION. UNION will filter out equal rows and since you will never get equal rows it is more efficient to use UNION ALL.
Also, rewrite your query to not use the WITH construct. I've had very bad experiences with this. Just use regular derived tables instead. In the case the query is still abnormally slow, try to serialize some derived tables to a temporary table and query the temporary table instead.
Try alternate approach by removing
(SELECT dbo.Strconcat(osname)
FROM (SELECT TOP 10 osname
FROM incidentlocation l
JOIN organisationstructure o
ON l.locationid = o.osid
WHERE incidentid = i.incidentid
ORDER BY l.locorder) loc)
Location,
Isnull((SELECT TOP 1 teamleader
FROM incidentinvestigator
WHERE personid = 1588
AND tablename = 'AdminLevels'
AND incidentid = i.incidentid), 0)
TeamLeader
from the SELECT. Avoid using complex functions/sub-queries in select.

Query I need to be sped up

I have this query in SQL Server 2005:
SELECT J.JobID,
dbo.tblCustomers.Name AS CustomerName,
J.CustomerJobNumber,
J.JobName,
(CASE WHEN [tblCustomers].[CoreCust] = 0 THEN 'AUXILIARY' ELSE 'CORE' END) AS Department,
J.JobStatusID,
dbo.tblJobTypes.JobType
FROM dbo.tblJobs (NOLOCK) AS J
INNER JOIN dbo.tblCustomers (NOLOCK) ON J.CustomerID = dbo.tblCustomers.CustomerID
INNER JOIN dbo.tblJobTypes (NOLOCK) ON J.JobTypeID = dbo.tblJobTypes.JobTypeID
INNER JOIN dbo.tblDepartments (NOLOCK) ON J.DepartmentId = dbo.tblDepartments.DepartmentID
WHERE (J.Closed = 0)
AND (J.Invoiced = 0)
AND (J.Active = 1)
AND (dbo.fncIsAllPointsDelivered(J.JobID) = 1)
AND (J.DepartmentId <> 2)
This query is taking too long to run, and I know the problem is the UDF - (dbo.fncIsAllPointsDelivered(J.JobID) = 1) -.
The SQL for the UDF is here:
DECLARE #DetailCount int
DECLARE #TrackingCount int
SELECT #DetailCount = COUNT(*)
FROM [dbo].[tblLoadDetails] (NOLOCK)
WHERE JobId = #JobId
SELECT #TrackingCount = COUNT(*)
FROM [dbo].[tblLoadDetails] (NOLOCK)
WHERE JobId = #JobId AND Delivered = 1
IF(#DetailCount = #TrackingCount AND #DetailCount > 0)
RETURN 1
RETURN 0
All of this runs blazingly fast unless the job has a large number of load details in it. I am trying to think of a way to either make the UDF faster or get rid of the need for the UDF, but I am at a loss. I am hoping some of you SQL gurus will be able to help me.
SELECT *
FROM tblJobs j
INNER JOIN
tblCustomers c
ON c.CustomerID = J.CustomerID
INNER JOIN
tblJobTypes jt
ON jt.JobTypeID = J.JobTypeID
INNER JOIN
tblDepartments d
ON d.DepartmentID = J.DepartmentId
WHERE J.Closed = 0
AND J.Invoiced = 0
AND J.Active = 1
AND J.DepartmentId <> 2
AND J.JobID IN
(
SELECT JobID
FROM tblLoadDetails
)
AND J.JobID NOT IN
(
SELECT JobID
FROM tblLoadDetails
WHERE Delivered <> 1
)
Create a composite index on these fields:
tblJobs (Closed, Invoiced, Active) INCLUDE (DepartmentID)
If your tblLoadDetails.Delivered is a bit field, then create the following index:
tblLoadDetail (JobID, Delivered)
and rewrite the last condition as this:
SELECT *
FROM tblJobs j
INNER JOIN
tblCustomers c
ON c.CustomerID = J.CustomerID
INNER JOIN
tblJobTypes jt
ON jt.JobTypeID = J.JobTypeID
INNER JOIN
tblDepartments d
ON d.DepartmentID = J.DepartmentId
WHERE J.Closed = 0
AND J.Invoiced = 0
AND J.Active = 1
AND J.DepartmentId <> 2
AND
(
SELECT TOP 1 Delivered
FROM tblLoadDetails ld
WHERE ld.JobID = j.JobID
ORDER BY
Delivered
) = 1
I'm working this from the top of my head, so I haven't tried this out. But I think you could do this to remove the function. Replace the call to the function with these two clauses. This is assuming that 'Delivered' is a BIT field:
AND EXISTS (SELECT 1 FROM tblLoadDetails WHERE JobID = J.JobID)
AND NOT EXISTS (SELECT 1 FROM tblLoadDetails WHERE JobID = J.JobID AND Delivered = 0)
The AND EXISTS covers the UDF's #DetailCount > 0 check; the AND NOT EXISTS then covers the #DetailCount = #TrackingCount, the assumption I'm making is that you're looking to see if the job exists and everying to do with that job has been delivered. so if there's even one thing that hasn't been delivered, it needs to be excluded.
As mentioned: from top of head, and thus not tested or not profiled. I think I've got the logic right. If not, it should be a simple variation thereof.