Aliasing a table in a window function? - sql

I am trying to alias a table in a window function, but not sure what I am doing wrong as when I alias it gives error that the columns cannot be resolved
SELECT e.city,
e.time,
e.day,
e.id,
m.id
FROM
(SELECT *,
rank() OVER (PARTITION BY e.id,
e.bin
ORDER BY e.time ASC) rnk
FROM table e
JOIN table2 m
on m.id = e.id
WHERE e.status = 'YES'
AND e.day BETWEEN date '2019-05-06' and date '2019-05-08')
WHERE rnk = 1

You have used the e alias in the outermost select. However, there is nothing in scope with that alias. The inner from doesn't "reach out" like that (scopes do "reach in" the other way though).
So:
SELECT e.city, e.time, e.day, e.id
FROM (SELECT e.*,
rank() OVER (PARTITION BY e.id, e.bin ORDER BY e.time ASC) as rnk
FROM table e
WHERE e.status = 'YES' AND
e.day BETWEEN date '2019-05-06' and date '2019-05-08'
) e
-------^ here
WHERE rnk = 1

Related

Select both row number and count from oracle

I'm trying to select records with pagination, and I need the total number of records so that I can display the number of records and pages on the UI.
The query I'm using is as below but it always returning the totalcount as 1.
WITH cteEmp AS
(SELECT e.empid, e.empname, d.deptid, d.deptname
FROM hr.Emp e
INNER JOIN hr.dept d ON e.deptid = d.deptid)
Select * from (SELECT row_number() over (order by hr.empid desc) rn, Count(*) totalcount,
C.empName FROM CTEPO C
LEFT JOIN hr.emphistory ON C.empid=hr.empid
GROUP BY c.empid,hr.empid) where rn>0 and rn<= 100
You can try this maybe it'll work for you:
(SELECT e.empid, e.empname, d.deptid, d.deptname
FROM hr.Emp e
INNER JOIN hr.dept d ON e.deptid = d.deptid)
Select * from (SELECT row_number() over (order by hr.empid desc) rn,
count(*) OVER (ORDER BY hr.empid desc ) AS totalcount
C.empName FROM CTEPO C
LEFT JOIN hr.emphistory ON C.empid=hr.empid
GROUP BY c.empid,hr.empid) where rn>0 and rn<= 100

Oracle 12c - Efficient way to join max date record

I have the following join a table to the most recent record for a given EMPLOYE_ID and I am wondering if there is a more efficient/faster way of retrieving the most recent record, what would be the best way?
SELECT * FROM EMPLOYEE
WHERE NOT EXISTS (
SELECT 1
FROM EMPLOYEE D
JOIN EMPLOYEE_HISTORY E
ON E.EMPLOYEE_ID = D.EMPLOYEE_ID
AND E.CREATE_DATE IN (SELECT MAX(CREATE_DATE)
FROM EMPLOYEE_HISTORY
WHERE EMPLOYEE_ID = D.EMPLOYEE_ID)
)
When I compared the explain plan to the following query it seems the below way is MORE costly.
SELECT *
FROM EMPLOYEE
WHERE NOT EXISTS
(SELECT 1
FROM EMPLOYEE D
JOIN (
SELECT E.*
FROM EMPLOYEE_HISTORY E
INNER JOIN (
SELECT EMPLOYEE_ID
, MAX(CREATE_DATE) max_date
FROM EMPLOYEE_HISTORY E2
GROUP BY EMPLOYEE_ID
) EE
ON EE.EMPLOYEE_ID = E.EMPLOYEE_ID
AND EE.max_date = E.CREATE_DATE
) A
ON A.EMPLOYEE_ID = D.EMPLOYEE_ID
AND ROWNUM = 1)
So does that mean it is indeed better?
There is no index on CREATE_DATE, however the PK is on EMPLOYEE_ID, CREATE_DATE
Use the RANK (or DENSE_RANK or ROW_NUMBER) analytic function:
SELECT 1
FROM EMPLOYEE E
JOIN (
SELECT *
FROM (
SELECT H.*,
RANK() OVER ( PARTITION BY EMPLOYEE_ID ORDER BY CREATE_DATE DESC ) AS rnk
FROM EMPLOYEE_HISTORY H
)
WHERE rnk = 1
) H
ON H.EMPLOYEE_ID = E.EMPLOYEE_ID
I would write the query using = rather than IN:
SELECT 1
FROM EMPLOYEE E JOIN
EMPLOYEE_HISTORY EH
ON EH.EMPLOYEE_ID = E.EMPLOYEE_ID AND
EH.CREATE_DATE = (SELECT MAX(EH2.CREATE_DATE)
FROM EMPLOYEE_HISTORY EH2
WHERE EH2.EMPLOYEE_ID = EH.EMPLOYEE_ID
);
IN is more general than = for the comparison.
Your primary key index should be used for the subquery, which should make it pretty fast.
Assuming that you actually do want to return actual columns, then I'm not sure if there is a way to make this faster.
If you really are selecting only 1, then forget the most recent record and just use EXISTS:
SELECT 1
FROM EMPLOYEE E
WHERE EXISTS (SELECT 1
FROM EMPLOYEE_HISTORY EH2
WHERE EH2.EMPLOYEE_ID = E.EMPLOYEE_ID
);
The only additional condition your query checks for is that CREATE_DATE is not NULL, but I'm guessing that is always true anyway.
If the CREATE_DATE of the EMPLOYEE must be after the maximum CREATE_DATE for that EMPLOYEE_ID in EMPLOYEE_HISTORY?
Then for that EMPLOYEE_ID, there doesn't exist an equal or higher CREATE_DATE in EMPLOYEE_HISTORY.
SELECT *
FROM EMPLOYEE Emp
WHERE NOT EXISTS (
SELECT 1
FROM EMPLOYEE_HISTORY Hist
WHERE Hist.EMPLOYEE_ID = Emp.EMPLOYEE_ID
AND Hist.CREATE_DATE >= Emp.CREATE_DATE
)
Test here

