Problem:
The Employee table holds the salary information in a year.
Write a SQL to get the cumulative sum of an employee's salary over a period of 3 months but exclude the most recent month.
The result should be displayed by 'Id' ascending, and then by 'Month' descending.
Employee table:
| Id | Month | Salary |
|----|-------|--------|
| 1 | 1 | 20 |
| 2 | 1 | 20 |
| 1 | 2 | 30 |
| 2 | 2 | 30 |
| 3 | 2 | 40 |
| 1 | 3 | 40 |
| 3 | 3 | 60 |
| 1 | 4 | 60 |
| 3 | 4 | 70 |
My Code:
SELECT t1.Id, t1.Month,
(SELECT SUM(Salary)
FROM Employee AS t2
WHERE t1.Id = t2.Id
AND t1.Month >= t2.Month) AS Salary
FROM Employee t1
WHERE Month <> (SELECT
MAX(Month)
FROM Employee
GROUP BY t1.Id)
ORDER BY Id, Month DESC;
My Output:
| Id | Month | Salary |
|----|-------|--------|
| 1 | 3 | 90 |
| 1 | 2 | 50 |
| 1 | 1 | 20 |
| 2 | 2 | 50 |
| 2 | 1 | 20 |
| 3 | 3 | 100 |
| 3 | 2 | 40 |
Expected:
| Id | Month | Salary |
|----|-------|--------|
| 1 | 3 | 90 |
| 1 | 2 | 50 |
| 1 | 1 | 20 |
| 2 | 1 | 20 |
| 3 | 3 | 100 |
| 3 | 2 | 40 |
I used MAX() and GROUP BY() functions to exclude the most recent month of each group, but it doesn't work for Id=2.
Is there any advice on how to get rid of the following row?
| 2 | 2 | 50 |
Thanks in advance.
To only get the cumulative sum for the last 3 months, excluding the most recent month per id, you can use
SELECT t1.Id, t1.Month, SUM(t2.Salary)
FROM Employee t1
JOIN Employee t2 ON t1.Id = t2.Id AND t1.Month - t2.Month <= 2 AND t1.Month - t2.Month >= 0
JOIN (SELECT id, MAX(month) as max_mth from Employee GROUP BY id) tmax on tmax.id=t1.id AND tmax.max_mth<>t1.month
GROUP BY t1.Id, t1.Month
ORDER BY t1.Id, t1.Month DESC;
Try this:
SELECT t1.id, t1.month,
(SELECT SUM(salary)
FROM employee t2
WHERE t1.id = t2.id
AND t1.month >= t2.month
AND t1.month - t2.month < 3) AS salary
FROM (
SELECT * FROM employee p
WHERE month <> (select MAX(month)
FROM employee c where c.id = p.id)) t1
ORDER BY id, month desc;
Output is:
+------+-------+--------+
| id | month | salary |
+------+-------+--------+
| 1 | 3 | 90 |
| 1 | 2 | 50 |
| 1 | 1 | 20 |
| 2 | 1 | 20 |
| 3 | 3 | 100 |
| 3 | 2 | 40 |
+------+-------+--------+
The problem you were having was that you were deleting only the last month present across all employees. What I believe you wanted was to delete the last month present for each employee even if that last month was several months back. This solution creates a derived table where the last month is missing for each employee and uses that in place of your t1 employee table.
I think this answer is closest to what you were trying to do in your original query:
SELECT t1.id, t1.month,
(SELECT SUM(salary)
FROM employee t2
WHERE t1.id = t2.id
AND t1.month >= t2.month
AND t1.month - t2.month < 3) AS salary
FROM employee t1
WHERE month <> (SELECT MAX(month)
FROM employee t3
WHERE t3.id = t1.id)
ORDER by id, month desc;
On second look you were actually pretty close. I believe the problem was that the "GROUP BY t1.Id" line doesn't actually group anything because t1.Id is constant for any given subquery as "t1" is defined in the outtermost select statement. Replace it with a where clause and limit the total to 3 months in the SUM() query, and you're there.
Try this query:
SELECT e.Id, e.Month, SUM( e2.Salary ) AS 'Salary'
FROM
Employee AS e
INNER JOIN Employee AS e2
ON e2.Id = e.Id
AND e2.Month <= e.Month
WHERE
e.Month <> ( SELECT MAX( [Month] ) FROM Employee WHERE Id = e.Id )
GROUP BY
e.Id, e.Month
ORDER BY
e.Id, e.Month DESC
Output is:
+----+-------+--------+
| Id | Month | Salary |
+----+-------+--------+
| 1 | 3 | 90 |
| 1 | 2 | 50 |
| 1 | 1 | 20 |
| 2 | 1 | 20 |
| 3 | 3 | 100 |
| 3 | 2 | 40 |
+----+-------+--------+
Related
I have two tables and want to display the results for MAX of SUM total, group by name and month.
only display maximum data from the total SUM results every month and group by name.
this my table
table t2
+-------+-------+-------+
| month | total | id_t3 |
+-------+-------+-------+
| 1 | 15 | 1 |
| 1 | 20 | 2 |
| 1 | 50 | 1 |
| 2 | 40 | 2 |
| 2 | 20 | 3 |
| 2 | 20 | 1 |
| 3 | 10 | 3 |
+-------+-------+-------+
table t3
+----+--------+
| id | name |
+----+--------+
| 1 | brian |
| 2 | jessi |
| 3 | redy |
+----+---------
i want result
+-------+---------------+
| month | total | name |
+-------+---------------+
| 1 | 65 | brian |
| 2 | 40 | jessi |
| 3 | 10 | redy |
+-------+---------------+
And this my query SQL
select t2.month, MAX(total), t3.name
from (
select t2.month, SUM(t2.total)as total, t3.name
from t2, t3
where t2.idt3 = t3.id
group by t2.month, t3.name
)result
group by t2.month, t3.name
order by total DESC
Hope it will solve your problem. group by month and name
select t2.month, SUM(t2.total), t3.name
from t2
join t3 on t2.id_t3 = t3.id
group by t2.month, t3.name
order by t2.total desc
If you want the maximum name per month, use window functions:
select month, name, total
from (select t2.month, t3.name, sum(t2.total) as total
row_number() over (partition by t2.month order by sum(t2.total) desc) as seqnum
from t2 join
t3
on t2.id_t3 = t3.id
group by t2.month, t3.name
) t
where seqnum = 1
order by month;
I have a table t1 as below:
-----------------------------
| date | id | value |
-----------------------------
| 2/28/2019 | 1 | 55 |
| 2/28/2019 | 2 | 44 |
| 2/28/2019 | 3 | 33 |
| 2/26/2019 | 1 | 22 |
| 2/26/2019 | 2 | 12 |
| 2/26/2019 | 3 | 11 |
-----------------------------
I want to take abc3 from t1 and then find abc3 value for date - 1 day in the same table t1 and display both records.
I created a query as:
select
a.date, a.id, (a.value - b.value) as 'difference-from-previous-day'
FROM
t1 a
INNER JOIN
t1 b
ON
a.ID = b.ID
WHERE
b.DATE in (dateadd(day, -1, a.DATE));
And that works fine when dates are all there.
But there are gaps in dates (e.g. when we look at data for Monday and there is a 'weekend' before Monday - there is no data in table t1 for last day -1 so this query shows NULL for Monday)...
How to write a query to pickup last date prior to current date (and NOT -1 date) for which data exists?
Expecting result:
2/28/2019 | 1 | 33 (which is "55 - 22")
2/28/2019 | 2 | 32 (which is "44 - 12")
2/28/2019 | 3 | 22 (which is "33 - 11")
Thanks.
You seem to want:
select t1.*
from t1
where t1.value = 'abc'
order by t1.date desc
limit 2;
You need a self join on the table like this:
select
t.date,
t.id,
t.value - tt.value diff
from t1 t inner join t1 tt
on tt.id = t.id
and tt.date = (select max(date) from t1 where date < t.date)
Maybe the inner join can be changed to a left join to cover the case where there is not a previous row for each id.
See the demo.
Results:
| date | id | diff |
| ------------------- | --- | ---- |
| 2019-02-28 00:00:00 | 1 | 33 |
| 2019-02-28 00:00:00 | 2 | 32 |
| 2019-02-28 00:00:00 | 3 | 22 |
I have a table t1 as below
-----------------------------
| date | id | value |
-----------------------------
| 2/28/2019 | 1 | abc1 |
| 2/28/2019 | 2 | abc2 |
| 2/28/2019 | 3 | abc3 |
| 2/27/2019 | 1 | abc4 |
| 2/27/2019 | 2 | abc5 |
| 2/27/2019 | 3 | abc3 |
-----------------------------
I want to take abc3 from t1 and then find abc3 value for date - 1 day in the same table t1 and display both records.
In this case it would be 2 records:
-------------------------------
| date | id | value |
-------------------------------
| 2/28/2019 | 3 | abc3 |
| 2/27/2019 | 3 | abc3 |
-------------------------------
How to achieve that?
Thanks.
Is this what you want?
select t.*
from t
where value = 'abc3'
order by date desc
limit 2;
Or, do you want to find abc3 because the value is the same on two consecutive days?
select t.*
from t
where value = 'abc3' and
exists (select 1
from tablename t2
where t2.value = t.value and
t2.date in (t.date - interval 1 day, t.date + interval 1 day)
);
You can use EXISTS:
select t.*
from tablename t
where
value = 'abc3'
and
exists (
select 1 from tablename
where value = 'abc3' and date in (t.date - INTERVAL 1 DAY, t.date + INTERVAL 1 DAY)
)
See the demo.
I have a table structure as follows
| user_id | value | date |
|---------|-------|------------|
| 1 | 5 | 2017-09-01 |
| 2 | 6 | 2017-09-01 |
| 1 | 1 | 2017-09-02 |
| 1 | 2 | 2017-09-03 |
| 2 | 9 | 2017-09-02 |
| 1 | 3 | 2017-09-04 |
| 2 | 5 | 2017-09-04 |
| 2 | 5 | 2017-09-05 |
| 1 | 1 | 2017-09-05 |
| 1 | 5 | 2017-09-06 |
| 1 | 6 | 2017-09-07 |
| 1 | 3 | 2017-09-08 |
| 1 | 4 | 2017-09-09 |
| 2 | 6 | 2017-09-06 |
| 1 | 1 | 2017-09-10 |
I have another table where initial cutoff date for user_ids are given like
| user_id | date |
|---------|------------|
| 1 | 2017-09-04 |
| 2 | 2017-09-05 |
The final cutoff date for all the user is 2017-09-08
I want to get the sum of values aggregated by user_id
What I have tried is
SELECT user_id, SUM(value) as Total
FROM table
WHERE date >= $DATE and date <= '2017-09-08'
GROUP BY user_id
I am stuck how should I deal with $DATE as it is variable for each user
The solution in the case should be
| user_id | Total |
|---------|------------|
| 1 | 18 |
| 2 | 11 |
Say you have table names as users and cutoff. cutoff having cutoff date for each user. Try this and let me know if it works or not.
select u.user_id,sum(u.value) Total
from users u join cutoff c on u.user_id=c.user_id and (u.date>=c.date and u.date<='2017-09-08')
group by u.user_id
SELECT x.user_id, SUM(x.value) as Total
FROM table x
WHERE x.date >= (select date from table2 where user_id = x.user_id)
and x.date <= '2017-09-08'
GROUP BY x.user_id
Using where exists
SELECT a.user_id, SUM(a.value) as Total
FROM table1 a
WHERE EXISTS (
SELECT 1
FROM table2 b
WHERE a.user_id = b.user_id
AND a.date >= b.date and a.date <= '2017-09-08'
)
GROUP BY user_id
DEMO
OR using conditional aggregation
SELECT a.user_id,
SUM(CASE WHEN a.date >= b.date and a.date <= '2017-09-08'
THEN a.value
ELSE 0
END) as Total
FROM table1 a
JOIN table2 b ON a.user_id = b.user_id
GROUP BY user_id
DEMO
I have this table:
SALESMAN | INVOICE | VALUE
1 | 7470 | 10
1 | 7471 | 20
1 | 7472 | 30
2 | 7473 | 40
2 | 7474 | 50
I want a query in order to get this result:
SALESMAN | INVOICE | VALUE | TOTAL_VALUE | TOTAL_ITEMS
1 | 7470 | 10 | 40 | 3
1 | 7471 | 20 | 40 | 3
1 | 7472 | 10 | 40 | 3
2 | 7473 | 40 | 90 | 2
2 | 7474 | 50 | 90 | 2
TOTAL_VALUE is the sum of all VALUE for the same SALESMAN.
TOTAL_ITEMS is the amount of rows with the same SALESMAN.
Is possible to achieve this in MySql?
Use GROUP BY
SELECT s.SALESMAN, s.INVOICE, s.VALUE,
xx.TOTAL_VALUE, xx.TOTAL_ITEMS
FROM sales s JOIN
(SELECT SALESMAN,
SUM(VALUE) AS TOTAL_VALUE,
COUNT(value) AS TOTAL_ITEMS
FROM sales
GROUP BY SALESMAN) xx ON S.SALESMAN = xx.SALESMAN;
this query:
SELECT SALESMAN, INVOICE , VALUE,
(select sum(VALUE) FROM your_table t1 where t1.SALESMAN = your_table.SALESMAN)
AS TOTAL_VALUE,
(select count(VALUE) FROM your_table t2 where t2.SALESMAN = your_table.SALESMAN)
AS TOTAL_ITEMS
from your_table