How to handle SQL subqueries with sums - mysql

I am practicing queries on an example database in MySQL.
I have an employee table with a primary key of emp_id.
I have a works_with table with a composite key of emp_id and client_id. It also has a column of total_sales.
I am trying to write a query that returns the name of any employee who has sold over 100,000 total.
I was able to return the employee id and total for sums over 100,000 like so:
SELECT SUM(total_sales) AS total_sales, emp_id
FROM works_with
WHERE total_sales > 100000
GROUP BY emp_id;
But I am unsure how to use this to also get employee name. I have tried nested queries but with no luck. For example when I try this:
SELECT first_name, last_name
FROM employee
WHERE emp_id IN (
SELECT SUM(total_sales) AS total_sales, emp_id
FROM works_with WHERE total_sales > 100000
GROUP BY emp_id
)
I get Error 1241: Operand should contain 1 column(s). I believe this is because I am selecting two columns in the nested query? So how would I handle this problem?

Just join:
select sum(w.total_sales) as total_sales, e.first_name, e.lastnmae
from works_with w
inner join employee e on e.emp_id = w.emp_id
group by e.emp_id
having sum(w.total_sales) > 10000;
Note that I used a having clause rather than the where clause: presumably, you want to sum all sales of each employee, and filter on that result. Your original queried sums only individual values that are greater than 100000.

Adding to GMB's solution.
Take your existing Select and wrap it in a Derived Table/CTE:
SELECT e.first_name, e.last_name, big_sales.total_sales
FROM employee as e
join
(
SELECT SUM(total_sales) AS total_sales, emp_id
FROM works_with
GROUP BY emp_id
HAVING total_sales > 100000
) as big_sales
on e.emp_id = big_sales.emp_id
Now you can show the total_sales plus employee details. Additionally this should be more efficient, because you aggregate & filter before the join.
If you only need to show the employee you can use a SubQuery (like the one you tried), but it must return a single column, i.e. remove the SUM from the Select list:
SELECT first_name, last_name
FROM employee
WHERE emp_id IN (
SELECT emp_id -- , SUM(total_sales) AS total_sales
FROM works_with
GROUP BY emp_id
HAVING SUM(total_sales) > 100000
)

Related

For ALL queries in SQL , Table division

I have a schema with three tables:
Project (project_id,proj_name,chief_arch)
Employee (emp_id,emp_name)
Assigned-to (project_id,emp_id)
I have created all tables with data on http://sqlfiddle.com/#!9/3f21e
You can view the all data (select * ...) on http://sqlfiddle.com/#!9/3f21e/1
Please first view the tables and data on SQLFIDDLE.
I have an existing query to get employee names who work on at least one project where employee 107 also worked:
select EMP_NAME from employee natural join `assigned-to`
WHERE EMP_ID<>'107' AND
PROJECT_ID IN(
SELECT PROJECT_ID FROM `assigned-to`
WHERE EMP_ID='107'
)
GROUP BY EMP_NAME;
SQLFiddle
But now I need to solve a slightly different problem. I need the employee names who on work on ALL projects that employee 107 works on.
How can I write a query for this problem?
Try this:
SELECT EMP_NAME
FROM EMPLOYEE NATURAL JOIN `ASSIGNED-TO`
WHERE EMP_ID<>'107' AND
PROJECT_ID IN (
SELECT PROJECT_ID FROM `ASSIGNED-TO`
WHERE EMP_ID='107'
)
GROUP BY EMP_NAME
HAVING COUNT(*)=(
SELECT COUNT(*)
FROM `ASSIGNED-TO`
WHERE EMP_ID='107'
);
See it run on SQL Fiddle.
You can do this by counting the projects other employees in common with the employee and then selecting only those where the count exactly matches the original employees count.
SELECT EMP_ID FROM `ASSIGNED-TO` WHERE PROJECT_ID IN
(SELECT PROJECT_ID FROM `ASSIGNED-TO` WHERE EMP_ID = '107')
AND EMP_ID <> '107'
GROUP BY EMP_ID
HAVING COUNT(*) = (SELECT COUNT(*) FROM `ASSIGNED-TO` WHERE EMP_ID = '107')
This will work too. I want to validate if the project id in assigned-to is found in project table.
select e.emp_name
from employee e
natural join `assigned-to` a
where emp_id <> 107
and a.project_id in (
select project_id
from (
select emp_id, project_id
from employee natural join `assigned-to` natural join project
where emp_id = 107 ) t
)
group by e.emp_id
having count(project_id) = (select count(project_id) from `assigned-to` where emp_id = 107)

How do I subquery the right results

I am trying to do a join subquery to return just employee names that earn less than 46000 and I can get it to work by also returning the employee id but not without it.
This is how Im doing it.
Select e.eid, e.ename
From employee_table e
Inner Join (
Select salary, eid
from salary
Where salary > 46000
) as s
On e.eid = s.eid;
This beacuse you are using a dinamic temporary table and if you don't select the eid column your temporary dinamica table don't contain this values and the on clause in join fails
Select e.eid, e.ename
From employee_table e
Inner Join (
Select salary, eid
from salary
Where salary < 46000
) as s
On e.eid = s.eid;
you can use an inner join without dinamic temporary table
Select e.eid, e.ename
From employee_table e
INNER JOIN salary s On e.eid = s.eid
where s.salary < 46000
So the salary isn't stored in the employee table, but in a separate salary table. The salary table contains the employee ID. This makes this a 1:n relation, i.e. one employee can have more than one salary.
I don't know your tables, so I don't know the reason for this. Maybe an employee can have many jobs, or there are part salaries like a base salary and additional salaries, or there is a date range stored with the salary to indicate when it is/was valid. I don't know.
Let's say, we can simply add an employee's salaries to get the total. Then we select the employee names from the employee table where we find a salary less than 46000 in the salary table.
select ename
from employee
where eid in
(
select eid
from salary
group by eid
having sum(salary) < 46000
);

