T-SQL, possibly combining MAX and BETWEEN? - sql

I have the following excerpt from an SQL Query in SQL Server:
LEFT JOIN tbl_StuAssess SA (NOLOCK)
ON CA.ASMT_PK = SA.ASMT_PK
AND SA.DELT_FLAG = 0
AND CA.SCRE_MTOD_PK = SA.SCRE_MTOD_PK
AND CVS.STUD_PK = SA.STUD_PK
AND(SA.ASMT_DATE BETWEEN #YearStartDate AND #YearEndDate)
Here is my issue, If the first 4 conditions I met, I want the last condition (SA.ASMT_DATE BETWEEN #YearStartDate AND #YearEndDate) to only return the Latest date that is in between #YearStartDate and #YearEndDate and that meets all the other conditions. Can anyone tell me how I can do that?
Thank you.

You can do possibly do this with a subquery:
LEFT JOIN
(select sa.*,
row_number() over (partition by asmt_pk, scre_mtod_pk, stud_pk order by asmt_dt desc) as seqnum
from tbl_StuAssess SA (NOLOCK)
where SA.DELT_FLAG = 0 and (SA.ASMT_DATE BETWEEN #YearStartDate AND #YearEndDate))
) sa
ON CA.ASMT_PK = SA.ASMT_PK and
CA.SCRE_MTOD_PK = SA.SCRE_MTOD_PK and
CVS.STUD_PK = SA.STUD_PK and
sa.seqnum = 1
This calculates the last date before the join. This works, if the join conditions are not filtering out some but not all of the records for a given group of asmt_pk, scre_mtod_pk, stud_pk.
The alternative is to use row_number() at the next higher level. Same idea, but without the rest of the code, I can't provide a code sample.

Related

Optimizing SQL Query speed

I am trying to optimize my SQL query below as I am using a very old RDMS called firebird. I tried rearranging the items in my where clause and removing the order by statement but the query still seems to take forever to run. Unfortunately firebird doesn't support Explain Execution Plan Functionalities and therefore I cannot identify the code that is holding up the query.
select T.veh_reg_no,T.CON_NO, sum(T.pos_gpsunlock) as SUM_GPS_UNLOCK,
count(T.pos_gpsunlock) as SUM_REPORTS, contract.con_name
from
(
select veh_reg_no,CON_NO,
case when pos_gpsunlock = upper('T') then 1 else 0 end as pos_gpsunlock
from vehpos
where veh_reg_no in
( select regno
from fleetvehicle
where fleetno in (97)
) --DS5
and pos_timestamp > '2022-07-01'
and pos_timestamp < '2022-08-01'
) T
join contract on T.con_no = contract.con_no
group by T.veh_reg_no, T.con_no,contract.con_name
order by SUM_GPS_UNLOCK desc;
If anyone can help it would be greatly appreciated.
I'd either comment out some of the sub-queries or remove a join or aggregation and see if that improves it. Once you find the offending code maybe you can move it or re-write it. I know nothing of Firebird but I'd approach that query with the below code, wrapping the aggregation outside of the joins and removing the "Where in" clause.
If nothing works can you create an aggregation table or pre-filtered table and use that?
select
x.*
,sum(case when x.pos_gpsunlock = upper('T') then 1 else 0 end) as SUM_GPS_UNLOCK
,count(*) as SUM_REPORTS
FROM (
select
a.veh_reg_no
,a.pos_gpsunlock
,a.CON_NO
,c.con_name
FROM vehpos a
JOIN fleetvehicle b on a.veg_reg_no = b.reg_no and b.fleetno = 97 and b.pos_timestamp between '222-07-01' and '2022-08-01'
JOIN contract c on a.con_no = contract.con_no
) x
Group By....
This might help by converting subqueries to joins and reducing nesting. Also an = instead of IN() operation.
select vp.veh_reg_no,vp.con_no,c.con_name,
count(*) as SUM_REPORTS,
sum(case when pos_gpsunlock = upper('T') then 1 else 0 end) as SUM_GPS_UNLOCK
from vehpos vp
inner join fleetvehicle fv on fv.fleetno = 97 and fv.regno = vp.veh_reg_no
inner join contract c on vp.con_no = c.con_no
where vp.pos_timestamp >= '2022-07-01'
and vp.pos_timestamp < '2022-08-01'
group by vp.veh_reg_no, vp.con_no, c.con_name

How to optimize this T-sql query?

There is a loop in this query, in the last where condition. and this
causes a severe problem to the performance of SQL.
I have no idea about how to modify it.
select pr.tavpun
from mta110 pr
where pr.taisoc = mta110.taisoc
and pr.taitar = mta110.taitar
and pr.taydat = mta110.taydat
and pr.tairef = mta110.tairef
and pr.tatind = (select max(pr2.tatind) from mta110 pr2
where pr2.taisoc = mta110.taisoc
and pr2.taitar = mta110.taitar
and pr2.taydat = mta110.taydat
and pr2.tairef = mta110.tairef
and pr2.tatind <= mgc100.gntind)) AS SalesPrice
Your query makes little sense, because pr is not a reasonable alias for mta110, and mta110 is not recognized in the outer query.
I speculate that you have two tables, pr and mta110 which are joined and you want the "most recent" row of mta110 for each matching row.
If this interpretation is correct, then you can use row_number() and a proper join:
select . . .
from pr join
(select m.*,
row_number() over (partition by taisoc, taitar, taydat, tairef order by gntind desc) as seqnum
from mta110 m
) m
on pr.? = m.?
where seqnum = 1;

Select row from a MAX() GROUP BY in SQL Server

I have a table I called Eventos. I have to select the corresponding outTime from the alarm which has the greater inTime.
And I have to do it quickly/optimized. I have about 1 million entries in the table.
This is my code:
SELECT
CadGrupoEventos.Severidade AS Nível,
CadGrupoEquipamentos.Nome AS Grupo,
CadEquipamentos.TAG AS Equipamento,
CadEventos.MensagemPT AS 'Mensagem de alarme',
MAX(Eventos.InTime) AS 'Hora do evento',
Eventos.OutTime AS 'Hora de saída'
FROM
CadGrupoEventos,
CadEquipamentos,
CadEventos,
Eventos,
CadUsuarios,
CadGrupoEquipamentos
WHERE
Eventos.Acked = 0
AND CadGrupoEventos.Codigo = CadEventos.Grupo
AND CadEquipamentos.Codigo = Eventos.TAG
AND CadEventos.Codigo = Eventos.CodMensagem
AND CadGrupoEquipamentos.Codigo = CadEquipamentos.Grupo
GROUP BY
CadGrupoEventos.Severidade,
CadEquipamentos.TAG,
CadEventos.MensagemPT,
CadGrupoEquipamentos.Nome,
Eventos.OutTime
This code, as it is, returns every single entry from the table.
I have to take Eventos.OutTime out of GROUP BY and still get the value of it.
This is just an educated guess based on your description. Notice I used ANSI-92 style joins which are much more explicit. I also used aliases to make this a lot more legible. Your query might look something like this.
select x.Severidade AS Nível,
x.Nome AS Grupo,
x.TAG AS Equipamento,
x.MensagemPT AS [Mensagem de alarme],
x.[Hora do evento],
x.OutTime AS [Hora de saída]
from
(
SELECT cge.Severidade,
cgequip.Nome,
ce.TAG,
cevt.MensagemPT,
MAX(e.InTime) AS [Hora do evento],
e.OutTime
, RowNum = ROW_NUMBER() over(partition by cge.Severidade, ce.TAG, cevt.MensagemPT, cgequip.Nome order by e.OutTime /*maybe desc???*/)
FROM CadGrupoEventos cge
join CadEventos cevt on cge.Codigo = cevt.Grupo
join Eventos e on AND cevt.Codigo = e.CodMensagem
join CadEquipamentos ce on ce.Codigo = e.TAG
join CadGrupoEquipamentos cgequip on cgequip.Codigo = ce.Grupo
cross join CadUsuarios cu --not sure if this is really what you want but your original code did not have any logic for this table
WHERE e.Acked = 0
GROUP BY cge.Severidade,
ce.TAG,
cevt.MensagemPT,
cgequip.Nome,
e.OutTime
) x
where x.RowNum = 1

where statement execute before inner join

I'm trying to grab the first instance of each result with a sysAddress of less than 4. However my statement currently grabs the min(actionTime) result first before applying the where sysAddress < 4. I'm trying to have the input for the inner join as the where sysAddress < 4 however i cant seem to figure out how to do it.
Should i be nesting it all differently? I didnt want to create an additional layer of table joins. Is this possible? I'm a bit lost at all the answers ive found.
SELECT
tblHistoryObject.info,
tblHistory.actionTime,
tblHistoryUser.userID,
tblHistoryUser.firstName,
tblHistoryUser.surname,
tblHistory.eventID,
tblHistoryObject.objectID,
tblHistorySystem.sysAddress
FROM tblHistoryObject
JOIN tblHistory
ON (tblHistory.historyObjectID = tblHistoryObject.historyObjectID)
JOIN tblHistorySystem
ON (tblHistory.historySystemID = tblHistorySystem.historySystemID)
JOIN tblHistoryUser
ON (tblHistory.historyUserID = tblHistoryUser.historyUserID)
INNER JOIN (SELECT
MIN(actionTime) AS recent_date,
historyObjectID
FROM tblHistory
GROUP BY historyObjectID) AS t2
ON t2.historyObjectID = tblHistoryObject.historyObjectID
AND tblHistory.actionTime = t2.recent_date
WHERE sysAddress < 4
ORDER BY actionTime ASC
WITH
all_action_times AS
(
SELECT
tblHistoryObject.info,
tblHistory.actionTime,
tblHistoryUser.userID,
tblHistoryUser.firstName,
tblHistoryUser.surname,
tblHistory.eventID,
tblHistoryObject.objectID,
tblHistorySystem.sysAddress,
ROW_NUMBER() OVER (PARTITION BY tblHistoryObject.historyObjectID
ORDER BY tblHistory.actionTime
)
AS historyObjectID_SeqByActionTime
FROM
tblHistoryObject
INNER JOIN
tblHistory
ON tblHistory.historyObjectID = tblHistoryObject.historyObjectID
INNER JOIN
tblHistorySystem
ON tblHistory.historySystemID = tblHistorySystem.historySystemID
INNER JOIN
tblHistoryUser
ON tblHistory.historyUserID = tblHistoryUser.historyUserID
WHERE
tblHistorySystem.sysAddress < 4
)
SELECT
*
FROM
all_action_times
WHERE
historyObjectID_SeqByActionTime = 1
ORDER BY
actionTime ASC
This does exactly what your original query did, without trying to filter by action_time.
Then it appends a new column, using ROW_NUMBER() to generate sequences from 1 for each individual tblHistoryObject.historyObjectID. Then it takes only the rows where this sequence value is 1 (the first row per historyObjectID, when sorted in action_time order).

SQL Selecting based off largest date from different table

I am attempting to write a query that pulls Order information from various tables. I have hit a road block at the target date value.
It seems that every time a target date is changed a new row is added in that table. All I want is to be able to select only the newest Target Date. What should I do?
select Distinct
OR01001 AS OrderNumber,
OR01002 AS OrderType,
OR01003 AS CustomerCode,
OR01015 AS OrderDate,
OR01017 AS CustomerREP,
OR01018 AS ContactPerson,
OR01019 AS SalesmanNumber,
OR03011 - OR03012 AS OpenQuantity,
SC03003 AS StockBalance,
OR01050 AS WarehouseNumber,
OR01072 AS CustomerPO,
OR03005 AS ItemCode,
OR03002 AS LineNumber,
OR500100.OR50004 As TargetDate
from OR010100
INNER Join OR030100 ON OR030100.OR03001 = OR010100.OR01001
INNER Join SL010100 ON SL010100.SL01001 = OR010100.OR01003
INNER Join SC030100 ON SC030100.SC03001 = OR030100.OR03005
Inner JOIN OR500100 ON OR500100.OR50001 = OR010100.OR01001
where OR010100.OR01002 <> 0 AND OR010100.OR01002 <> 6 AND OR01017 = 'SLOTT'
Order by OR01017 ASC;
If I understand your columns correctly, here's one way:
SELECT ...,
OR500100A.OR50004 AS TargetDate
FROM ...
INNER JOIN OR500100 AS OR500100A ON OR500100A.OR50001 = OR010100.OR01001
AND NOT EXISTS(SELECT 1 FROM OR500100 AS OR500100B
WHERE OR500100B.OR5001 = OR010100.OR01001
AND OR500100B.OR50004 > OR500100A.OR50004)
...
This makes sure you only get one OR500100 row with the latest value in OR50004 for the given OR5001.
from what i understand,
SELECT
...
MAX(OR500100.OR50004) As TargetDate
FROM...
WHERE...
GROUP BY --everything but OR500100.OR50004
ORDER BY...
should do the trick
EDIT: ty Ic.