Using Oracle rank() in a left join - sql

When I run this subquery by itself:
select *
from (select rpt_nbr, iteration, seq_nbr, emp_id_key, rank() over
(partition by emp_id_key order by iteration, seq_nbr) rk
from SJTCAPP.LAB_RPT_SPEC_EMP where rpt_nbr = 1572413)
where rk = rownum
I get a good result it with only 1 emp_id_key returned for each iteration and seq_nbr, even though there can be multiple emp_id_key s assigned. So that is good, however, when I add that to the rest of my query:
select * from
SJTCAPP.LAB_RPT r
left join SJTCAPP.LAB_RPT_SPEC s on s.rpt_nbr = r.rpt_nbr
left join (select * from
(select rpt_nbr, iteration, seq_nbr, emp_id_key, rank() over (partition by emp_id_key order
by iteration, seq_nbr) rk from SJTCAPP.LAB_RPT_SPEC_EMP ) where rk = rownum)
se on se.rpt_nbr = s.rpt_nbr and se.seq_nbr = s.seq_nbr and se.iteration = s.iteration
left join sjtcapp.employee tech on tech.emp_id_key = se.emp_id_key
I get a NULL value for the tech.emp_id_key join
Update:
select * from (select rpt_nbr, iteration, seq_nbr, emp_id_key, rank() over (partition by emp_id_key order by iteration, seq_nbr ) rk from SJTCAPP.LAB_RPT_SPEC_EMP where rpt_nbr = 1572413) where rk = rownum and rpt_nbr = 1572413
The above query also gives 'good' results.
RPT_NBR ITERATION SEQ_NBR EMP_ID_KEY RK
1572413 1 1 44746 1
1572413 1 2 44746 2
Before I had more of a straightforward join here and received a correct query with individual technician names. Only issue is that if more than one were assigned it caused a duplicate, which is why I added the rank subquery.

If constrain iteration and seq_nbr is unique you can use exists instead rank
SELECT
rpt_nbr,
iteration,
seq_nbr,
emp_id_key
FROM
SJTCAPP.LAB_RPT_SPEC_EMP emp
WHERE
NOT EXISTS
(
SELECT
*
FROM
SJTCAPP.LAB_RPT_SPEC_EMP emp2
WHERE
emp2.emp_id_key = emp.emp_id_key AND
emp2.iteration < emp.iteration AND
emp2.seq_nbr < emp.seq_nbr
)

I ended up going back to using a GROUP BY
left join (
select min(rpt_nbr) as rpt_nbr, min(iteration) as iteration, min(seq_nbr) as seq_nbr, min(emp_id_key) as emp_id_key from LAB_RPT_SPEC_EMP group by rpt_nbr, iteration, seq_nbr
) se
on se.rpt_nbr = s.rpt_nbr and se.seq_nbr = s.seq_nbr and se.iteration = s.iteration

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);

Select most recent status for each ID and department code

I have the following table:
I want to get the most recent status for each dept_code that a CL_ID has. So the desired output would be this:
I have tried the following but this give me just the most recent status for each client and not each of their dept_codes.
SELECT *
FROM [CIMSHR6_MERGED].[dbo].[C3CLSTAT] C
INNER JOIN
(SELECT CLIENT_NUMBER, MAX(STATUS_DATE) AS SDATE
FROM [CIMSHR6_MERGED].[dbo].[C3CLSTAT]
GROUP BY CLIENT_NUMBER) X
ON X.CLIENT_NUMBER = C.CLIENT_NUMBER
AND X.SDATE = C.STATUS_DATE
ORDER BY C.CLIENT_NUMBER
Any help would be much appreciated. Thanks.
A convenient method that works in SQL Server is:
select top (1) cl.*
from [CIMSHR6_MERGED].[dbo].[C3CLSTAT] cl
order by row_number() over (partition by cl_id, dept_code order by status_date desc);
A method that is efficient with the right indexes in almost any database is:
select cl.*
from [CIMSHR6_MERGED].[dbo].[C3CLSTAT] cl
where cl.status_date = (select max(cl2.status_date)
from [CIMSHR6_MERGED].[dbo].[C3CLSTAT] cl2
where cl2.cl_id = cl.cl_id and cl2.dept_code = cl.dept_code
);
The right index is on (cl_id, dept_code, status_date).
I would also use ROW_NUMBER, but with a subquery:
SELECT CL_ID, Status_date, Status, Dept_code
FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CL_ID, Dept_code ORDER BY Status_date DESC) rn
FROM CIMSHR6_MERGED].[dbo].[C3CLSTAT]
) t
WHERE rn = 1;
1) Firstly group everything on Dept_Code,CL_ID and assign rank for each row with in the group in descending order.
2) Select all the rows with rnk=1 which would display your desired result.
SELECT Z.CL_ID,
Z.Status_Date,
Z.Status,
Z.Dept_Code
FROM
(
SELECT *,
RANK() OVER( PARTITION BY Dept_Code,CL_ID, ORDER BY Status_Date DESC ) AS rnk
FROM [CIMSHR6_MERGED].[dbo].[C3CLSTAT]
) Z
WHERE Z.rnk = 1;
This would work for almost all databases
select * from c3clstat c
where exists
(select 1 from c3clstat c1
where c1.cl_id=c.cl_id
and c1.dept_code=c.dept_code
group by cl_id,dept_code
having c.status_date=max(c1.status_date)
)

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!

Sql query with join and group by and

