SQL Top(2) of a secondary Column - sql

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;

Related

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

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

How to find the most repeat data in oracle?

I have a TRANSACTION table.
Columns are =
Personnel_id and Personnel_Name
I want to find the most repetitive data sorting.
How i can do it?
I have tried and I found, the most repetitive result but i can't show personnel_name.
Here is my query ;
SELECT PERSONNEL_ID,
COUNT(PERSONNEL_ID)
FROM KOMTAS.TRANSACTIONS
GROUP BY PERSONNEL_ID;
And this code gives an error ;
SELECT MAX(R),
PERSONNEL_ID
FROM ( SELECT PERSONNEL_ID,
COUNT(PERSONNEL_ID) R
FROM KOMTAS.TRANSACTIONS
GROUP BY PERSONNEL_ID
) ;
help please !
Add a second group by to the second query:
SELECT MAX(R),
PERSONNEL_ID
FROM ( SELECT PERSONNEL_ID,
COUNT(PERSONNEL_ID) R
FROM KOMTAS.TRANSACTIONS
GROUP BY PERSONNEL_ID )
GROUP BY PERSONNEL_ID;
I would suggest using rownum and a subquery:
SELECT t.*
FROM (SELECT PERSONNEL_ID, COUNT(PERSONNEL_ID) as cnt
FROM KOMTAS.TRANSACTIONS
GROUP BY PERSONNEL_ID
ORDER BY COUNT(PERSONNEL_ID) DESC
) t
WHERE rownum = 1;
Try This
SELECT MAX(cnt) AS c,
PersonId
FROM ( SELECT PersonId,
COUNT(PersonId) cnt
FROM TRANSACTIONS
GROUP BY PersonId )
GROUP BY PersonId
ORDER BY c DESC;
Check here SQLFiddle

How to get top 5 history rows of each sql job?

I am trying to get top 5 (latest) of each sql job.
I tried to query it like this
SELECT Pr1.job_ID,MSDB.dbo.Agent_datetime(Pr1.run_date, Pr1.run_time) LastRun, COUNT(*) num
FROM MSDB.dbo.sysjobhistory Pr1
JOIN MSDB.dbo.sysjobhistory Pr2
ON MSDB.dbo.Agent_datetime(Pr1.run_date, Pr1.run_time) = MSDB.dbo.Agent_datetime(Pr2.run_date, Pr2.run_time)
AND Pr1.job_ID = Pr2.job_ID
GROUP BY Pr1.job_ID, MSDB.dbo.Agent_datetime(Pr1.run_date, Pr1.run_time)
HAVING COUNT (*) <= 5
ORDER BY job_ID, MSDB.dbo.Agent_datetime(Pr1.run_date, Pr1.run_time)
But this query runs forever. I am using sql-server.
i also tried with a recommendation from below.
WITH CTE AS
(
SELECT jo.name,Pr1.job_ID, Pr1.run_date, Pr1.run_time,pr1.run_status,
Count(*) Over (Partition By Pr1.job_ID,Pr1.run_date,Pr1.run_time) As num,
Row_Number() Over (Partition By Pr1.job_ID,Pr1.run_date,Pr1.run_time
Order By pr1.run_date desc--, pr1.run_time desc
) As Rn
FROM MSDB.dbo.sysjobhistory Pr1
join MSDB.dbo.sysjobs jo on jo.job_id=pr1.job_id
JOIN MSDB.dbo.sysjobhistory Pr2
ON Pr1.job_ID = Pr2.job_ID --and pr1.run_status=pr2.run_status
and pr1.run_date >=pr2.run_date and pr1.run_time >=pr2.run_time
)
SELECT name,job_ID, run_date, run_time,run_status
FROM CTE
WHERE num <= 3 AND Rn = 1
Use row_number():
select pr.*
from (select pr.*,
row_number() over (partition by pr.job_id
order by pr.run_date desc, pr.run_time desc
) as seqnum
from MSDB.dbo.sysjobhistory pr
) pr
where seqnum <= 5;
It's running forever because you use the scalar valued function Agent_datetime. If you use a query that doesn't need those functions the query optimizer can do it's job and use indexes.
You can simplify your task with window functions and a common-table-expression(CTE). Following groups by the column job_ID and returns the latest record of each group according to the run_date and run_time columns:
WITH CTE AS
(
SELECT Pr1.job_ID, Pr1.run_date, Pr1.run_time,
Count(*) Over (Partition By Pr1.job_ID) As num,
Row_Number() Over (Partition By Pr1.job_ID
Order By Pr1.run_date DESC, Pr1.run_time DESC) As Rn
FROM MSDB.dbo.sysjobhistory
)
SELECT job_ID, run_date, run_time
FROM CTE
WHERE num <= 3 AND Rn = 1
Note that i've removed your pointless join condition and the self-join:
ON MSDB.dbo.Agent_datetime(Pr1.run_date, Pr1.run_time)
= MSDB.dbo.Agent_datetime(Pr1.run_date, Pr1.run_time)

Oracle SQL query result into a temporary table for use in a sub query

I want to create a temporary table which is inturn derived from a query to be used in another sub-query so as to simplify the rownum() and partition by condition. The query I have entered is as below but it returns an error t.trlr_num invalid identifier.
with t as
(select distinct
ym.trlr_num,
ym.arrdte,
ri.invnum,
ri.supnum
from rcvinv ri, yms_ymr ym
where ym.trlr_cod='RCV'
and ri.trknum = ym.trlr_num
and ym.wh_id <=50
and ym.trlr_stat in ('C','CI','R','OR')
and ym.arrdte is not null
order by ym.arrdte desc
)
select trlr_number, invnum, supnum
from
(
select
t.trlr_num, t.invnum, t.supnum,
row_number() over (partition by t.trlr_number,t.invnum order by t.arrdte) as rn
from t
)
where rn = 1;
From above, I put a condition to create a table t as a temporary table to be used in the below select statement. But is seems to error out with invalid identifier.
seems typo, replace trlr_number with trlr_num and it work
with t as
(select distinct
ym.trlr_num,
ym.arrdte,
ri.invnum,
ri.supnum
from rcvinv ri, yms_ymr ym
where ym.trlr_cod='RCV'
and ri.trknum = ym.trlr_num
and ym.wh_id <=50
and ym.trlr_stat in ('C','CI','R','OR')
and ym.arrdte is not null
order by ym.arrdte desc
)
select trlr_num, invnum, supnum
from
(
select
t.trlr_num, t.invnum, t.supnum,
row_number() over (partition by t.trlr_num,t.invnum order by t.arrdte) as rn
from t
)
where rn = 1;
You could use multiple subqueries in the WITH clause as separate temporary tables. It would be nice and easy to understand:
WITH t AS
(SELECT DISTINCT ym.trlr_num trlr_num,
ym.arrdte arrdte,
ri.invnum invnum,
ri.supnum supnum
FROM rcvinv ri,
yms_ymr ym
WHERE ym.trlr_cod ='RCV'
AND ri.trknum = ym.trlr_num
AND ym.wh_id <=50
AND ym.trlr_stat IN ('C','CI','R','OR')
AND ym.arrdte IS NOT NULL
),
t1 AS (
SELECT t.trlr_num,
t.arrdte,
t.invnum,
t.supnum,
row_number() OVER (PARTITION BY t.trlr_num, t.invnum ORDER BY t.trlr_num, t.invnum DESC) rn
FROM t
)
SELECT trlr_num, arrdte, invnum, supnum
FROM t1
WHERE rn = 1;