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

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

Related

Display only result > 2

I face a problem with the result on my script.
My formula for MARGIN is ((plnamt-(ibhexc/ibhand))/plnamt)*100.
I want to display only result > 2. How to do this? Please help.
This my script:
select
a.plnitm,a.plnstr,max(a.plncdt),max(a.plnndt),max(a.plnamt),max(a.plnevt),b.idept,c.ibhand,c.ibhexc,
decimal((c.ibhexc/c.ibhand),12,4) as AVG_COST,
decimal(((((max(a.plnamt)-(c.ibhexc/c.ibhand))/(max(a.plnamt))))*100),12,4) as MARGIN
from prcpln a
inner join invmst b on a.plnitm = b.inumbr
inner join invbal c on a.plnitm = c.inumbr and a.plnstr = c.istore and c.ibhand <> 0
where a.plnstr = ''14006''
group by a.plnitm,a.plnstr,b.idept,c.ibhand,c.ibhexc
order by a.plnitm
Simplistically, for situations like this, you can take your query and put it inside a cte:
WITH q AS (
select
a.plnitm,a.plnstr,max(a.plncdt),max(a.plnndt),max(a.plnamt),max(a.plnevt),b.idept,c.ibhand,c.ibhexc,
decimal((c.ibhexc/c.ibhand),12,4) as AVG_COST,
decimal(((((max(a.plnamt)-(c.ibhexc/c.ibhand))/(max(a.plnamt))))*100),12,4) as MARGIN
from prcpln a
inner join invmst b on a.plnitm = b.inumbr
inner join invbal c on a.plnitm = c.inumbr and a.plnstr = c.istore and c.ibhand <> 0
where a.plnstr = ''14006''
group by a.plnitm,a.plnstr,b.idept,c.ibhand,c.ibhexc
)
SELECT * FROM q WHERE margin > 2
ORDER BY q.plnitm
This is similar to the advice to use HAVING - a HAVING is a "where clause that is done after a GROUP BY"
A cte is a way of taking some calculated block of data and giving it an alias that can be used just like a table. I wanted to answer this way to point out to you that queries don't have to be formed purely from tables; tables are just blocks of data (with a name), and the output from a select is also "just a block of data" that can be given a name (by use of a cte or subquery) and then used just like a table is

do a "order by DESC or ASC" in a union with different columns

Good day I need to sort by the Fecha column but when using order by I can not because the select in the union does not have the same columns. how could you sort by this field Fecha. Can you please help me solve this?
e.g. as below.
Below is the query i am using.
SELECT 'PESO:','VOLUMEN:','NO. PIEZAS:','ETD:','ETA:'
UNION ALL
SELECT
(SELECT ISNULL(Valor,'') FROM MovCampoExtra WHERE Modulo = 'VTAS' AND ID = Venta.ID AND CampoExtra = 'CEDIS0103') AS pesoBruto,
(SELECT ISNULL(Valor,'') FROM MovCampoExtra WHERE Modulo = 'VTAS' AND ID = Venta.ID AND CampoExtra = 'CEDIS0121') AS volumen,
(SELECT ISNULL(Valor,'') FROM MovCampoExtra WHERE Modulo = 'VTAS' AND ID = Venta.ID AND CampoExtra = 'CEDIS0102') AS cantidadUnidades,
(SELECT CEDIS0120 FROM VentaCampoExtra WHERE ID = Venta.ID) AS ETD,
(SELECT CEDIS0123 FROM VentaCampoExtra WHERE ID = Venta.ID) AS ETA
FROM Venta
--Venta AS v
INNER JOIN Cte AS c ON c.Cliente = Venta.Cliente
INNER JOIN Usuario us On us.Usuario = Venta.Usuario
WHERE
#Origen=Venta.TMLICentroCosto AND
Venta.Mov IN ('Instruccion EXPO', 'Instruccion IMPO','Instruc. Nacional')
AND Venta.Empresa IN ('TM')
AND Venta.ID is NOT NULL
AND Venta.Estatus NOT IN ('Cancelado')
UNION ALL
SELECT 'FECHA:','HORA:','ACTIVIDADES:',' ',' '
UNION ALL
SELECT
Fecha = ISNULL(CONVERT(VARCHAR, mb.Fecha, 103),''), -- AS Fecha,
Hora = ISNULL(CONVERT(VARCHAR, mb.Fecha, 108),''), -- AS Hora,
Comentarios = ISNULL(mb.Evento2,''),
'',
''
FROM Venta
--Venta AS v
INNER JOIN Cte AS c ON c.Cliente = Venta.Cliente
LEFT OUTER JOIN MovBitacora AS mb ON mb.ID = Venta.ID
INNER JOIN Usuario us On us.Usuario = Venta.Usuario
WHERE
#Origen=Venta.TMLICentroCosto AND
Venta.Mov IN ('Instruccion EXPO', 'Instruccion IMPO','Instruc. Nacional')
AND Venta.Empresa IN ('TM')
AND Venta.ID is NOT NULL
AND Venta.Estatus NOT IN ('Cancelado')
ORDER BY Fecha ASC
RETURN
END
Okay so moaning out the way first, this is a horrible way to do things. You should not be returning header columns from the sql query, and hacking together two unrelated queries. This should be two queries called by the application layer, which should also be handling the headers.
But if you have to do it this way for some arcane reason, you could add a "sorting" column to each query
Header 1 sortval = A
Peso query sortval = B + Peso
Header 2 sortval = C
Fecha query sortval = D + Fecha (in YYYYMMDD format)
Then add a big select round the whole thing to select the columns (which you will need to add aliases for in the first header select) and order by the sortval.

