I am trying to perform two different SUMs within a single query.
user points date_achieved
-----------------------------
Foo 10 2000-01-01
Foo 20 2011-11-18
Bar 10 2000-01-01
Bar 20 2011-11-15
Bar 30 2011-11-18
1) Total points
SELECT user, SUM(points) AS points_total
FROM myTable
GROUP BY user
2) Points accumulated within the past month
SELECT user, SUM(points) AS points_month
FROM myTable
WHERE date_achieved >= CURRENT_DATE - INTERVAL 1 MONTH
GROUP BY user
What is the best way to achieve this with a single query?
Many thanks.
Just use a case statement to only count points when it meets your criteria.
SELECT
user,
SUM(points) AS points,
SUM(case
when date_achieved >= (CURRENT_DATE - INTERVAL 1 MONTH)
THEN points
else 0 END) points_last_Month
FROM myTable
GROUP BY user
select
user,
sum(points) as points_total,
sum(
case
when date_achieved >= current_date - interval 1 month then points
else 0
end
) as points_month
from myTable
group by user
Related
I have a MySQL db users table that has a column called lastLogin this is just a simple timestamp to indicate when a user has last logged into the system.
e.g
id
name
lastLogin
accountId
2
bob
1639572638
4
3
tim
1639572638
4
3
ant
1639572638
5
4
leroy
1339572638
6
expected results
accountId
activeUsers
4
2
5
1
6
0
My current query returns 0 rows but not sure why
SELECT accountId, from_unixtime(lastLogin) as lastlogin, count(distinct(id)) as activeUsers
FROM user
HAVING lastlogin > now() - INTERVAL 30 day
ORDER BY lastlogin desc;
SELECT accountId,
FROM_UNIXTIME(MAX(lastlogin)) lastlogin, -- not listed in desired output
-- but present in the query
SUM(lastlogin > UNIX_TIMESTAMP(CURRENT_DATE - INTERVAL 30 DAY)) activeUsers
FROM user
GROUP BY accountId
For distinct id use
SELECT accountId,
FROM_UNIXTIME(MAX(lastlogin)) lastlogin,
COUNT(DISTINCT CASE WHEN lastlogin > UNIX_TIMESTAMP(CURRENT_DATE - INTERVAL 30 DAY) THEN id END) activeUsers
FROM user
GROUP BY accountId
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=f754e9ed49d872d0d68173a803f96126
Try this:
with u as
(select accountId, count(distinct id) as activeUsers from user
group by accountId
having FROM_UNIXTIME(max(lastlogin)) > now() - INTERVAL 30 day),
v as
(select distinct accountId from user)
(select v.accountId, coalesce(u.activeUsers, 0) as activeUsers from v left join
u on v.accountId = u.accountId)
Fiddle
okay i figured it out hopefully helps someone else -
SELECT accountId,count(distinct(id)) as activeUsers
FROM user
WHERE FROM_UNIXTIME(lastlogin) > now() - INTERVAL 30 day
GROUP BY accountId;
ID
TIMESTAMP
1
2020-01-01 12:00:00
2
2020-02-01 12:00:00
3
2020-05-01 12:00:00
4
2020-06-01 12:00:00
5
2020-07-01 12:00:00
I am looking for a way to get records in a MySQL database that are within a certain range of each other. In the above example, notice that there is a month between the first two records, then a three month gap, before we see another three records with a month between.
What is a way to group these into two result sets, so I will get Ids 1, 2 and 3, 4, 5 A solution using days would be probably work the best as thats easier to modify.
You can use lag() and then logic to see where a gap is big enough to start a new set of records. A cumulative sum gives you the groups you want:
select t.*,
sum(case when prev_timestamp >= timestamp - interval 1 month then 0 else 1 end) over (order by timestamp) as grouping
from (select t.*,
lag(timestamp) over (order by timestamp) as prev_timestamp
from t
) t;
If you want to summarize this with a start and end date:
select min(timestamp), max(timestamp)
from (select t.*,
sum(case when prev_timestamp >= timestamp - interval 1 month then 0 else 1 end) over (order by timestamp) as grouping
from (select t.*,
lag(timestamp) over (order by timestamp) as prev_timestamp
from t
) t
) t
group by grouping;
For example, the following query:
select group_concat(ID)
from (
select w1.ID,w1.TS,w2.ID flag
from work1 w1 left outer join work1 w2
on timestampdiff(month,w2.TS,w1.TS)=1
order by w1.ID
) w
group by
case when flag is null then #str:=ID else #str end
See db fiddle
I need to display week over week difference with mysql in Week Over Week Users column. My data looks like the following:
Date Users Week Over Week Users
06-01-2019 10 10
06-08-2019 15 15
06-15-2019 5 5
Currently, Week Over Week Users only reflects the data that I have in Users column. The desired output would be:
Date Users Week Over Week Users
06-01-2019 10 10
06-08-2019 15 5
06-15-2019 5 -10
Basically if on the second week the number of users grew up to 15 users, then I need to display 5 (as in +5 users since last week, so new week Users - last week Users would be the formula)
Here is my code:
(
SUM(
CASE
WHEN WEEK(`Date`) = WEEK(CURRENT_DATE()) THEN `Users`
ELSE 0
END
) - SUM(
CASE
WHEN WEEK(`Date`) = WEEK(CURRENT_DATE()) - 1 THEN `Users`
ELSE 0
END
)
)
But it doesn't work as it duplicates the Users column.
You want lag():
select t.*,
(users - lag(users, 1, 0) over (order by date)) as week_over_week
from t;
If you are running MySQL 5.x, where window functions such as lag() are not available, you can use a correlated subquery to get the "previous" value:
select
t.date,
t.users,
t.users - coalesce(
(
select t1.users
from mytable t1
where t1.date < t.date
order by t1.date desc
limit 1
),
0
) week_over_week_users
from mytable t
I have a MySQL table with 5 columns:
Device | Name | Date | Source | Income
Neither Device nor Name nor Source are unique.
I am trying to write the SQL for getting the following:
Device | Name | Source | Income (for last 3 days) | Income (for last 9 days) | Income (for last 12 days)
What is the best way to do this?
You can get a conditional SUM() via CASE statements:
SELECT Device
,Name
,Source
,SUM(CASE WHEN date BETWEEN UNIX_TIMESTAMP(NOW() - INTERVAL 3 day) AND UNIX_TIMESTAMP(NOW()) THEN Income END) AS Last_3_Days
,SUM(CASE WHEN date BETWEEN UNIX_TIMESTAMP(NOW() - INTERVAL 9 day) AND UNIX_TIMESTAMP(NOW()) THEN Income END) AS Last_9_Days
FROM YourTable
GROUP BY Device
,Name
,Source
SELECT COUNT(*) as total_l3d FROM table WHERE DATEDIFF(Date, NOW()) <= 3
UNION
SELECT COUNT(*) as total_l9d FROM table WHERE DATEDIFF(Date, NOW()) <= 9
UNION
SELECT COUNT(*) as total_l12d FROM table WHERE DATEDIFF(Date, NOW()) <= 12
I have a query which returns the total of users who registered for each day. Problem is if a day had no one register it doesn't return any value, it just skips it. I would rather it returned zero
this is my query so far
SELECT count(*) total FROM users WHERE created_at < NOW() AND created_at >
DATE_SUB(NOW(), INTERVAL 7 DAY) AND owner_id = ? GROUP BY DAY(created_at)
ORDER BY created_at DESC
Edit
i grouped the data so i would get a count for each day- As for the date range, i wanted the total users registered for the previous seven days
A variation on the theme "build your on 7 day calendar inline":
SELECT D, count(created_at) AS total FROM
(SELECT DATE_SUB(NOW(), INTERVAL D DAY) AS D
FROM
(SELECT 0 as D
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
) AS D
) AS D
LEFT JOIN users ON date(created_at) = date(D)
WHERE owner_id = ? or owner_id is null
GROUP BY D
ORDER BY D DESC
I don't have your table structure at hand, so that would need adjustment probably. In the same order of idea, you will see I use NOW() as a reference date. But that's easily adjustable. Anyway that's the spirit...
See for a live demo http://sqlfiddle.com/#!2/ab5cf/11
If you had a table that held all of your days you could do a left join from there to your users table.
SELECT SUM(CASE WHEN U.Id IS NOT NULL THEN 1 ELSE 0 END)
FROM DimDate D
LEFT JOIN Users U ON CONVERT(DATE,U.Created_at) = D.DateValue
WHERE YourCriteria
GROUP BY YourGroupBy
The tricky bit is that you group by the date field in your data, which might have 'holes' in it, and thus miss records for that date.
A way to solve it is by filling a table with all dates for the past 10 and next 100 years or so, and to (outer)join that to your data. Then you will have one record for each day (or week or whatever) for sure.
I had to do this only for MS SqlServer, so how to fill a date table (or perhaps you can do it dynamically) is for someone else to answer.
A bit long winded, but I think this will work...
SELECT count(users.created_at) total FROM
(SELECT DATE_SUB(CURDATE(),INTERVAL 6 DAY) as cdate UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 5 DAY) UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 4 DAY) UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 3 DAY) UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 2 DAY) UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 1 DAY) UNION ALL
SELECT CURDATE()) t1 left join users
ON date(created_at)=t1.cdate
WHERE owner_id = ? or owner_id is null
GROUP BY t1.cdate
ORDER BY t1.cdate DESC
It differs from your query slightly in that it works on dates rather than date times which your query is doing. From your description I have assumed you mean to use whole days and therefore have used dates.