SELECT DISTINCT UNIQUE - sql

I have a table of products and i need to extract from each idproduct the last 3 unique records IDFornecedor, ie
image1
Result:
image2
Code:
WITH x AS (SELECT dbo.ItensDocumentos.Referencia, dbo.Documentos.Data, dbo.FacturasFornecedor.IDFornecedor, dbo.ItensDocumentos.IDProduto, dbo.Fornecedores.Nome, dbo.Documentos.BaseIncidenciaIva,
dbo.ItensDocumentos.Quantidade, dbo.ItensDocumentos.TotalIliquido / dbo.ItensDocumentos.Quantidade AS Valor
FROM dbo.ItensDocumentos INNER JOIN
dbo.Documentos ON dbo.ItensDocumentos.IDDocumento = dbo.Documentos.IDDocumento INNER JOIN
dbo.FacturasFornecedor ON dbo.Documentos.IDDocumento = dbo.FacturasFornecedor.IDDocumento INNER JOIN
dbo.Fornecedores ON dbo.FacturasFornecedor.IDFornecedor = dbo.Fornecedores.IDFornecedor
WHERE (dbo.ItensDocumentos.TotalIliquido / dbo.ItensDocumentos.Quantidade <> 0) AND dbo.Fornecedores.IDFornecedor<> 2 ),
Y AS (SELECT * FROM X),
Z AS (SELECT IDProduto,Data,Valor,IDFornecedor,Referencia,Nome,ROW_NUMBER() OVER (PARTITION BY IDProduto ORDER BY MAX(Data) DESC) AS Seq
from y
GROUP BY IDProduto,Data,Valor,IDFornecedor,Referencia,Nome)
SELECT IDProduto,Data,Valor,IDFornecedor,Referencia,Nome,Seq,PorDefeito = 0,PrazoREposicao = 0 from z WHERE Seq <= 3 order by Referencia asc