Confused in join query in SQL

The following works:
SELECT IBAD.TRM_CODE, IBAD.IPABD_CUR_QTY, BM.BOQ_ITEM_NO,
IBAD.BCI_CODE, BCI.BOQ_CODE
FROM IPA_BOQ_ABSTRCT_DTL IBAD,
BOQ_CONFIG_INF BCI,BOQ_MST BM
WHERE BM.BOQ_CODE = BCI.BOQ_CODE
AND BCI.BCI_CODE = IBAD.BCI_CODE
AND BCI.STATUS = 'Y'
AND BM.STATUS = 'Y'
order by boq_item_no;
Results:
But after joining many tables with that query, the result is confusing:
SELECT (SELECT CMN_NAME
FROM CMN_MST
WHERE CMN_CODE= BRI.CMN_RLTY_MTRL) MTRL,
RRI.RRI_RLTY_RATE AS RATE,
I.BOQ_ITEM_NO,
(TRIM(TO_CHAR(IBAD.IPABD_CUR_QTY,
'9999999999999999999999999999990.999'))) AS IPABD_CUR_QTY,
TRIM(TO_CHAR(BRI.BRI_WT_FACTOR,
'9999999999999999999999999999990.999')) AS WT,
TRIM(TO_CHAR((IBAD.IPABD_CUR_QTY*BRI.BRI_WT_FACTOR),
'9999999999999999999999990.999')) AS RLTY_QTY,
(TRIM(TO_CHAR((IBAD.IPABD_CUR_QTY*BRI.BRI_WT_FACTOR*RRI.RRI_RLTY_RATE),
'9999999999999999999999990.99'))) AS TOT_AMT,
I.TRM_CODE AS TRM
FROM
(SELECT * FROM ipa_boq_abstrct_dtl) IBAD
INNER JOIN
(SELECT * FROM BOQ_RLTY_INF) BRI
ON IBAD.BCI_CODE = BRI.BCI_CODE
INNER JOIN
(SELECT * FROM RLTY_RATE_INF) RRI
ON BRI.CMN_RLTY_MTRL = RRI.CMN_RLTY_MTRL
INNER JOIN
( SELECT IBAD.TRM_CODE, IBAD.IPABD_CUR_QTY,
BM.BOQ_ITEM_NO, IBAD.BCI_CODE, BCI.BOQ_CODE
FROM IPA_BOQ_ABSTRCT_DTL IBAD,
BOQ_CONFIG_INF BCI,BOQ_MST BM
WHERE
BM.BOQ_CODE = BCI.BOQ_CODE
AND BCI.BCI_CODE = IBAD.BCI_CODE
and BCI.status = 'Y'
and bm.status = 'Y') I
ON BRI.BCI_CODE = I.BCI_CODE
AND I.TRM_CODE = BRI.TRM_CODE
AND BRI.TRM_CODE =4
group by BRI.CMN_RLTY_MTRL, RRI.RRI_RLTY_RATE, I.BOQ_ITEM_NO,
IBAD.IPABD_CUR_QTY, BRI.BRI_WT_FACTOR, I.TRM_CODE, I.bci_code
order by BRI.CMN_RLTY_MTRL
Results:
TRM should be 11 instead of 4 in the first row.
you getting 4 because you use
AND BRI.TRM_CODE =4
if you remove this criter you can get true result
In your first query, both of the rows you've highlighted have BCI_CODE=1866.
In the second query, you are joining that result set with a number of others (which come from the same tables, which seems odd). In particular, you are joining from the subquery to another table using BCI_CODE, and from there to (SELECT * FROM ipa_boq_abstrct_dtl) IBAD. Since both of the rows from the subquery have the same BCI_CODE, they will join to the same rows in the other tables.
The quantity that you are actually displaying in the second query is from (SELECT * FROM ipa_boq_abstrct_dtl) IBAD, not from the other subquery.
Is the problem simply that you mean to select I.IPABD_CUR_QTY instead of IBAD.IPABD_CUR_QTY?
You might find this clearer if you did not reuse the same aliases for tables at multiple points in the query.

