Refer to outer column in inner query Where clause - mysql

I have two tables Employee and Departament in relation that is shown bellow. For each departament I want to get the fifth best paid employee.
How the query should look like if we have for example MySQL?
I trying to do something like this but it doesn’t see d.id column in inner select.
select d.name, e.id from Employee e
join Departament d on d.id = e.dep_id
where e.id = (
select s.eid from (
select ee.id as eid, #rowid:=#rowid+1 as rowid from Employee ee, (SELECT #rowid:=0) as init
where ee.dep_id = d.id
order by ee.salary desc
) s
where s.rowid = 5
)
Is it possible to do it in generic way not dependent to any database semantics?
How to do it using MySQL and what is the best way to do it?

SELECT X.NAME, X.SALARY AS SAL_5 FROM
(
SELECT DEPT.NAME,EMP.SALARY, RANK() OVER (PARTITION BY NAME ORDER BY SALARY DESC) RN
FROM EMP, DEPT
WHERE EMP.DEPT_ID=DEPT.DEPT_ID
) X
WHERE X.RN=5;

Just do the row_id calculation in the from clause:
select d.name, e.id
from (select ee.*,
(#rowid := if(#d = dept_id, #rowid + 1,
if(#d := dept_id, 1, 1)
)
) as rowid
from Employee ee cross join
(SELECT #rowid := 0, #d := NULL) as init
order by ee.dept_id, ee.salary desc
) e join
Departament d
on d.id = e.dept_id
where e.rowid = 5;
And, yes, there is an ANSI standard way of doing this. In fact, I can readily think of two approaches. But MySQL supports neither window functions not fetch first 1 row only.

Related

MySQL - Get row number post select & join

I am trying to obtain the row number (i.e. rank) for the following select statement (which includes a column JOIN) but without declaring a SET variable at the beginning.
Reason for this is because I am using a WordPress/MySQL plugin which can only emulate single statement code. The common hack of declaring a prior variable to 0 then incrementing is not recognized.
Is there another way to obtain the row number using the select & join below?
SELECT s.id
, s.item
, s.state
, c.job_count
FROM wp_state_tab s
LEFT
JOIN wp_custom_tab c
ON c.state_id = s.id
WHERE c.date = CURDATE()
ORDER
BY c.job_count DESC
Sample Data Output
MySQL version is 5.6.40-84.0-log
MySQL can be fiddly about variables -- good thing they are deprecated. With ORDER BY or GROUP BY, you often have to use a subquery:
SELECT (#rn := #rn + 1) as seqnum, sc.*
FROM (SELECT s.id, s.item, s.state, c.job_count
FROM wp_state_tab s LEFT JOIN
wp_custom_tab c
ON c.state_id = s.id
WHERE c.date = CURDATE()
ORDER BY c.job_count DESC
) sc CROSS JOIN
(SELECT #rn := 0) params;
You can use a subquery for the iteration of a newly defined row number without explicitly declaring variable as :
select #i := #i + 1 as rownum,
s.id, s.item, s.state, c.job_count
from wp_state_tab s
left join wp_custom_tab c
on c.state_id = s.id and c.date = CURDATE()
join (select #i:=0) t2
order by job_count desc;

MySQL - Get Distinct Primary Col Table Row Order By Foreign Key Table

The title may be confusing, here is my schema
and here is the result of my query
how can i remove duplicates and just get the values highlighted, i am trying to order by message time
Regards
The following syntax will work in both SQL Server and MySQL:
SELECT c.ContactID, c.Name, m.Text, m.Messagetime
FROM Contacts c INNER JOIN
Messages m
ON c.ContactID = m.ContactID
WHERE NOT EXISTS (select 1
from messages m2
where m2.ContactId = m.ContactId and
m2.MessageTime > m.MessageTime
)
ORDER BY m.MessageTime desc;
Note that if you have duplicate most recent message times, then all will be returned.
One way in SQL-Server is using a ranking function like ROW_NUMBER:
WITH CTE AS
(
SELECT c.ContactID, c.Name, m.Text, m.Messagetime,
RN = ROW_NUMBER() OVER (PARTITION BY c.ContactID
ORDER BY m.MessageTime DESC)
FROM dbo.Contacts c
INNER JOIN Messages m ON c.ContactID = m.ContactID
)
SELECT ContadctId, Name, Text, Messagetime
FROM CTE
WHERE RN = 1

Limit amount of results from one table, but allow infinite from another

I've got this query:
SELECT *, m.id AS mooringid
FROM mooring m JOIN customer c ON m.assignedTo = c.id
WHERE m.Number = :var OR (CONCAT(c.TitleName,' ',c.Surname) LIKE CONCAT('%', :var, '%')) OR m.MooringArea = :var
ORDER BY c.Surname limit 0,250
That is supposed to get elements assigned to a customer from another table, the only way I saw that I could do this is by "infusing" the customer details with the element, returning the elements but I want to limit the amount of customers returned but allow infinite elements, however, this limits the elements rather than the customer and that just doesn't work in my situation.
Is this possible? Am I missing something?
One way to tackle the issue is to do a subquery on customers and extract the number you need. Something like:
from (select c.*
from customers c
limit 100
) c
But, in your case, you have a lot of secondary filtering going on (with the join conditions and the where). Instead, add a customer counter to each row and use that for selecting a certain number of customers:
select t.*
from (SELECT *, m.id AS mooringid,
#rn := if(#cid = c.id, #rn + 1, 1) as rn,
#cid = c.id
FROM mooring m JOIN
customer c
ON m.assignedTo = c.id cross join
(select #rn := 0, #cid := -1) const
WHERE m.Number = :var OR
(CONCAT(c.TitleName,' ',c.Surname) LIKE CONCAT('%', :var, '%')) OR
m.MooringArea = :var
ORDER BY c.Surname
) t
order by c.SurName
where rn <= 10;

MAX aggregate function in sql server

I wrote the following query to return the the records with the latest date.
select fs.company_id, max(fs.create_dt) as latestcreatedate
from field_sale fs
group by fs.company_id
order by fs.company_id
The query all works fine. But I need to retrieve the record with all related columns attached to it. Such as, id, title, desc and etc.
How can I retrieve the records with its corresponding columns?
Couple ways of doing so :
-- 1.
SELECT a.*
FROM field_sale a
INNER JOIN
(
select fs.company_id, max(fs.create_dt) as latestcreatedate
from field_sale fs
group by fs.company_id
)b
ON b.company_id = a.company_id AND b.latestcreatedate = a.create_dt
order by a.company_id;
-- 2.
SELECT b.* FROM
(
SELECT a.* , ROW_NUMBER()
OVER (PARTITION BY a.company_id ORDER BY a.create_dt DESC)
AS rn
FROM field_sale a
)b WHERE b.rn = 1
ORDER BY company_id
WITH t AS (
SELECT fs.company_id,
fs.create_dt AS latestcreatedate,
id,
title,
etc,
ROW_NUMBER() OVER ( PARTITION BY fs.company_id ORDER BY fs.create_dt DESC ) AS rowNum
FROM field_sale fs
)
SELECT t.company_id,
t.latestcreatedate,
t.id,
t.title,
t.etc
FROM t
WHERE t.rowNum = 1
ORDER BY t.company_id

Rank not being determined properly

I'm using this query:
SELECT A.place_idx,A.place_id,B.TOTAL_CNT,(#r := #r + 1) AS rank FROM CUSTOM_LIST
AS A
INNER JOIN
(SELECT #r := 0)
AS C
INNER JOIN
(SELECT place_id,COUNT(place_id) AS TOTAL_CNT from COUNT_TABLE GROUP BY place_id)
AS B ON B.place_id=A.place_id order by B.TOTAL_CNT desc;
Which gives this result:
But I want this result:
How do I need to modify my query? What am I doing wrong?
SELECT *,(#r := #r + 1) AS rank FROM
(
SELECT A.place_idx,A.place_id,B.TOTAL_CNT FROM CUSTOM_LIST
AS A
INNER JOIN
(SELECT place_id,COUNT(place_id) AS TOTAL_CNT from COUNT_TABLE GROUP BY place_id)
AS B ON B.place_id=A.place_id order by B.TOTAL_CNT desc
) AS T, (SELECT #r := 0) AS tt
Your C.rank is getting calculated as they are processed, not after they are sorted. There is really no need for this data, anyways. Since you're sorting the rows by your metric, you know the first row is the first rank, etc. You can handle it on the programming side of things after you pull it out.
Alternatively, you can put what you have in an inner select, then do the rank after.