MySQL- Can any one please explain this query

I want to understand the following query:
SELECT DISTINCT salary
FROM employees a
WHERE 3 >= (
SELECT COUNT(DISTINCT salary)
FROM employees b
WHERE b.salary <= a.salary
)
ORDER BY a.salary DESC;
Starting from the inner SELECT (a correlated sub-query). Such a query will be executed for each row in the outer query. So what does it do?
Return the number of unique salaries that are less than or equal to the current employee's salary.
SELECT COUNT(DISTINCT salary)
FROM employees b
WHERE b.salary <= a.salary
So, given that number for the current row of the outer select, what does that do? Return the unique salaries (in order) where the number returned from the sub-query is less than or equal to 3.
SELECT DISTINCT salary
FROM employees a
WHERE 3 >= (some number)
ORDER BY a.salary DESC;
Putting it all together, we fetch:
Unique salaries in order where such a salary is one of the worst 3.
I think that this query should return the 3 worst salaries!

MySQL - Group and total, but return all rows in each group

I'm trying to write a query that finds each time the same person occurs in my table between a specific date range. It then groups this person and totals their spending for a specific range. If their spending habits are greater than X amount, then return each and every row for this person between date range specified. Not just the grouped total amount. This is what I have so far:
SELECT member_id,
SUM(amount) AS total
FROM `sold_items`
GROUP BY member_id
HAVING total > 50
This is retrieving the correct total and returning members spending over $50, but not each and every row. Just the total for each member and their grand total. I'm currently querying the whole table, I didn't add in the date ranges yet.
JOIN this subquery with the original table:
SELECT si1.*
FROM sold_items AS si1
JOIN (SELECT member_id
FROM sold_items
GROUP BY member_id
HAVING SUM(amount) > 50) AS si2
ON si1.member_id = si2.member_id
The general rule is that the subquery groups by the same column(s) that it's selecting, and then you join that with the original query using the same columns.
SELECT member_id, amount
FROM sold_items si
INNER JOIN (SELECT member_id,
SUM(amount) AS total
FROM `sold_items`
GROUP BY member_id
HAVING total > 50) spenders USING (member_id)
The query you have already built can be used as a temporary table to join with. if member_id is not an index on the table, this will become slow with scale.
The word spenders is a table alias, you can use any valid alias in its stead.
There are a few syntaxes that will get the result you are looking, here is one using an inner join to ensure that all rows returned have a member_id in the list returned by the group by and that the total is repeated for each a certain member has:
SELECT si.*, gb.total from sold_items as si, (SELECT member_id as mid,
SUM(amount) AS total
FROM `sold_items`
GROUP BY member_id
HAVING total > 50) as gb where gb.mid=si.member_id;
I think that this might help:
SELECT
member_id,
SUM(amount) AS amount_value,
'TOTAL' as amount_type
FROM
`sold_items`
GROUP BY
member_id
HAVING
SUM(amount) > 50
UNION ALL
SELECT
member_id,
amount AS amount_value,
'DETAILED' as amount_type
FROM
`sold_items`
INNER JOIN
(
SELECT
A.member_id,
SUM(amount) AS total
FROM
`sold_items` A
GROUP BY
member_id
HAVING
total <= 50
) AS A
ON `sold_items`.member_id = A.member_id
Results of the above query should be like the following:
member_id amount_value amount_type
==========================================
1 55 TOTAL
2 10 DETAILED
2 15 DETAILED
2 10 DETAILED
so the column amount_type would distinguish the two specific member groups
You could do subquery with EXISTS as an alternative:
select *
from sold_items t1
where exists (
select * from sold_items t2
where t1.member_id=t2.member_id
group by member_id
having sum(amount)>50
)
ref: http://dev.mysql.com/doc/refman/5.7/en/exists-and-not-exists-subqueries.html
In case you need to group by multiple columns, you can use a composite identifier with concatenate in combination with a group by subquery
select id, key, language, group
from translation
--query all key-language entries by composite identifier...
where concat(key, '_', language) in (
--by lookup of all key-language combinations...
select concat(key, '_', language)
from translation
group by key, language
--that occur more than once
having count(*) > 1
)

SQL Query - How to find out how which employees worked at more than one store

I have a relationship table such that it has
employeeID | storeID
What would be the query to find out which employees worked at more than one store?
SELECT employeeID WHERE ???
And possibly also list each different stores just once per employee...
Use group by and having, as in:
select employeeID, count(*) from table group by employeeID having count(distinct storeID) > 1
This will give you the employees working at more than one store. Use this as a subquery to list the stores for each such employee.
you can try -
select distinct employeeID,StoreID from table1
where storeID in
(
select storeID from table1 group by storeID having count(distinct employeeID) >1
)
cor storing count and showing store ID also in one query you can use following query..
select a.employeeID,a.storeID,b.cnt
from table1 a,
(select employeeID,count(*) cnt
from table1
group by employeeID
having count(distinct storeID) >1) b
where a.employeID=b.employeeid