I have two table in sql. First one the patient list, second one is their report. all patient's reports are in the report, just with id we can join them. Each patient has some reports (Maybe all the fields of a record is not filled). Now I want to make a report that get the last report of each patient but if some field are empty in the last record of that patient I should fill it with last filled record of that patients records. I have date in the table of reports.
I want to do it for all patients. Here I will add a pic for one patient as an example
In the example above, I want just highlighted ones for this patient in the report.
I have write this query but it give even when a filed in the last record is null while it has data in previous records.
SELECT patient.bartar_id,patient.bartar_enteringthesystem,patient.bartar_proviencename,
patient.bartar_cityname,patient.bartar_coloplastrepname,patient.bartar_consultorname,
patient.bartar_provienceofsurgeryname,patient.bartar_cityofsurgeryname,
patient.bartar_surgeryhospitalname,patient.bartar_doctor,patient.bartar_patientstatusname,
patient.bartar_ostomytypename, patient.bartar_ostomytimename,
r.bartar_date,r.bartar_delay,r.bartar_nextcall,r.new_newcaller,
r.bartar_brandname,r.bartar_pastename,r.bartar_bagname,r.bartar_accname,
r.bartar_pastepermonth,r.bartar_bagepermonth,r.bartar_insuranceinfo,
patient.bartar_deathhealeddate,patient.bartar_dateofseurgery
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_newpaitient] as patient
JOIN (SELECT r.*, row_number() over (partition by r.bartar_patientname
order by r.bartar_date desc) as seqnum
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] as r
where r.bartar_delay is not null
) r
ON r.bartar_patientname = patient.bartar_newpaitientid and seqnum = 1
ORDER BY patient.bartar_id DESC ;
patient Table
Report Table
Join
Final Report What I want
this is a sample,
in your case you have to get the value of each column in a subquery (either in the join statement, or in the main select statement
example:
inner join (
select distinct bartar_patientname
,(select top 1 bartar_pastePerMonth from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] c2 where c2.bartar_patientname = cte.bartar_patientname and c2.bartar_pastePerMonth is not null order by c2.bartar_date desc) as bartar_date
,(select top 1 bartar_acc from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] c2 where c2.bartar_patientname = cte.bartar_patientname and c2.bartar_acc is not null order by c2.bartar_date desc) as bartar_acc
,(select top 1 bartar_insuranceinfo from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] c2 where c2.bartar_patientname = cte.bartar_patientname and c2.bartar_insuranceinfo is not null order by c2.bartar_date desc) as bartar_insuranceinfo
,(select top 1 bartar_brand from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] c2 where c2.bartar_patientname = cte.bartar_patientname and c2.bartar_brand is not null order by c2.bartar_date desc) as bartar_brand
from [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] cte
) r
Again, this is a sample of the solution.
Your script is fine as it looks, so I'll just place that on a temporary table for now, and do a per sequence query and filter it by "OR" afterwards.
Please try the script below.
SELECT patient.bartar_id,patient.bartar_enteringthesystem,patient.bartar_proviencename,
patient.bartar_cityname,patient.bartar_coloplastrepname,patient.bartar_consultorname,
patient.bartar_provienceofsurgeryname,patient.bartar_cityofsurgeryname,
patient.bartar_surgeryhospitalname,patient.bartar_doctor,patient.bartar_patientstatusname,
patient.bartar_ostomytypename, patient.bartar_ostomytimename,
r.bartar_date,r.bartar_delay,r.bartar_nextcall,r.new_newcaller,
r.bartar_brandname,r.bartar_pastename,r.bartar_bagname,r.bartar_accname,
r.bartar_pastepermonth,r.bartar_bagepermonth,r.bartar_insuranceinfo,
patient.bartar_deathhealeddate,patient.bartar_dateofseurgery
, ROW_NUMBER() OVER (PARTITION BY r.bartar_newpaitientid, r.bartar_pastepermonth ORDER BY r.bartar_date DESC) AS bartarpaste_sequence
, ROW_NUMBER() OVER (PARTITION BY r.bartar_newpaitientid, r.bartar_acc ORDER BY r.bartar_date DESC) AS bartaracc_sequence
, ROW_NUMBER() OVER (PARTITION BY r.bartar_newpaitientid, r.bartar_insuranceinfo ORDER BY r.bartar_date DESC) AS bartarins_sequence
, ROW_NUMBER() OVER (PARTITION BY r.bartar_newpaitientid, r.bartar_brandname ORDER BY r.bartar_date DESC) AS bartarbrd_sequence
INTO #tmpPatientReport
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_newpaitient] as patient
JOIN (SELECT r.*, row_number() over (partition by r.bartar_patientname
order by r.bartar_date desc) as seqnum
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] as r
where r.bartar_delay is not null
) r
ON r.bartar_patientname = patient.bartar_newpaitientid and seqnum = 1
ORDER BY patient.bartar_id DESC;
SELECT
*
FROM #tmpPatientReport
WHERE
bartarpaste_sequence = 1
OR bartaracc_sequence = 1
OR bartarins_sequence = 1
OR bartarbrd_sequence = 1
just do join for each column
JOIN (SELECT r.colx, row_number() over (partition by r.bartar_patientname
order by r.bartar_date desc) as seqnum
FROM [Bartar_MSCRM].[dbo].[Filteredbartar_callcenterreport] as r
where r.bartar_delay is not null and r.colx in not null
) rx
ON rx.bartar_patientname = patient.bartar_newpaitientid and seqnum = 1

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)