Suppose I have a table which contains invoice lines by row and 6 columns where an employee ID can be tagged to that invoice. Employee IDs cannot be duplicated in a row, however the same employee ID can exist in different columns for different invoice lines. In the table below, REP 1 should have a total amount of 500.
I want to be able to sum up the total amounts by employee ID(REP 1, REP 2, etc..). I can do this with a large union query, but the issue is that I have a list of about 450 employee IDs that I need to sum up. Is there a way I can have one query spit out a list of employee IDs and their total amounts?
I would suggest using cross apply:
select v.e, sum(t.amount)
from t cross apply
(values (slot1), (slot2), (slot3), (slot4), (slot5), (slot6)) v(e)
group by v.e;
Note: This assumes that you are using SQL Server.
Joining the tables is one way...
select e.employee_id,
sum(i.amount)
from invoice i,
join employee e on (e.employee_id in (i.slot1, i.slot2, i.slot3, i.slot4, i.slot5, i.slot6))
group by e.employee_id;
An alternative way...
select employee_id,
sum(amount)
from (
select slot1 employee_id, sum(amount) amount from invoice group by slot1 union
select slot2, sum(amount) from invoice group by slot2 union all
select slot3, sum(amount) from invoice group by slot3 union all
select slot4, sum(amount) from invoice group by slot4 union all
select slot5, sum(amount) from invoice group by slot5 union all
select slot6, sum(amount) from invoice group by slot6
) as q1
group by employee_id;
Related
I have a table containing
customerID, InvoiceID, ProductID, Date, Income,
I need to count the number of clients by the number of invoices
I need to write a query that returns something like
Invoice amount ------ number of client that have that amount of invoices
1 ------------------------ 4
2 ------------------------ 3
4 ------------------------ 7
Here's what I've tried
SELECT COUNT(DISTINCT customerID) AS 'Number of Clients',
COUNT(InvoiceID) AS 'Number of Invoices'
FROM Sheet
GROUP BY COUNT(InvoiceID)
ORDER BY COUNT(InvoiceID)
but I can't use aggregate field in group by
You need a nested select to first calculate the number of invoices per customer. The outer select can then calculate the number of customers for each invoice count.
SELECT [Number of Invoices], COUNT(*) AS [Number of Clients]
FROM (
SELECT customerID, COUNT(DISTINCT InvoiceID) AS [Number of Invoices]
FROM Sheet
GROUP BY customerID
) A
GROUP BY [Number of Invoices]
ORDER BY [Number of Invoices]
A common table expression can also be used in place of the nested select:
;WITH CTE AS (
SELECT customerID, COUNT(DISTINCT InvoiceID) AS [Number of Invoices]
FROM Sheet
GROUP BY customerID
)
SELECT [Number of Invoices], COUNT(*) AS [Number of Clients]
FROM CTE
GROUP BY [Number of Invoices]
ORDER BY [Number of Invoices]
See this db<>fiddle for a demo or this one that contains your originally posted data (but a different result).
(The above has been updated to include DISTINCT to COUNT(DISTINCT InvoiceID) to handle multiple rows with the same customerID and InvoiceID in the originally posted data.)
Consider a table EMPLOYEE with columns empid, deptid and salary to extract all employees who have salaries higher than the avg. of their department
I tried it two ways - 1st way is this:
Select empid, deptid, salary
From employee
Group by empid, deptid, salary
Having salary > (Select avg(salary) From employee)
This returned a correct output.
But
Select empid, deptid, salary, avg(salary) average
From employee
Group by empid, deptid, salary
Having salary > avg(salary)
returned no output as well as no error it was black output.
Software used is Microsoft SQL Server
I wanted to know why second output is not returning any output?
The reason you get no results in your second query is because you are grouping by salary, therefore all salaries within that group will be the same, as such the average will be the same. So if the salary within a particular group is 50k, the average will be 50k, and 50k is not greater than 50k, it is equal.
I'd also assume that empId is your primary key, so grouping by this is (with no other joins) is almost always going to be pointless because by definition you have one row per group.
Your first query also isn't right, you would need to filter the subquery by department to ensure you were only comparing the average within that particular employees department, e.g.
Select empid,deptid,salary
From employee AS e
Group by empid,deptid,salary
Having salary>(Select avg(salary) FROM employee AS e2 WHERE e2.deptId = e.Deptid)
However, since you have moved the aggregate to the subquery, the GROUP BY and HAVING are not necessary and this can be simplified to:
Select empid,deptid,salary
From employee AS e
Where salary>(Select avg(salary) FROM employee AS e2 WHERE e2.deptId = e.Deptid);
I would however be inclined to do this with a windowed function:
SELECT e.EmpID, e.DeptId, e.Salary, e.DeptAvg
FROM ( SELECT e.EmpID,
e.deptid,
e.Salary,
DeptAvg = AVG(e.Salary) OVER(PARTITION BY e.deptid)
FROM employee AS e
) AS e
WHERE e.Salary > e.DeptAvg;
First of all you for each row SQL calculates AVG for particular record so each AVG(salary) will be equal to salary of particular employee. Here you looking for two operations, first find average salary of individual department and second is for particular department find list of employees which have greater than avg salary of that department
;WITH cteavgsalary AS
(SELECT deptid,
AVG(salary) AS avgsalary
FROM employee
GROUP BY deptid)
SELECT e.empid, e.deptid, e.salary, cte.avgsalary
FROM employee e
INNER JOIN cteavgsalary cte ON e.deptid = cte.deptid
AND e.salary > cte.avgsalary
Here cteavgsalary will be table which will hold record of department wise average salary and then you making inner join of that CTE on particular department Id
It will provide you exact output you looking for.
This gives you the salary and avg by dept.
Select deptid, avg(salary) as dept_avg
From employee
Group by deptid
This gives you the ones above avg
Then you join back to find the ones you want.
select e.empid, e.deptid, e.salary
FROM emplyee e
JOIN (
Select deptid, avg(salary) as dept_avg
From employee
Group by deptid
) sub on sub.deptid = e.deptid
WHERE e.salary < sub.dept_avg
I was working through some subquery questions and code below was provided as the answer.
my question:
if the inner query returns two minimum salaries that are the same, but belong to different departments. how will the outer query interpet this? will it recognize that salaries refer to different departments?
SELECT first_name, last_name, salary, department_id
FROM employees
WHERE salary IN ( SELECT MIN(salary)
FROM employees
GROUP BY department_id );
thank you
No, it does not know anything about the department information. You need to change the IN to a JOIN:
SELECT e.first_name, e.last_name, e.salary, e.department_id
FROM employees e
INNER JOIN ( SELECT department_id,IN(salary) AS salary
FROM employees
GROUP BY department_id) s
ON s.department_id=e.department_id
AND s.salary=e.salary;
As the statement is currently written it is going to show you each record from employees table which matches the minimum salary for each department with the salary itself. So, the outer query doesn't know anything about the department_id, meaning that there is no correlation between this attribute from the inner query with the outer query at all. You would need to change your logic for example to a JOIN to achieve that.
You can use rank() :
select e.*
from (select e.*, rank() over (partition by e.department_id order by e.salary) as seq
from employees e
) e
where e.seq = 1;
You can pass additional information/condition like this for the department.
CREATE TABLE employees (
empname VARCHAR(20)
,salary DECIMAL(18, 2)
,department_id INT
)
INSERT INTO employees
VALUES ('A', 100,1), ('B', 100, 2), ('C', 300, 2)
SELECT *
FROM employees
WHERE salary IN (
SELECT MIN(salary)
FROM employees
GROUP BY department_id
HAVING department_id = 2
)
AND department_id = 2;
Here is the output
empname salary department_id
-----------------------------
B 100.00 2
Although, 100.00 is the minimum salary for both A and B, but you have passed information for department id: 2. So, only B has come in the output.
let's say employee table has employee details and deptId of the employee.
to get the number of employees in each deptid,
select deptId, COUNT(*) from employee group by deptId;
question is: to get the deptId having max number of employees of the above result set,
select Top 1 deptId, COUNT(*) from employee group by deptId order by 2 desc
(2-ref to second column in the query list) - will do.. but
Is there anyway to avoid ordering this set? or better way of writing this sql,
thanks
If you just want the MAX number of employees within a department, you can do this:
SELECT TOP 1 DepartmentID,
COUNT(EmployeeID)
FROM EmployeeTable
GROUP BY DepartmentID
ORDER BY COUNT(EmployeeID) DESC
Without any ordering, it is hard, but try
Select deptId, cnt
From (Select deptId, count(*) cnt
from employee
Group By deptId) Z
Where cnt = (Select Max(cnt)
From (Select deptId, count(*) cnt
From employee
Group By deptId) ZZ)
I have a PURCHASES table that has a unique ID of PURCHASE_ID. I want to break down how many distinct customers purchases they had per year that had at least 2 unique purchases. This is the query that I wrote:
SELECT
YEAR(PURCHASE_TIME) PURCHASE_YEAR,
COUNT(DISTINCT CUSTOMER_ID) TOTAL_CUSTOMERS
FROM PURCHASES
GROUP BY YEAR(PURCHASE_TIME)
HAVING COUNT(PURCHASE_ID) > 1
However, this query is giving me the total distinct patients per year of purchase no matter how many purchases they had. Meaning, that I am getting customers that had only 1 purchase for the year AND those that had more than one. It is as if the HAVING clause is being ignored.
It doesn’t change anything if I use a HAVING COUNT(DISTINCT PURCHASE_ID) > 1 either. Even though I shouldn’t technically need that since the PURCHASE_ID is already unique and is a primary key.
This works though.
SELECT
PURCHASE_YEAR,
COUNT(DISTINCT CUSTOMER_ID) TOTAL_CUSTOMERS
FROM
(
SELECT
YEAR(PURCHASE_TIME) PURCHASE_YEAR,
CUSTOMER_ID
FROM PURCHASES
GROUP BY YEAR(PURCHASE_TIME),CUSTOMER_ID
HAVING COUNT(PURCHASE_ID) > 1
) VW
GROUP BY PURCHASE_YEAR
try this:
SELECT PURCHASE_YEAR,
COUNT(1) AS CNT
FROM
(SELECT YEAR(PURCHASE_TIME) PURCHASE_YEAR,
CUSTOMER_ID
FROM PURCHASES
GROUP BY YEAR(PURCHASE_TIME),
CUSTOMER_ID
HAVING COUNT(1) > 1) AS CNT
GROUP BY PURCHASE_YEAR
ORDER BY PURCHASE_YEAR
Try the following:
SELECT
YEAR(PURCHASE_TIME) PURCHASE_YEAR,
COUNT(CUSTOMER_ID) TOTAL_CUSTOMERS
FROM PURCHASES
GROUP BY YEAR(PURCHASE_TIME), CUSTOMER_ID
HAVING COUNT(PURCHASE_ID) > 1
Try adding DISTINCT to your filter:
SELECT
YEAR(PURCHASE_TIME) PURCHASE_YEAR,
COUNT(DISTINCT CUSTOMER_ID) TOTAL_CUSTOMERS
FROM PURCHASES
GROUP BY YEAR(PURCHASE_TIME)
HAVING COUNT(DISTINCT PURCHASE_ID) > 1