Limit per some field - sql

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

Related

Return second from the last oracle sql

SELECT * FROM
(
SELECT DISTINCT(TRUNC(receipt_dstamp))
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
ORDER BY 1 ASC
)
WHERE ROWNUM <= 5
Output:
Hi all, i've got this subeqery and in this case my oldest date is in row 1, i want to retrive only second from the last(from the top in this case) which is gonna be 01-SEP-21.
I was trying to play with ROWNUM and OVER but without any results, im getting blank output.
Thank you.
Full query:
SELECT TRUNC(receipt_dstamp) as old_putaway_date, COUNT(tag_id) as tag_old_putaway
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
AND TRUNC(receipt_dstamp) IN (
SELECT * FROM
(
SELECT DISTINCT(TRUNC(receipt_dstamp))
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
ORDER BY 1 ASC
)
WHERE ROWNUM = 1
)
GROUP BY TRUNC(receipt_dstamp);
You should be able to simplify the entire query to:
SELECT old_putaway_date,
COUNT(tag_id) as tag_old_putaway
FROM (
SELECT TRUNC(receipt_dstamp) as old_putaway_date,
tag_id,
DENSE_RANK() OVER (ORDER BY TRUNC(receipt_dstamp)) AS rnk
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
)
WHERE rnk = 3
GROUP BY
old_putaway_date;
You can use dense_rank() :
SELECT * FROM (
SELECT L.*,DENSE_RANK()
OVER (PARTITION BY L.TAG_OLD_PUTAWAY ORDER BY L.OLD_PUTAWAY_DATE DESC) RNK
FROM
(
SELECT TRUNC(receipt_dstamp) as old_putaway_date, COUNT(tag_id) as tag_old_putaway
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
AND TRUNC(receipt_dstamp) IN (
SELECT * FROM
(
SELECT DISTINCT(TRUNC(receipt_dstamp))
FROM inventory
WHERE substr(location_id,1,3) = 'GI-'
ORDER BY 1 ASC
)
WHERE ROWNUM = 1
)
GROUP BY TRUNC(receipt_dstamp)
) L
) WHERE RNK = 2
You are using an old Oracle syntax that is not standard compliant in the regard that it relies on a subquery result order. (Sub)query results are unordered data sets by definition, but Oracle lets this pass in order to make their ROWNUM work with it.
Oracle now supports the standard SQL FETCH clause, which you should use instead.
SELECT DISTINCT TRUNC(receipt_dstamp) AS receipt_date
FROM inventory
WHERE SUBSTR(location_id, 1, 3) = 'GI-'
ORDER BY receipt_date
OFFSET 2 ROWS
FETCH NEXT 1 ROW ONLY;
https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6

Select every second record then determine earliest date