Find lowest number from column in table

I have 3 tables - EVENT, MEMBER, RANK. I need to show the best result (from RANK) along with the member it belongs to, for a specific event (ex EVENT01) What is the simplest way to do this? My code below seems to select the lowest number, but duplicates itself and claims other members got the same result.
Expected output:
EVENT_ID EVENT_TYPE EVENT_NAME MEMBER_ID MEMBER_NAME RESULT
event01 swimming 100m mem001 John Smith 10
Code so far:
SELECT E.EVENT_ID, E.EVENT_TYPE, E.EVENT_NAME, R.MEMBER_ID, M.MEMBER_FIRSTNAME, M.MEMBER_LASTNAME, (SELECT MIN(RESULT)
FROM RANK WHERE E.EVENT_ID = 'EVENT003' ) AS AVG_INCOME_ALL_CLUBS
FROM EVENT E, RANK R, MEMBER M
WHERE E.EVENT_ID = R.EVENT_ID
AND R.MEMBER_ID = M.MEMBER_ID
ORDER BY MEMBER_ID;
First, learn to use explicit JOIN syntax.
Second, the answer to your question is ROW_NUMBER():
SELECT *
FROM (SELECT E.EVENT_ID, E.EVENT_TYPE, E.EVENT_NAME, R.MEMBER_ID, M.MEMBER_FIRSTNAME, M.MEMBER_LASTNAME,
ROW_NUMBER() OVER (PARTITION BY E.EVENT_ID ORDER BY R.RESULT ASC) as SEQNUM
FROM EVENT E, JOIN
RANK R
ON E.EVENT_ID = R.EVENT_ID JOIN
MEMBER M
ON R.MEMBER_ID = M.MEMBER_ID
) ERM
WHERE SEQNUM = 1
ORDER BY MEMBER_ID;
SELECT E.EVENT_ID, E.EVENT_TYPE, E.EVENT_NAME, R.MEMBER_ID,M.MEMBER_FIRSTNAME, M.MEMBER_LASTNAME,
FROM EVENT E, JOIN
RANK R
ON E.EVENT_ID = R.EVENT_ID JOIN
MEMBER M
ON R.MEMBER_ID = M.MEMBER_ID
where R.RESULT=
(SELECT MIN(RESULT) FROM RANK WHERE E.EVENT_ID = 'EVENT003') AND R.EVENT_ID='EVENT003';

Select lowest number from column and show other columns

