I have a couple tables in MySQL DB
EID Name
1 Title A
2 Title B
3 Title C
LID EID Location Address Order
1 1 Office NY 1
2 1 Home IL 2
3 2 Office CA 1
4 3 Home NJ 2
I have the above 2 tables (Employee and Location). I would like to know the location of each Employee with office as a preferred choice and if 'office' does not exist then would need 'Home' location . The order column defined the order/priority of what is needed.
here is the output needed
EID LID Name Location Address
1 1 Title A Office NY
2 3 Title B Office CA
3 4 Title C Home NJ
The first join of the query below just connects the Employee and Location tables, but note that it results in all records from Location being joined. The critical part of the below query is the second INNER JOIN to a subquery. This subquery identifies the minimum (i.e. highest priority) order for each employee ID. This is then used to discard records from the first join which are not the highest priority.
SELECT t1.EID,
t2.LID,
t1.Name,
t2.Location,
t2.Address
FROM Employee t1
INNER JOIN Location t2
ON t1.EID = t2.EID
INNER JOIN
(
SELECT EID, MIN(`Order`) AS min_order
FROM Location
GROUP BY EID
) t3
ON t2.EID = t3.EID AND
t2.Order = t3.min_order
One other note: Don't name your columns Order, which is a MySQL keyword. To get my query to work, I had to put it in backticks, which is inconvenient to say the least, and possibly error prone.
Demo here:
SQLFiddle
There are two posibility to get your result.
1)If you need Based on Order result then use this query
SELECT e1.EID, l1.LID, e1.Name, l1.Location, l1.Address
FROM Employee e1
JOIN
(SELECT MIN(`Order `) as Minorder, EID, LID, Location, Address, Order
FROM Location l1
GROUP BY EID) l1
ON l1.EID = e1.EID AND l1.Minorder = l1.Order;
2)if you need result Based on EID then use this query
SELECT e1.EID,l1.LID,e1.Name,l1.Location,l1.Address
FROM Employee e1 JOIN
(SELECT MIN(`EID`)as Mineid,EID,LID,Location,Address,`Order` FROM Location l1 GROUP BY EID)l1
ON l1.Mineid = e1.EID;
Extra Note:-
Plese donot use mysql inbuilt keyword as Column name or Table name for more information read this link click here
You can the expected result by using inner join
Select a.eid,b.Lid,a.name,b.location,b.address from Table1 a innner join (select * from Tableb group by eid) b on
a.eid=b.eid;
you can try this code this will help you as i think
select E.EID,E.name,ad.LID,ad.LOCATION,ad.ADDRESS,ad.[order]
from #emp E inner join #address ad on E.EID = ad.EID
inner join (select EID, min([order]) [order]
from #address
group by EID) tt on ad.EID = tt.EIDand ad.[order] = tt.[order]
Related
"employee" Table
emp_id
empName
1
ABC
2
xyx
"client" Table:
id
emp_id
clientName
1
1
a
2
1
b
3
1
c
4
2
d
"collection" Table
id
emp_id
Amount
1
2
1000
2
1
2000
3
1
1000
4
1
1200
I want to aggregate values from the three tables input tables here reported as samples. For each employee I need to find
the total collection amount for that employee (as a sum)
the clients that are involved with the corresponding employee (as a comma-separated value)
Here follows my current query.
MyQuery:
SELECT emp_id,
empName,
GROUP_CONCAT(client.clientName ORDER BY client.id SEPARATOR '') AS clientName,
SUM(collection.Amount)
FROM employee
LEFT JOIN client
ON clent.emp_id = employee.emp_id
LEFT JOIN collection
ON collection.emp_id = employee.emp_id
GROUP BY employee.emp_id;
The problem of this query is that I'm getting wrong values of sums and clients when an employee is associated to multiple of them.
Current Output:
emp_id
empName
clientName
TotalCollection
1
ABC
a,b,c,c,b,a,a,b,c
8400
2
xyz
d,d
1000
Expected Output:
emp_id
empName
clientName
TotalCollection
1
ABC
a , b , c
4200
2
xyz
d
1000
How can I solve this problem?
There are some typos in your query:
the separator inside the GROUP_CONCAT function should be a comma instead of a space, given your current output, though comma is default value, so you can really omit that clause.
each alias in your select requires the table where it comes from, as long as those field names are used in more than one tables among the ones you're joining on
your GROUP BY clause should at least contain every field that is not aggregated inside the SELECT clause in order to have a potentially correct output.
The overall conceptual problem in your query is that the join combines every row of the "employee" table with every row of the "client" table (resulting in multiple rows and higher sum of amounts during the aggregation). One way for getting out of the rabbit hole is a first aggregation on the "client" table (to have one row for each "emp_id" value), then join back with the other tables.
SELECT emp.emp_id,
emp.empName,
cl.clientName,
SUM(coll.Amount)
FROM employee emp
LEFT JOIN (SELECT emp_id,
GROUP_CONCAT(client.clientName
ORDER BY client.id) AS clientName
FROM client
GROUP BY emp_id) cl
ON cl.emp_id = emp.emp_id
LEFT JOIN (SELECT emp_id, Amount FROM collection) coll
ON coll.emp_id = emp.emp_id
GROUP BY emp.emp_id,
emp.empName,
cl.clientName
Check the demo here.
Regardless of my comment, here is a query for your desired output:
SELECT
a.emp_id,
a.empName,
a.clientName,
SUM(col.Amount) AS totalCollection
FROM (SELECT e.emp_id,
e.`empName`,
GROUP_CONCAT(DISTINCT c.clientName ORDER BY c.id ) AS clientName
FROM employee e
LEFT JOIN `client` c
ON c.emp_id = e.emp_id
GROUP BY e.`emp_id`) a
LEFT JOIN collection col
ON col.emp_id = a.emp_id
GROUP BY col.emp_id;
When having multiple joins, you should be careful about the relations and the number of results(rows) that your query generates. You might as well have multiple records in output than your desired ones.
Hope this helps
SELECT emp_id,
empName,
GROUP_CONCAT(client.clientName ORDER BY client.id SEPARATOR '') AS clientName,
C .Amount
FROM employee
LEFT JOIN client
ON clent.emp_id = employee.emp_id
LEFT JOIN (select collection.emp_id , sum(collection.Amount ) as Amount from collection group by collection.emp_id) C
ON C.emp_id = employee.emp_id
GROUP BY employee.emp_id;
it works for me now
I am using MySQL 5.6.
I have a SQL table with a list of users:
id name
1 Alice
2 Bob
3 John
and a SQL table with the list of gifts for each user (numbered in order of preference):
id gift rank
1 balloon 2
1 shoes 1
1 seeds 3
1 video-game 1
2 computer 2
3 shoes 2
3 hat 1
And I would like a list of the preferred gift for each user (the highest rank - if two gifts have the same rank, pick only one randomly) (bonus: if the list could be randomized, that would be perfect!):
id name gift rank
2 Bob computer 2
1 Alice shoes 1
3 John hat 1
I tried to use the clause GROUP BY but without any success.
Considering rank as a part of your data; Without using window functions or complex sub queries
SELECT u.id, u.name, g.gift
FROM users u
JOIN gifts g ON g.id = u.id
LEFT JOIN gifts g2 ON g2.id = g.id AND g2.rank > g.rank
WHERE g2.id IS NULL;
Added link http://sqlfiddle.com/#!9/62f59e/15/0
You can use row_number to get one row for each User.(Mysql 8.0+)
SELECT A.ID,NAME,GIFT,`RANK` FROM USERS A
LEFT JOIN (
SELECT ID,GIFT,`RANK` FROM
(SELECT *,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY `RANK` ASC) AS RN FROM X) X
WHERE RN =1
) B
ON A.ID= B.ID
I do not know DB what you use. And I'm not an expert in SQL(I can have some mistake in next). But I think it is not difficult.
So I can give you just advice that you have to think gradually. Let me write.
First All I need is the highest rank. So I have to get this.
SELECT MAX(RANK)
FROM GIFT
GROUP BY ID
And then I think that I need get gifts from this rank.
SELECT GIFT.*
FROM GIFT
INNER JOIN(
SELECT ID, MAX(RANK)
FROM GIFT
GROUP BY ID
) filter ON GIFT.ID = filter.ID AND GIFT.RANK = filter.RANK
I think this is the table what you want!
So If below code works, That's what you really want.
SELECT *
FROM USER
LEFT OUTER JOIN(
above table
) GIFT ON USER.ID = GIFT.ID
But Remember this, I said I'm not an expert in SQL. There can be better way.
Checkout the query
SELECT tbluser.id,name,gift,rank into tblrslt
FROM tbluser
LEFT JOIN tblgifts
ON tbluser.id = tblgifts.id order by id,rank;
SELECT tt.*
FROM tblrslt tt
INNER JOIN
(SELECT id, min(rank) AS rank
FROM tblrslt
GROUP BY id) groupedtt
ON tt.id = groupedtt.id
AND tt.rank = groupedtt.rank order by id
In MySQL versions older than 8 you have no ranking functions available. You'll select the minimum rank per user instead and use these ranks to select the gift rows. This means you access the gifts table twice.
I suggest this:
select *
fron users u
join gifts g
on g.id = u.id
and (g.id, g.rank) in (select id, min(rank) from gifts group by id)
order by u.id;
If you also want to show users without gifts, simply change the inner join to a left outer join.
I'm trying to query
number of courses passed,
the earliest course passed
time taken to pass first course, for each student who is not currently expelled.
The tricky part here is 2). I constructed a sub-query by mapping the course table onto itself but restricting matches only to datepassed=min(datepassed). The query appears to work for a very sample, but when I try to apply it to my full data set (which would return ~1 million records) the query takes impossibly long to execute (left it for >2 hours and still wouldn't complete).
Is there a more efficient way to do this? Appreciate all your help!
Query:
SELECT
S.id,
COUNT(C.course) as course_count,
C2.course as first_course,
DATEDIFF(MIN(C.datepassed),S.dateenrolled) as days_to_first
FROM student S
LEFT JOIN course C
ON C.studentid = S.id
LEFT JOIN (SELECT * FROM course GROUP BY studentid HAVING datepassed IN (MIN(datepassed))) C2
ON C2.studentid = C.studentid
WHERE YEAR(S.dateenrolled)=2013
AND U.id NOT IN (SELECT id FROM expelled)
GROUP BY S.id
ORDER BY S.id
Student table
id status dateenrolled
1 graduated 1/1/2013
3 graduated 1/1/2013
Expelled table
id dateexpelled
2 5/1/2013
Course table
studentid course datepassed
1 courseA 5/1/2014
1 courseB 1/1/2014
1 courseC 2/1/2014
1 courseD 3/1/2014
3 courseA 1/1/2014
3 couseB 2/1/2014
3 courseC 3/1/2014
3 courseD 4/1/2014
3 courseE 5/1/2014
SELECT id, course_count, days_to_first, C2.course first_course
FROM (
SELECT S.id, COUNT(C.course) course_count,
DATEDIFF(MIN(datepassed),S.dateenrolled) as days_to_first,
MIN(datepassed) min_datepassed
FROM student S
LEFT JOIN course C ON C.studentid = S.id
WHERE S.dateenrolled BETWEEN '2013-01-01' AND '2013-12-31'
AND S.id NOT IN (SELECT id FROM expelled)
GROUP BY S.id
) t1 LEFT JOIN course C2
ON C2.studentid = t1.id
AND C2.datepassed = t1.min_datepassed
ORDER BY id
I would try something like:
SELECT s.id, f.course,
COALESCE( DATEDIFF( c.first_pass,s.dateenrolled), 0 ) AS days_to_pass,
COALESCE( c.num_courses, 0 ) AS courses
FROM student s
LEFT JOIN
( SELECT studentid, MIN(datepassed) AS first_pass, COUNT(*) AS num_courses
FROM course
GROUP BY studentid ) c
ON s.id = c.studentid
JOIN course f
ON c.studentid = f.studentid AND c.first_pass = f.datepassed
LEFT JOIN expelled e
ON s.id = e.id
WHERE s.dateenrolled BETWEEN '2013-01-01' AND '2013-12-31'
AND e.id IS NULL
This query assumes a student can pass only one course on a given day, otherwise you can get more than one row for a student as its possible to have many first courses.
For performance it would help to have an index on dateenrolled in student table and a composite index on (studentid,datepassed) in courses table.
SELECT art.*,arg. FROM rd_articles AS art
LEFT JOIN rd_argument AS arg ON art.cat=arg.id WHERE art.enabled=1 ORDER BY art.id DESC
LIMIT 10
This is simple join query
Article table structure is
ID cat Description Date
1 1 Abc 08-01-2014
2 1 Aaa 10-01-2014
3 2 Abcv 11-01-2014
4 3 Aaa 12-01-2014
5 3 Aaa 14-01-2014
Arguments table is
ID Name
1 A
2 B
3 C
I want pick last updated(Date) one item from each cat.
How ?
This assumes that the enabled column is in rd_articles:
SELECT art.*, arg.*
FROM (
SELECT * FROM rd_articles
INNER JOIN (
SELECT cat, MAX(date) AS maxdate
FROM rd_articles
WHERE enabled = 1
GROUP BY cat
) md ON rd_articles.cat = md.cat AND rd_articles.date = md.maxdate
) art
LEFT JOIN rd_argument AS arg ON art.cat = arg.id
The innermost query gets the maximum date for each category, then joins it to the rd_articles table to get only those rd_articles rows that have the latest date for each article. That becomes the cat alias, which is then left-joined to the arguments table just like in your original query. You can add the LIMIT 10 at the end if needed; I wasn't sure what to do with that.
Note that if there's a tie for a category's latest date, you'll get more than one row for each category. If a tie could happen you'll need to break the tie somehow, for example by using the description or the ID. Let me know if that's the case and I'll update my answer.
SELECT ART.*, ARG.*
FROM ARTICLE AS ART
INNER JOIN RD_AGRUEMENT AS ARG
ON ARG.ID = ART.ID
WHERE (ID, DATE) IN
(SELECT ID, MAX(DATE) FROM ARTICLE GROUP BY ID)
I have two tables: DATA and USERS
USERS
id sqft postal province city
==========================================================
1 1 Y7R BC Vancouver
2 2 Y7R BC Vancouver
3 1 L5B ON Toronto
and
DATA
id uid power
=======================
1 1 1000
2 2 1300
3 1 1500
uid in table DATA matches to id in table USERS
I want to be able to count the the number of distinct uid in DATA where the postal code is Y7R and sqft is 1
SELECT COUNT(id)
FROM `DATA` AS `d`
INNER JOIN `USERS` AS `u`
ON u.id=d.uid
WHERE u.postal='Y7R' AND u.sqft=1
GROUP BY u.id;
They should be distinct anyway if you have a proper schema, if so just remove the group by clause.
SELECT COUNT(DISTINCT D.UID) FROM DATA D
LEFT JOIN USERS U ON D.UID=U.ID
WHERE U.POSTAL='Y7R' AND U.SQFT=1)
In case you need distinct
You can use this solution:
SELECT COUNT(DISTINCT a.id)
FROM USERS a
JOIN DATA b ON a.id = b.uid
WHERE a.sqft = 1 AND
a.postal = 'Y7R'
Try this one:
SELECT COUNT(DISTINCT a.id)
FROM USERS a
INNER JOIN DATA b
ON a.id = b.uid
WHERE a.sqft = 1 AND
a.postal = 'Y7R'