If you want the last three distinct suppliers per product, one option uses aggregation and row_number() (if your database supports window functions):
select *
from (
select
idProduto,
idFornecedor,
max(data) maxData,
row_number() over(partition by idProduto order by max(data) desc
from mytable
group by idProduto, idFornecedor
) t
where rn <= 3
order by idProduto, idFornecedor, rn desc

Related

SQL query to get first and last of a sequence

With the following query ...
select aa.trip_id, aa.arrival_time, aa.departure_time, aa.stop_sequence, aa.stop_id, bb.stop_name
from OeBB_Stop_Times aa
left join OeBB_Stops bb on aa.stop_id = bb.stop_id
I get the following table:
Now I want the first and last line/value from the column stop_sequence referring to column trip_id, so the result should be:
How can I do that?
Thanks
You can do a sub-query to get the min and max and join against that data.
Like this:
select aa.trip_id, aa.arrival_time, aa.departure_time, aa.stop_sequence, aa.stop_id, bb.stop_name
from OeBB_Stop_Times aa
join (
SELECT trip_id, max(stop_sequence) as max_stop, min(stop_sequence) as min_stop
FROM OeBB_Stop_Times
GROUP BY trip_di
) sub on aa.trip_id = sub.trip_id AND (aa.stop_sequence = sub.max_stop or aa.stop_sequence = sub.min_stop)
left join OeBB_Stops bb on aa.stop_id = bb.stop_id
You can use the ROW_NUMBER() window function twice to filter out rows, as in:
select *
from (
select *,
row_number() over(partition by trip_id order by arrival_time) as rn,
row_number() over(partition by trip_id order by arrival_time desc) as rnr
from OeBB_Stop_Times
) x
where rn = 1 or rnr = 1
order by trip_id, arrival_time
You can use row_number():
select s.*
from (select st.trip_id, st.arrival_time, st.departure_time,
st.stop_sequence, st.stop_id, s.stop_name,
row_number() over (partition by st.trip_id order by st.stop_sequence) as seqnum_asc,
row_number() over (partition by st.trip_id order by st.stop_sequence desc) as seqnum_desc
from OeBB_Stop_Times st left join
OeBB_Stops s
on st.stop_id = s.stop_id
) s
where 1 in (seqnum_asc, seqnum_desc);
Note that I fixed the table aliases so they are meaningful rather than arbitrary letters.
Actually, if the stop_sequence is guaranteed to start at 1, this is a bit simpler:
select s.*
from (select st.trip_id, st.arrival_time, st.departure_time,
st.stop_sequence, st.stop_id, s.stop_name,
max(stop_sequence) over (partition by st.trip_id) as max_stop_sequence
from OeBB_Stop_Times st left join
OeBB_Stops s
on st.stop_id = s.stop_id
) s
where stop_sequence in (1, max_stop_sequence);

Get attributs from lines whith both MIN and MAX clauses on different columns

I have the following table:
for each (id_notification/no_doc) I want to have the line that has the minimum no_ligne and then the maximim dt_capt. In this case, the result would be the third line. I also need to have a column that indicates the number of lines we have for each (id_notification/no_doc). In this case it swould be 4.
What I did is a first join to have lines that have no_ligne=min(no_ligne) (I know that it can be easier)
and then a second join to have lines that have dt_capt=max(dt_capt) but it doesn't work if the line which has a max(dt_capt) doesn't have a no_ligne which is equal to min(no_ligne). This is what I tried:
select * from
(select a.id_notification, a.no_doc, b.minlignes, b.nblignes, a.dt_capt
from ${use_database}.lkr_send_editique as a
join
(select id_notification, no_doc, count(no_ligne) as nblignes, min(no_ligne) as minlignes from ${use_database}.lkr_send_editique group by id_notification, no_doc) as b
on a.id_notification=b.id_notification and a.no_doc=b.no_doc and b.minlignes=a.no_ligne) as tt
join
(select s.id_notification, s.no_doc, s.dt_capt,
s.typ_mvt from ${use_database}.lkr_send_editique as s
join
(select id_notification, no_doc, max(dt_capt) as dtmax FROM ${use_database}.lkr_send_editique group by id_notification, no_doc) as c
on s.id_notification=c.id_notification and s.no_doc=c.no_doc and s.dt_capt=c.dtmax and s.dt_capt=c.dtmax) as maxxx
on tt.id_notification=maxxx.id_notification and tt.no_doc=maxxx.no_doc and tt.dt_capt=maxxx.dt_capt;
for each (id_notification/no_doc) I want to have the line that has the minimum no_ligne and then the maximim dt_capt. In this case, the result would be the third line.
You can use row_number():
select t.*
from (select t.*,
count(*) over (partition by id_notification, no_doc) as cnt
row_number() over (partition by id_notification, no_doc order by no_ligne, dt_capt desc) as seqnum
from ${use_database}.lkr_send_editique t
) t
where seqnum = 1;
Use NOT EXISTS to get the 3d row only and join to a query that returns the number of lines:
select t.*, tt.counter
from tablename t inner join (
select id_notification, no_doc, count(*) counter
from tablename
group by id_notification, no_doc
) tt on tt.id_notification = t.id_notification and tt.no_doc = t.no_doc
where not exists (
select 1 from tablename
where id_notification = t.id_notification and no_doc = t.no_doc
and (no_ligne < t.no_ligne or (no_ligne = t.no_ligne and dt_capt > t.dt_capt))
)

Include join in a subquery

I trying to make a query to obtain the last date time when a records repeat.
select * from OCCONTROLMERCADERIA om
join OCCONTROLMERCADERIALINEAS oml on om.occontrolid=oml.occontrolid
where om.OCControlNroId=5519337
This return this:
Result Query 1
The query that i want should return the register are in the blue box.
I have this query that found (without the join):
with
p as (select * from OCCONTROLMERCADERIA om where om.OCControlNroId=5519337),
p_rnk as (
select *, row_number() over (partition by OCControlNroId order by OCControlFecha desc) as rn
from p
)
select * from p_rnk where rn = 1 order by OCControlNroId;
This return this:
Result Query 2
That its ok, if see the box blue in the first image, the last date corresponds to the record id 13756.
But I need that the query return the 3 records that corresponds to id 13756, and not only one, so i trying to apply the join.
with
p as (select * from OCCONTROLMERCADERIA om
join OCCONTROLMERCADERIALINEAS oml on om.occontrolid=oml.occontrolid
where om.OCControlNroId=5519337),
p_rnk as (
select *, row_number() over (partition by OCControlNroId order by OCControlFecha desc) as rn
from p
)
select * from p_rnk where rn = 1 order by OCControlNroId;
But this return:
The column 'OCControlId' was specified multiple times for 'p'.
I´m trying this (select om.*):
with
p as (select om.* from OCCONTROLMERCADERIA om
join OCCONTROLMERCADERIALINEAS oml on om.occontrolid=oml.occontrolid
where om.OCControlNroId=5519337),
p_rnk as (
select *, row_number() over (partition by OCControlNroId order by OCControlFecha desc) as rn
from p
)
select * from p_rnk where rn = 1 order by OCControlNroId;
And the error disappears but only return one records, no the three. Which could the problem ?
Thanks for your help!

oracle SQL subquery: using the minimum but getting all columns [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Closed 5 years ago.
MIN(A.price) CustomerID TripID travelby
25 x05 66 train
66 x07 21 train
100 x07 12 train
Trying to figure this one out. the above results is from a query of 2 tables. However, I need to modify it so that it gives me the result of min price with all of its 4 columns.
this was my original sql:
(select min(price)
from trips a, customers b
where a.tripid = b.tripid and c.travelmode = 'train')
I can only get the row with 25, but that is only if i request the price column. How would you go about this, but get all columns? Thank you for all input
SELECT *
FROM (
SELECT *
FROM trips a
INNER JOIN customers b
ON ( a.tripid = b.tripid )
WHERE travelby = 'train'
ORDER BY price ASC
)
WHERE ROWNUM = 1;
or
SELECT *
FROM (
SELECT price,
CustomerID,
a.TripID,
travelby,
ROW_NUMBER() OVER ( ORDER BY price ASC ) AS rn
FROM trips a
INNER JOIN customers b
ON ( a.tripid = b.tripid )
WHERE travelby = 'train'
ORDER BY price ASC
)
WHERE rn = 1;
Or:
SELECT MIN( price ) AS price,
MIN( CustomerID ) KEEP ( DENSE_RANK FIRST ORDER BY price, ROWNUM ) AS CustomerID,
MIN( a.TripID ) KEEP ( DENSE_RANK FIRST ORDER BY price, ROWNUM ) AS TripID,
MIN( travelby ) KEEP ( DENSE_RANK FIRST ORDER BY price, ROWNUM ) AS travelby
FROM trips a
INNER JOIN customers b
ON ( a.tripid = b.tripid )
WHERE travelby = 'train'
You can do something like this:
select . . .
from (select . . ., row_number() over (order by price desc) as seqnum
from trips t join
customers c
on c.tripid = t.tripid and ?.travelmode = 'train'
)
where seqnum = 1;
Your version of the query is quite confusing:
Why is it surrounded by parentheses?
What is c.travelmode? You haven't defined the table alias c.
Learn to use proper, explicit JOIN syntax.

Limit per some field

Suppose we have such an sql query, with joined data from another table.
SELECT
pr.num, pr.resort_id, p.src_mask
FROM
rutraveler.rt_photo_resort AS pr
JOIN rutraveler.rt_photo AS p ON pr.photo_id = p.id
WHERE pr.resort_id = '612' AND p.src_mask is not null
ORDER BY num
LIMIT 30
So far we have to do several queries for several resort_id.
How to change the query so that we have only one query (WHERE resort_id in (612, 333, 111) with result no more than 30 items per each resort_id?
Use ROW_NUMBER to count the rows per resort_id.
SELECT resort_id, num, resort_id, src_mask
FROM
(
SELECT
pr.resort_id, pr.num, pr.resort_id, p.src_mask,
ROW_NUMBER() OVER (PARTITION BY pr.resort_id ORDER BY num) AS rn
FROM
rutraveler.rt_photo_resort AS pr
JOIN rutraveler.rt_photo AS p ON pr.photo_id = p.id
WHERE resort_id in (612, 333, 111) AND p.src_mask is not null
) data
WHERE rn <= 30
ORDER BY resort_id, num;
you can use CTE with ROW_NUMBER() and PARTITION BY
WITH Results_CTE AS
(
SELECT
pr.num, pr.resort_id, p.src_mask,ROW_NUMBER() over ( PARTITION BY pr.resort_id ORDER BY num) As Row_number
FROM
rutraveler.rt_photo_resort AS pr
JOIN rutraveler.rt_photo AS p ON pr.photo_id = p.id
WHERE pr.resort_id IN (A,B,C) AND p.src_mask is not null
)
SELECT *
FROM Results_CTE
WHERE Row_number <= 30