T-SQL, possibly combining MAX and BETWEEN?

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.

SQL Server "ORDER BY" optimization - massive performance decrease

Using SQL Server 2000. I have a table that receives a dump from a legacy system once a day, I am trying to write a query that will process this table with a few reference table joins and an order by clause.
This is the SQL I have:
select d.acct_no,
d.associate_id,
d.first_name,
d.last_name,
d.acct_bal,
plr.long_name p_lvl,
tlr.long_name t_lvl,
d.category,
d.status,
tm.site_name,
d.addr1 + ' ' + isnull(d.addr2,'') address,
d.city,
d.state,
d.country,
d.post_code,
CASE WHEN d.home_phone_ok = 1 THEN d.home_phone END home_phone,
CASE WHEN d.work_phone_ok = 1 THEN d.work_phone END work_phone,
CASE WHEN d.alt_phone_ok = 1 THEN d.alt_phone END alt_phone,
CASE WHEN d.email_ok = 1 THEN d.email END email,
d.last_credit last_paid,
d.service,
d.quantity,
d.amount,
ar.area_desc area
from item_dump d
left outer join territory_map tm on tm.short_postcode = left(post_code,3) and country in ('United States','Canada')
left outer join p_level_ref plr on plr.p_level_id = d.p_lvl_id
left outer join t_level_ref tlr on tlr.t_level_id = d.t_lvl_id
left outer join (select distinct master_item_id, site_item_id from invoice_detail) as map on map.item_id = d.item_no
left outer join item_ref i on i.item_id = map.master_item_id
left outer join area_ref ar on ar.area_id = i.area_id
where (d.cat_id > 80 or d.cat_id < 70)
and d.standing < 4
and d.status not like 'DECEASED'
and d.paid = 1
order by d.associate_id
Most of these columns are straight from the legacy system dump table item_dump. All the joins are only reference tables with few rows. The legacy table itself has about 17000 records but with the where statements the query comes out to 3000.
I have a non-clustered index on the associate_id column.
When I run this query without the order by associate_id clause it takes about 2 seconds. With the order by clause it takes a full minute!
I've tried adding the where clause columns to the index along with associate_id but that didn't change the performance at all.
The end of the execution plan without the order by looks like this:
Using order by, parallelism kicks in on the order by argument and it looks like this:
I thought maybe it was weird SQL Server 2000 parallelism handling, but adding the (maxdop 1) hint made the query take 3 minutes instead!
It isn't really sensible for me to put sorting in the application code because this query caches for about 6 hours before it gets run again and I would have to sort it in the application code many times a minute.
I must be missing something very basic but after straining at the query for an hour i.e. running it 10 times, I can't see what it is anymore.
What happens when u remove all the outer joins and ofcourse the select's in there..
select d.acct_no,
d.associate_id,
d.first_name,
d.last_name,
d.acct_bal,
d.category,
d.status,
d.addr1 + ' ' + isnull(d.addr2,'') address,
d.city,
d.state,
d.country,
d.post_code,
CASE WHEN d.home_phone_ok = 1 THEN d.home_phone END home_phone,
CASE WHEN d.work_phone_ok = 1 THEN d.work_phone END work_phone,
CASE WHEN d.alt_phone_ok = 1 THEN d.alt_phone END alt_phone,
CASE WHEN d.email_ok = 1 THEN d.email END email,
d.last_credit last_paid,
d.service,
d.quantity,
d.amount
from item_dump d
where (d.cat_id > 80 or d.cat_id < 70)
and d.standing < 4
and d.status not like 'DECEASED'
and d.paid = 1
order by d.associate_id
If that works fast then i would go for sub selects inside the select's
select d.acct_no,
d.associate_id,
d.first_name,
d.last_name,
d.acct_bal,
plr.long_name p_lvl,
tlr.long_name t_lvl,
d.category,
d.status,
(select tm.site_name
from territory_map tm
where tm.short_postcode = left(post_code,3)
and country in ('United States','Canada') as site_name
etc. it'll be really faster as left outer joining them in the from clause