I need to find the lowest number in my RESULT column which I know can be done with MIN but I am not sure how to do this as well as select my other columns. My code displays all the results for all members but I just want it to show the best result (lowest number) along with the other details for that member.
Right now I have multiple entries that look like this:
EVENT_ID EVENT_TYPE EVENT_NAME MEMBER_ID MEMBER_FIRSTNAME MEMBER_LASTNAME RESULT
event1 track 100M 0001 John Smith 11.3
I just need to select the row with the lowest result and show that one only. Here is my code so far:
SELECT E.EVENT_ID, E.EVENT_TYPE, E.EVENT_NAME, R.MEMBER_ID, M.MEMBER_FIRSTNAME, M.MEMBER_LASTNAME, R.RESULT
FROM EVENT E, MEMBER M, RANK R
WHERE E.EVENT_ID = R.EVENT_ID
AND R.MEMBER_ID = M.MEMBER_ID;
SGEDDES - I tried following your last method and couldn't get it to work:
SELECT E.EVENT_ID, E.EVENT_TYPE, E.EVENT_NAME, R.MEMBER_ID, M.MEMBER_FIRSTNAME, M.MEMBER_LASTNAME, R.RESULT
FROM (EVENT E, MEMBER M, RANK R
WHERE E.EVENT_ID = R.EVENT_ID
AND R.MEMBER_ID = M.MEMBER_ID;
ORDER BY RESULT)
WHERE ROWNUM = 1;
Here's one option using row_number (also please note the join syntax -- in general you shouldn't use commas in the from clause):
SELECT *
FROM (
SELECT E.EVENT_ID, E.EVENT_TYPE, E.EVENT_NAME,
R.MEMBER_ID, M.MEMBER_FIRSTNAME, M.MEMBER_LASTNAME, R.RESULT,
row_number() over (order by R.RESULT) rn
FROM EVENT E JOIN RANK R ON E.EVENT_ID = R.EVENT_ID
JOIN MEMBER M ON R.MEMBER_ID = M.MEMBER_ID
) t
WHERE rn = 1
If you need to group the results by a specific field(s), you an use partition by. For example:
row_number() over (partition by e.event_id order by result) rn
And here's another option using rownum:
SELECT *
FROM (<<YOUR QUERY HERE>> ORDER BY R.Result )
WHERE ROWNUM = 1;
In the most recent versions of Oracle, you can use fetch first 1 row only:
SELECT E.EVENT_ID, E.EVENT_TYPE, E.EVENT_NAME, R.MEMBER_ID,
M.MEMBER_FIRSTNAME, M.MEMBER_LASTNAME, R.RESULT
FROM EVENT E JOIN
RANK R
ON E.EVENT_ID = R.EVENT_ID JOIN
MEMBER M
ON R.MEMBER_ID = M.MEMBER_ID
ORDER BY R.RESULT
FETCH FIRST 1 ROW ONLY;
If you have a less recent version of Oracle, then one of sgeddes's solutions is fine.

SQL Join to get most recent record

I have three tables:
Measurements (MeasureID, Time, Distance, Value)
Events(EventID, Time Value)
EventValues (EventDataID, EventID, Type, Value)
I need to get for every measurement, the most recent event (in the past) and its associated eventvalues data.
My current query is quite ugly:
SELECT
M.*,
(SELECT TOP 1 EV.value FROM [Event] E JOIN EventValues EV ON E.EventID = EV.EventID
WHERE M.Time >= E.Time ORDER BY M.Time-E.Time) AS Data,
FROM [Measure] M
ORDER BY M.Distance
and it only allows me to select one column from the EventValues table (I need more)
Is there any way this can be done using a join?
EDIT: I also need to select ALL entries from the measurement table even if they are before the first event (i.e. just select null data for the join)
You can use CROSS APPLY.
SELECT
M.*, Data.*
FROM [Measure] M
CROSS APPLY
(SELECT TOP 1 EV.* FROM [Event] E JOIN EventValues EV ON E.EventID = EV.EventID
WHERE M.Time >= E.Time ORDER BY E.Time DESC) AS Data
ORDER BY M.Distance
Try something like this (not tested)
SELECT * FROM
(
SELECT M.*, E.*, EV.EventDataID, EV.Type, EV.Value,
Rank() over (Partition BY M.MeasureID order by M.Time - E.Time) as Rank
FROM [Measure] M
INNER JOIN [Event] E ON M.Time >= E.Time
INNER JOIN EventValues EV ON E.EventID = EV.EventID
) T
WHERE Rank = 1
EDIT
SELECT * FROM
(
SELECT M.*, E.*, EV.EventDataID, EV.Type, EV.Value,
Rank() over (Partition BY M.MeasureID order by M.Time - E.Time) as Rank
FROM [Measure] M
LEFT JOIN [Event] E ON M.Time >= E.Time
LEFT JOIN EventValues EV ON E.EventID = EV.EventID
) T
WHERE Rank = 1