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.
Related
Sample Data:
id | room_id | seat_num
----------------------------------
1 | 1 | null
2 | 1 | null
3 | 2 | null
4 | 2 | null
Desire Data:
id | room_id | seat_num
----------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
4 | 2 | 2
how to write a sql to update the room seat number to serial num in MySQL 5.7? the room's seat is from 2-20.
One option uses the update/join syntax. In MySQL 5.7, where window functions are not available, you can emulate row_number() with a correlated subquery (which is somehow safer than user variables):
update mytable t
inner join (
select id,
(select count(*) from mytable t1 where t1.room_id = t.room_id and t1.id <= t.id) rn
from mytable t
) t1 on t1.id = t.id
set t.seat_num = t1.rn
Demo on DB Fiddle:
id | room_id | seat_num
:- | ------: | :-------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
4 | 2 | 2
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 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
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 |
+----+-------+--------+
I have following mysql table:
===================================
id | uid | vid | date | num |
===================================
1 | 1 | 1 | 2017-01-01 | |
2 | 1 | 1 | 2017-02-20 | |
3 | 2 | 1 | 2017-02-25 | |
4 | 1 | 2 | 2017-03-05 | |
5 | 1 | 1 | 2017-04-01 | |
===================================
I have to update num column in such way that if the date is the earliest date for the same uid & vid, then the num column will be 1, otherwise it will be 2. So, after updating the database/table the table will be:
===================================
id | uid | vid | date | num |
===================================
1 | 1 | 1 | 2017-01-01 | 1 |
2 | 1 | 1 | 2017-02-20 | 2 |
3 | 2 | 1 | 2017-02-25 | 1 |
4 | 1 | 2 | 2017-03-05 | 1 |
5 | 1 | 1 | 2017-04-01 | 2 |
===================================
As 2017-01-01 is the earliest date among id#1, id#2 & id#5 [uid=1 & vid=1], so num is 1 for id=1 and in case of id#2 & id#5, the num values are 2.
But although uid=1 is for id#4 but vid=2 in that case. So it will be num=1 as 2017-03-05 is the earliest and only one date for id=4 uid & vid combination. In the same way, num is 1 for id#3 as uid & vid are different.
How can I create a single mysql query, so that it will update the num column automatically based on the uid, vid & date?
update your_table t
left join (
select
uid, vid, min(id) as id
from your_table t
join (
select uid, vid, min(date) as date
from your_table
group by uid, vid
) t2 using (uid, vid, date)
group by uid, vid
) t2 on t.uid = t2.uid
and t.vid = t2.vid
and t.id = t2.id
set num = case when t2.id is null then 2 else 1 end;