I also need help speeding up this query, take 25min on LIVE db, 1 second on TEST db.
This is after some modifications, originally I was querying the db server several hundred times to get the data from a php page using a while loop for each row of the main query, then I tried making a procedure using a temp table to return the data, I canceled the execution after 45min. So then I tried this.
I imagine one could do a inner join on the "select top 1 from NurQueryResults" queries but I couldn't figure it out. I got results but not the most recent at the top, I did an "order by t1.time, t2.time, t3.time...." it did have t1 most recent result at top though, and it returned more results than it should have.
SELECT o.VisitID AS VisitID,
(SELECT TOP 1 Response
FROM NurQueryResults
WHERE QueryID = 'OEDTCAT'
AND VisitID = o.VisitID
ORDER BY DateTime DESC) AS PPN,
(SELECT TOP 1 Response
FROM NurQueryResults
WHERE QueryID = 'OEDTMEAT'
AND VisitID = o.VisitID
ORDER BY DateTime DESC) AS MEAT,
(SELECT TOP 1 Response
FROM OeOrderQueries
WHERE QueryID = 'OESPMOD'
AND VisitID = o.VisitID
ORDER BY RowUpdateDateTime DESC) AS SPMOD,
(SELECT TOP 1 Response
FROM OeOrderQueries
WHERE QueryID = 'OERT3'
AND VisitID = o.VisitID
ORDER BY RowUpdateDateTime DESC) AS SPMOD2,
(SELECT TOP 1 Response
FROM NurQueryResults
WHERE QueryID = 'OEDTDECUB'
AND VisitID = o.VisitID
ORDER BY DateTime DESC) AS DECUB,
(SELECT TOP 1 Response
FROM NurQueryResults
WHERE QueryID = 'OEALL2'
AND VisitID = o.VisitID
ORDER BY DateTime DESC) AS FOODALL,
o.OrderedProcedureName,
o.OrderDateTime,
a.RoomID,
a.BedID,
a.Name,
a.Sex,
DATEDIFF(year, a.ComputedBirthDateTime, GETDATE()) AS Age
FROM OeOrders o
INNER JOIN AdmVisits a
ON o.VisitID = a.VisitID
AND o.Category = 'DIET'
AND o.StatusChoice = 'S'
AND a.Status = 'ADM IN'
ORDER BY o.VisitID,
o.OrderDateTime DESC
I'm just wondering, how would the results be when you do something like this:
SELECT
o.VisitID AS VisitID,
PPN.Response
FROM OeOrders o
JOIN AdmVisits a
ON o.VisitID = a.VisitID
AND o.Category = 'DIET'
AND o.StatusChoice = 'S'
AND a.Status = 'ADM IN'
join
(
SELECT
Response,
VisitID,
ROW_NUMBER() OVER(ORDER BY DateTime DESC) AS MyRowNumber
FROM NurQueryResults
WHERE QueryID = 'OEDTCAT'
) AS PPN
on o.VisitID = PPN.VisitID
and PPN.MyRowNumber = 1
This is not your entire query, but just the first sub-query which gets column PPN.
If you change all sub-queries like this JOIN, does it have any performance gain?
Turn on the Execution Plan and check out the results. It may show that you are missing an index or something of that nature.
Speaking of indexes check the fragmentation of the indexes involved and if they're high consider rebuilding them. I have had quite a lot of success recently in getting procedures that took 2min to run on production to execute in 1-3 seconds by following the above process.
Related
Could you please advise how can achieve below?
I'm joining below subquery (quote_history) with quote table (q.).
Just to note, I'm getting 2 rows for each quote_ref.
Currently it's joining by quote_ref, but I'd like to add CASE that if q.quote_description = 'XYZ' then join also on quote_history.rown = 1 (I want only 1st row in this case), else ignore this another AND (leave only by quote_ref)
eg. for q.quote_description = 'XYZ' I'd like to get something like
LEFT JOIN (SELECT quote_ref
,activity_date_key
,quote_status_key
,ROW_NUMBER() OVER (PARTITION BY quote_ref ORDER BY activity_date_key) rown
,COUNT(*) OVER (PARTITION BY quote_ref, quote_status_key) max_rown
FROM [...]
) quote_history
ON q.quote_ref = quote_history.quote_ref
AND quote_history.rown = 1 --this is only when q.quote_description = 'XYZ'
but for other cases (q.quote_description <> 'XYZ') I want only
LEFT JOIN (SELECT quote_ref
,activity_date_key
,quote_status_key
,ROW_NUMBER() OVER (PARTITION BY quote_ref ORDER BY activity_date_key) rown
,COUNT(*) OVER (PARTITION BY quote_ref, quote_status_key) max_rown
FROM [...]
) quote_history
ON q.quote_ref = quote_history.quote_ref
Alternatively (but it's uglier solution I think) I can also get something like
JOIN [...]
ON q.quote_ref = quote_history.quote_ref
AND IF q.quote_description = 'XYZ' THEN quote_history.rown in (1)
ELSE quote_history.rown in (1,2)
You can use:
ON q.quote_ref = quote_history.quote_ref AND
(quote_history.rown = 1 or q.quote_description <> 'XYZ')
Note: This assumes that q.quote_description is never NULL. The logic can be adjusted for this if necessary (otherwise it just complicates the idea).
I am trying to query some 'job' databases where I need the last datetime value for each operation_service in a given job. I also need to know whether the operation is complete or not.
SELECT Job.Job, Job_Operation.Operation_Service, Job_Operation.Sequence,
MAX(Job_Operation_Time.Last_Updated) AS 'Last_Updated', Job_Operation_Time.Operation_Complete
FROM Job_Operation
LEFT JOIN Job ON Job.Job = Job_Operation.Job
LEFT JOIN Job_Operation_Time ON Job_Operation_Time.Job_Operation = Job_Operation.Job_Operation
WHERE Job.Status = 'Active'
GROUP BY Job.Job, Job_Operation.Operation_Service, Job_Operation.Sequence, Job_Operation_Time.Last_Updated, Job_Operation_Time.Operation_Complete
ORDER BY Job.Job, Sequence
A snippet of some results here:
What I would like is a query that returns all the highlighted records but does not return the records with a red line through the job field. NULL values are possible for both Operation_Complete and Last_Updated.
use row_number()
with cte as
(
SELECT Job.Job, Job_Operation.Operation_Service, Job_Operation.Sequence,
(Job_Operation_Time.Last_Updated) AS 'Last_Updated', Job_Operation_Time.Operation_Complete
,row_number()over(partition by Job_Operation.Operation_Service order by Job_Operation_Time.Last_Updated desc) rn
FROM Job_Operation
LEFT JOIN Job ON Job.Job = Job_Operation.Job
LEFT JOIN Job_Operation_Time ON Job_Operation_Time.Job_Operation = Job_Operation.Job_Operation
WHERE Job.Status = 'Active'
) select * from cte where rn=1
select DAC.LocationCode, DAC.Description, ReqApp.Rank, App.Approver as UserName,
CASE WHEN app.Approver = app.AlternateApprover THEN ''
ELSE AltApp.AlternateApprover END As AltApprover,
ISNULL(CONVERT(Varchar,AltApp.FromDate,101),'')AS FromDate,
ISNULL(CONVERT(Varchar,AltApp.ToDate,101),'')AS ToDate
from tblAPAlternateApprovers App
INNER JOIN tblAPAlternateApprovers AltApp
ON App.ID = AltApp.ID
INNER JOIN tblAPReqLocations DAC
ON App.tblAPReqLocationsID = DAC.ID
INNER JOIN tblAPReqApprover ReqApp
ON App.Approver = ReqApp.Approver AND
App.tblAPReqLocationsID = ReqApp.LocationID
ORDER BY DAC.LocationCode ASC, ReqApp.Rank asc
Output
When SQL Adds an 'alternate approver' (for purchase orders), it creates an additional record for the actual approver. So, trying to find a way to show only 1 record for those approvers that also have alternates. i.e. 'jlhayes' has 2 records. One with an alternate and one without. For these records, I want to only see the ones that have an alternate.Thank you for your help. I've spend a couple hours and out of ideas.
You can wrap AltApprover case statement in max(AltApprover) and group by DAC.LocationCode, DAC.Description, ReqApp.Rank, App.Approver and do similarly for FromDate and ToDate:
select DAC.LocationCode, DAC.Description, ReqApp.Rank, App.Approver as UserName,
max(CASE WHEN app.Approver = app.AlternateApprover THEN ''
ELSE AltApp.AlternateApprover END) As AltApprover,
max(ISNULL(CONVERT(Varchar,AltApp.FromDate,101),'')) AS FromDate,
max(ISNULL(CONVERT(Varchar,AltApp.ToDate,101),'')) AS ToDate
from tblAPAlternateApprovers App
INNER JOIN tblAPAlternateApprovers AltApp
ON App.ID = AltApp.ID
INNER JOIN tblAPReqLocations DAC
ON App.tblAPReqLocationsID = DAC.ID
INNER JOIN tblAPReqApprover ReqApp
ON App.Approver = ReqApp.Approver AND
App.tblAPReqLocationsID = ReqApp.LocationID
GROUP BY DAC.LocationCode, DAC.Description, ReqApp.Rank, App.Approver
ORDER BY DAC.LocationCode ASC, ReqApp.Rank asc
I have a record set where some of the rows are duplicated. In particular, they are duplicated for the last three rows of the record set. Of the entire four rows, the correct result set that I desire would include the first row and the last row. I desire this because for a particular SARAPPD_TERM_CODE_ENTRY, the record needed is the one where the SARAPPD_SEQ_NO value is at its max. So, the first row because for that particular term, the sequence number is maxed at one and the last row because the sequence number is maxed at six. Image and query are below.
select ppd.sarappd_seq_no, ppd.sarappd_term_code_entry, ppd.sarappd_apdc_code,
dap. dap.saradap_term_code_entry,
spri.spriden_id,
t.sgbstdn_astd_code, t.*
from sgbstdn t
left join spriden spri on t.sgbstdn_pidm = spri.spriden_pidm
left join saradap dap on spri.spriden_pidm = dap.saradap_pidm
join sarappd ppd on dap.saradap_pidm = ppd.sarappd_pidm
where t.sgbstdn_astd_code not in ('AS', 'DS', 'WD', 'SU', 'LA')
and t.sgbstdn_stst_code = 'AS'
and spri.spriden_change_ind is null
and spri.spriden_id = '123456789'
and (ppd.sarappd_apdc_code = 25 or ppd.sarappd_apdc_code = 30
or ppd.sarappd_apdc_code =35)
and ppd.sarappd_term_code_entry = dap.saradap_term_code_entry
--where b.sarappd_term_code_entry = ppd.sarappd_term_code_entry)
order by ppd.sarappd_term_code_entry
I believe this is a simple "where this = ( select max() ) type of query but I've been trying some different things and nothing is working. I'm not getting the results I want. So with that said, any help on this would be greatly appreciated. Thanks in advance.
You could use ROW_NUMBER()
;WITH cte
AS
(select
ROW_NUMBER() OVER (PARTITION BY SARAPPD_TERM_CODE_ENTRY ORDER BY SARAPPD_SEQ_NO DESC) AS RN
ppd.sarappd_seq_no,
ppd.sarappd_term_code_entry,
ppd.sarappd_apdc_code,
dap. dap.saradap_term_code_entry,
spri.spriden_id,
t.sgbstdn_astd_code, t.*
from sgbstdn t
left join spriden spri on t.sgbstdn_pidm = spri.spriden_pidm
left join saradap dap on spri.spriden_pidm = dap.saradap_pidm
join sarappd ppd on dap.saradap_pidm = ppd.sarappd_pidm
where t.sgbstdn_astd_code not in ('AS', 'DS', 'WD', 'SU', 'LA')
and t.sgbstdn_stst_code = 'AS'
and spri.spriden_change_ind is null
and spri.spriden_id = '123456789'
and (ppd.sarappd_apdc_code = 25 or ppd.sarappd_apdc_code = 30
or ppd.sarappd_apdc_code =35)
and ppd.sarappd_term_code_entry = dap.saradap_term_code_entry
order by ppd.sarappd_term_code_entry) a
SELECT *
FROM cte WHERE rn = 1
I need some help optimizing a MSSQL view that is, honestly, a little too much complex for my knowledge.
The view is working good but I would like to rewrite it using less subqueries or a better structure in order to simplify it and use less server resources.
The main issue is a CASE WHEN with the same subquery repeated 4 times... I tried to understand if I can put it in a variable and use it for the CASE instead of repeating the query each time but it seems not possible to me...
Here's the query
SELECT dbo.MACCHINE.id_macchina, [...] dbo.VIEW_CANTIERI.indirizzo,
(SELECT TOP (1) data_fine
FROM dbo.MANUTENZIONI
WHERE (id_macchina = dbo.MACCHINE.id_macchina)
ORDER BY data_fine DESC)
AS ultima_manutenzione,
DATEDIFF(day,
(SELECT TOP (1) data_fine
FROM dbo.MANUTENZIONI AS MANUTENZIONI_1
WHERE (id_macchina = dbo.MACCHINE.id_macchina)
ORDER BY data_fine DESC), GETDATE())
AS data_diff,
(CASE WHEN
stato = 0
THEN 'GREY'
WHEN stato = 2
THEN 'BLACK'
WHEN stato = 1
THEN
(CASE WHEN
DATEDIFF(day,
(SELECT TOP (1) data_fine
FROM dbo.MANUTENZIONI AS MANUTENZIONI_1
WHERE (id_macchina = dbo.MACCHINE.id_macchina)
ORDER BY data_fine DESC), GETDATE()) >= 90
THEN 'RED'
WHEN
DATEDIFF(day,
(SELECT TOP (1) data_fine
FROM dbo.MANUTENZIONI AS MANUTENZIONI_1
WHERE (id_macchina = dbo.MACCHINE.id_macchina)
ORDER BY data_fine DESC), GETDATE()) >= 80
THEN 'ORANGE'
WHEN
DATEDIFF(day,
(SELECT TOP (1) data_fine
FROM dbo.MANUTENZIONI AS MANUTENZIONI_1
WHERE (id_macchina = dbo.MACCHINE.id_macchina)
ORDER BY data_fine DESC), GETDATE()) >= 60
THEN 'YELLOW'
WHEN
(SELECT TOP (1) data_fine
FROM dbo.MANUTENZIONI AS MANUTENZIONI_1
WHERE (id_macchina = dbo.MACCHINE.id_macchina)
ORDER BY data_fine DESC) IS NULL
THEN 'RED' ELSE 'GREEN'
END)
END)
AS colore FROM dbo.MACCHINE INNER JOIN
dbo.MACCHINE_MODELLI ON dbo.MACCHINE.id_modello = dbo.MACCHINE_MODELLI.id_modello INNER JOIN
dbo.MACCHINE_TIPOLOGIE ON dbo.MACCHINE_MODELLI.id_tipologia = dbo.MACCHINE_TIPOLOGIE.id_tipologia INNER JOIN
dbo.VIEW_CANTIERI ON dbo.MACCHINE.id_cantiere = dbo.VIEW_CANTIERI.id_cantiere INNER JOIN
dbo.MACCHINE_PRODUTTORI ON dbo.MACCHINE_MODELLI.id_produttore = dbo.MACCHINE_PRODUTTORI.id_produttore INNER JOIN
dbo.CLIENTI ON dbo.VIEW_CANTIERI.id_cliente = dbo.CLIENTI.id_cliente WHERE (dbo.MACCHINE._del = 'N')
Any suggestion is really appreciated, if you need more information about the db I will try to provide it...
I notice you are using TOP(1), so this must be 2005 or above. You can keep the result of the datediff in an OUTER APPLY subquery so that it is only evaluated once.
SELECT
M.id_macchina,
[...],
V.indirizzo,
MA.data_fine AS ultima_manutenzione,
MA.data_diff,
CASE
WHEN stato = 0 THEN 'GREY'
WHEN stato = 2 THEN 'BLACK'
WHEN stato = 1 THEN
CASE
WHEN MA.data_diff >= 90 THEN 'RED'
WHEN MA.data_diff >= 80 THEN 'ORANGE'
WHEN MA.data_diff >= 60 THEN 'YELLOW'
WHEN MA.data_fine IS NULL THEN 'RED'
ELSE 'GREEN'
END
END AS colore
FROM dbo.MACCHINE M
INNER JOIN dbo.MACCHINE_MODELLI I ON M.id_modello = I.id_modello
INNER JOIN dbo.MACCHINE_TIPOLOGIE T ON I.id_tipologia = T.id_tipologia
INNER JOIN dbo.VIEW_CANTIERI V ON M.id_cantiere = V.id_cantiere
INNER JOIN dbo.MACCHINE_PRODUTTORI P ON I.id_produttore = P.id_produttore
INNER JOIN dbo.CLIENTI C ON V.id_cliente = C.id_cliente
OUTER APPLY (SELECT TOP (1)
MA.data_fine,
DATEDIFF(day, MA.data_fine, GETDATE()) AS data_diff
FROM dbo.MANUTENZIONI AS MA
WHERE MA.id_macchina = M.id_macchina
ORDER BY MA.data_fine DESC) MA
WHERE M._del = 'N'
Apologies if you've done this already, but I would suggest using SHOWPLAN (ctrl+L), or performance tools if you have them. Confirm that it's really these CASE statements that are causing the issue - the bottleneck could be one of the joins for instance, a missing index, or stale statistics promoting a bad execution plan.
If the view is read from far more often than it is written to, or it doesn't need to have exactly up to date data (i.e. you could cache the results each day and read the view from last night's refresh), consider using an indexed view. This will cache the view as if it were a table, so the the operations (including both the CASE and the JOINs) are not performed every time you read from the table (having an index also speeds usage). Alternatively, if only recent data changes, you could try partitioning an indexed view on date.