"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
Related
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]
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.
Suppose that we have following tables:
company company_has_employee employee
-------------------------------------------------------------
id company_id id
companzy_name employee_id emplyee_name
How to create SQL Query, which retrieves any two employees, who works for the same company and what is this company?
Assuming juergen d's joins of the table are correct, I will modify the query to
select top 2 company_name, e.employee_name
from compyny c
join company_has_employee ce on ce.company_id = c.id
join employee e on e.id = cs.employee_id
group by company_name
having count(e.id) > 1
This will always return the top 2 employees
juergen d's original query will always return the first and last employees based on their ID.
If you want two employees chosen randomly, then you can try this:
select top 2 company_name, e.employee_name
from compyny c
join company_has_employee ce on ce.company_id = c.id
join employee e on e.id = cs.employee_id
group by company_name
having count(e.id) > 1
order by RAND((e.ID)*DATEPART(millisecond, GETDATE()))
The last order by clause will change the order of records randomly and you will always get the top 2 of a random order...which means 2 random employees will be selected each time the query is run.
select company_name,
min(e.employee_name) as emp1,
max(e.employee_name) as emp2
from compyny c
join company_has_employee ce on ce.company_id = c.id
join employee e on e.id = cs.employee_id
group by company_name
having count(e.id) > 1
I have three tables that I'm working with.
AccountingLine - Holds the generic account details
Budget - Holds the budget data for each AccountingLine (Many rows per AccountingLine)
Actual - Holds the actual cost data for each AccountingLine (Many rows per AccountingLine)
I'm trying to get the results in a single query which will return ALL ROWS from the AccountingLine table, and SUM the Amounts for each AccountingLine from the Budget and Actuals table.
Using the SQL below, the SUM isn't working for the Budget or Actual data. If I remove one of the joins and one of the SUM functions then it calculates correctly for the single joined table. Very strange... anyone run across this with multiple SUM functions on three or more tables in MySQL?
SELECT A.*, SUM(B.`amount`) AS BudgetAmount, SUM(ACT.`amount`) as ActualAmount
FROM accounting_line A
LEFT JOIN budget B ON B.accounting_line_id = A.accounting_line_id
LEFT JOIN actual ACT ON ACT.accounting_line_id = A.accounting_line_id
GROUP BY A.`accounting_line_id`
By issuing the statement above, I'd expect to see the accounting_line fields, the SUM of the Budget amounts for each accounting_line and the SUM of the Actual amounts for each accounting_line.
I've searched all over and can't find an instance of multiple SUM functions. Thanks so much for any advice.
Josh
Table Data is below:
Table: AccountingLine
act_line_id department
----------------------------------
1 Sales
2 HumanResources
Table: Budget
budget_id actg_line_id amount
----------------------------------------------
1 1 3500.00
2 2 5000.00
3 2 15000.00
Table: Actual
actual_id actg_line_id amount
----------------------------------------------
1 1 1000.00
2 2 500.00
3 2 9000.00
A join repeats each matching row in the other table. So if you have 3 rows in three tables and join them together, you end up with 9 rows. If you sum, each sum from the second and third table is 3x too high.
One solution is to sum in a subquery, so that the join only finds one row:
SELECT A.*
, B.SumAmount as BudgetAmount
, ACT.SumAmount as ActualAmount
FROM accounting_line A
LEFT JOIN
(
select accounting_line_id
, sum(amount) as SumAmount
from budget
group by
accounting_line_id
) as B
ON B.accounting_line_id = A.accounting_line_id
LEFT JOIN
(
select accounting_line_id
, sum(amount) as SumAmount
from actual
group by
accounting_line_id
) as ACT
ON ACT.accounting_line_id = A.accounting_line_id
try this modified one, calculate it's totals on a subquery
SELECT a.*, b.totalBudget, c.totalActual
FROM AccountingLine a LEFT JOIN
(
SELECT actg_line_id, SUM(amount) totalBudget
FROM Budget
GROUP BY actg_line_id
) b on a.act_line_id = b.actg_line_id
LEFT JOIN
(
SELECT actg_line_id, SUM(amount) totalActual
FROM Actual
GROUP BY actg_line_id
) c on a.act_line_id = c.actg_line_id
SQLFiddle Demo
Try this
Select A.* ,SUM(B.Amount) As BudgetAmount,SUM(Act.Amount) As ActualAmount
from AccountingLine A
INNER JOIN Budget B
ON B.budget_id = A.actg_line_id
INNER JOIN Actual Act
ON Act.actual_id = A.accounting_line_id
Grounp By A.accounting_line_id
Suppose I have the following two tables in my MySQL database:
Table 1:: EMP: EmpID, EmpName
eg. (1, 'John'), (2,'Alex'),(3,'Tom')
Table 2:: Team: TeamID, ManagerID, MemberID
eg. record1: (Team1, 1, 2), record2: (Team1, 1, 3)
so there is a team with id team1, John is the manager and Alex and Tom are its members.
I want to display the records of the Team table on the screen in the following manner
| Team | Manager | Members |
| team1 | John | Alex, Tom |
What should be SQL query which will join the above two tables and return me the names of the members when based on the memberIDs.
Also the result will be displayed as 1 row containing all the team members separated by a comma.
If there is a better way of designing these two tables then please suggest that also. It will be much appreciated.
Thanks.
I think you need to use GROUP_CONCAT. GROUP_CONCAT() function is used to concatenate column values into a single string. It is very useful if you would otherwise perform a lookup of many row and then concatenate them on the client end.
SELECT b.TeamID as TeamName,
a.EmpName as Manager,
GROUP_CONCAT(c.EmpName) Members
FROM Emp a
INNER JOIN Team b
ON a.EmpID = b.ManagerID
INNER JOIN Emp c
ON b.MemberID = c.EmpID
GROUP BY b.TeamID, a.EmpName
You can also change the separator, and the ordering
GROUP_CONCAT( c.EmpName SEPARATOR '-' ),...
GROUP_CONCAT( c.EmpName ORDER BY c.EmpName DESC ),...
SQLFiddle Demo
Using GROUP_CONCAT(expr) you can try something like
This function returns a string result with the concatenated non-NULL
values from a group. It returns NULL if there are no non-NULL values.
The default separator between values in a group is comma (“,”).
SELECT t.TeamID Team,
m.EmpName manager,
GROUP_CONCAT(mem.EmpName) Members
FROM Team t INNER JOIN
Emp m ON t.ManagerID = m.EmpID INNER JOIN
Emp mem ON t.MemberID = mem.EmpID
GROUP BY t.TeamID Team,
m.EmpName
You need to join the EMP table twice and use group_concat to list the members seperately.
select TeamID as Team,
e2.EmpName as Manager,
group_concat(e1.EmpName) as Members
from Team t
left outer join EMP e1 on t.MemberID = e1.EmpID
left outer join EMP e2 on t.ManagerID = e2.EmpID
group by TeamID
If you use a left outer join then you will get results even if there is no manager or members for a team. If you only want teams with members and manager you can use inner join.
Define a view called vTeam with this definition:
SELECT dbo.Team.TeamID, dbo.EMP.EmpName AS Manager, EMP_1.EmpName
AS Member, EMP_1.EmpID AS MemberID, dbo.EMP.EmpID AS ManagerID FROM
dbo.EMP AS EMP_1 RIGHT OUTER JOIN
dbo.Team ON EMP_1.EmpID = dbo.Team.MemberID LEFT OUTER JOIN
dbo.EMP ON dbo.Team.ManagerID = dbo.EMP.EmpID
This query will give you your result:
SELECT TeamID, Manager, MemberList = STUFF((
SELECT ',' + Member FROM vTeam as xx
WHERE xx.TeamID = x.TeamID and x.Manager = xx.Manager
FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 1, '') FROM dbo.vTeam AS x GROUP BY TeamID,Manager;
Team1 John Alex,Tom
Team2 Alex John,Alex,Tom