I have table that looks like the following
I have to select every second record per PatientID that would give the following result (my last query returns this result)
I then have to select the record with the oldest date which would be the following (this is the end result I want)
What I have done so far: I have a CTE that gets all the data I need
WITH cte
AS
(
SELECT visit.PatientTreatmentVisitID, mat.PatientMatchID,pat.PatientID,visit.RegimenDate AS VisitDate,
ROW_NUMBER() OVER(PARTITION BY mat.PatientMatchID, pat.PatientID ORDER BY visit.VisitDate ASC) AS RowNumber
FROM tblPatient pat INNER JOIN tblPatientMatch mat ON mat.PatientID = pat.PatientID
LEFT JOIN tblPatientTreatmentVisit visit ON visit.PatientID = pat.PatientID
)
I then write a query against the CTE but so far I can only return the second row for each patientID
SELECT *
FROM
(
SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate, RowNumber FROM cte
) as X
WHERE RowNumber = 2
How do I return the record with the oldest date only? Is there perhaps a MIN() function that I could be including somewhere?
If I follow you correctly, you can just order your existing resultset and retain the top row only.
In standard SQL, you would write this using a FETCH clause:
SELECT *
FROM (
SELECT
visit.PatientTreatmentVisitID,
mat.PatientMatchID,
pat.PatientID,
visit.RegimenDate AS VisitDate,
ROW_NUMBER() OVER(PARTITION BY mat.PatientMatchID, pat.PatientID ORDER BY visit.VisitDate ASC) AS rn
FROM tblPatient pat
INNER JOIN tblPatientMatch mat ON mat.PatientID = pat.PatientID
LEFT JOIN tblPatientTreatmentVisit visit ON visit.PatientID = pat.PatientID
) t
WHERE rn = 2
ORDER BY VisitDate
OFFSET 0 ROWS FETCH FIRST 1 ROW ONLY
This syntax is supported in Postgres, Oracle, SQL Server (and possibly other databases).
If you need to get oldest date from all selected dates (every second row for each patient ID) then you can try window function Min:
SELECT * FROM
(
SELECT *, MIN(VisitDate) OVER (Order By VisitDate) MinDate
FROM
(
SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate,
RowNumber FROM cte
) as X
WHERE RowNumber = 2
) Y
WHERE VisitDate=MinDate
Or you can use SELECT TOP statement. The SELECT TOP clause allows you to limit the number of rows returned in a query result set:
SELECT TOP 1 PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate FROM
(
SELECT *
FROM
(
SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate,
RowNumber FROM cte
) as X
WHERE RowNumber = 2
) Y
ORDER BY VisitDate
For simplicity add order desc on date column and use TOP to get the first row only
SELECT TOP 1 *
FROM
(
SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate, RowNumber FROM cte
) as X
WHERE RowNumber = 2
order by VisitDate desc

SQL Server different select statement on same table and different result

I have a products table that contains date-of-create, price, rate,....... and multiple columns I need one select statement that returns the 10 newest records, 10 lowest prices and the top rated products as separated result of each other something like
(select top 10 from products Order By Date ASC ) as newest_list
(select top 10 from products Order By price DESC ) as price_list
(select top 10 from products Order By Rate ASC ) as rateList_list
where ( newest_list price_list rateList_list) are different table result
Which better way to approach that result. Thanks.
You basically want union all . . . and a subquery:
select *
from ((select top 10 'newest' as which, p.*
from products p
Order By Date ASC
) union all
(select top 10 'priciest', p.*
from products p
Order By price DESC
) union all
(select top 10 'ratiest', p.*
from products p
Order By Rate ASC
)
) p
If you don't want duplicates, you can use union or window functions:
select p.*
from (select p.*,
row_number() over (order by date desc) as seqnum_d,
row_number() over (order by price desc) as seqnum_p,
row_number() over (order by rate asc) as seqnum_r
from p
) p
where seqnum_d <= 10 or seqnum_p <= 10 or seqnum_r <= 10;

SQL Top(2) of a secondary Column

I have a query
SELECT PR.WBS1, PR.WBS2 FROM PR
It brings back what I need, but want to limit to the top (2) of the WBS2 for each of the WBS1's.
You can use ROW_NUMBER().
It groups the based on WBS1 and then selects two from each group.
;WITH CTE AS (
SELECT
PR.WBS1,
PR.WBS2,
ROW_NUMBER() OVER( PARTITION BY PR.WBS1 ORDER BY PR.WBS2) AS RN
FROM
PR
)
SELECT
WBS1,
WBS2
FROM
CTE
WHERE
RN <=2
SELECT PR.WBS1, PR.WBS2
FROM PR
WHERE (
SELECT COUNT(*)
FROM PR p
WHERE p.WBS1 = PR.WBS1
AND p.WBS2 >= PR.WBS2
) <= 2;
I had to change a few things on the above answer.
SELECT PR.WBS1, PR.WBS2
FROM PR
WHERE (
SELECT COUNT(*)
FROM PR p
WHERE p.WBS1 = PR.WBS1
AND p.WBS2 <= PR.WBS2
) <= 2;

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.