How to create a SQL query that calculate monthly grow in population - mysql

I want to create a SQL query that count the number of babies born in month A, then it should count the babies born in month B but the second record should have the sum of month A plus B. For example;
Month | Number
--------|---------
Jan | 5
Feb | 7 <- Here were 2 babies born but it have the 5 of the previous month added
Mar | 13 <- Here were 6 babies born but it have the 7 of the two previous months added
Can somebody maybe please help me with this, is it possible to do something like this?
I have a straight forward table with babyID, BirthDate, etc.
Thank you very much

Consider using a subquery that calculates a running count. Both inner and outer query would be aggregate group by queries:
Using the following sample data:
babyID Birthdate
1 2015-01-01
2 2015-01-15
3 2015-01-20
4 2015-02-01
5 2015-02-03
6 2015-02-21
7 2015-03-11
8 2015-03-21
9 2015-03-27
10 2015-03-30
11 2015-03-31
SQL Query
SELECT MonthName(BirthDate) As BirthMonth, Count(*) As BabyCount,
(SELECT Count(*) FROM BabyTable t2
WHERE Month(t2.BirthDate) <= Month(BabyTable.BirthDate)) As RunningCount
FROM BabyTable
GROUP BY Month(BirthDate)
Output
BirthMonth BabyCount RunningCount
January 3 3
February 3 6
March 5 11

Related

Grouping in Mysql

i need to get the top touristCount in each month like January Zambia has 4 touristCount i need to select only Zambia for January and so on
user
`useri_id` | `username` | `email` | `nationality`
1 Joseph `` US
2 Abraham. `` UK
3 g.wood '' Zambia
4 Messi. '' France
5 Ronaldo. '' Namibia
6 Pogba. '' Holand.
bookings
booking_id | user_id | booking_date | tour_id
1 1 2022-01-01 1
2 1 2022-01-01 6
3 1 2022-05-01 2
4 3 2022-01-01 5
5 2 2022-04-01 5
6 2 2022-11-01 7
7 3 2022-12-01 2
8 6 2022-01-01 1
this is what i have tried
SELECT s.nationality AS Nationality,
COUNT(b.tourist_id) AS touristsCount,
MONTH(STR_TO_DATE(b.booked_date, '%d-%m-%Y')) AS `MonthNumber`
FROM bookings b, users s
WHERE s.user_id = b.tourist_id
AND YEAR(STR_TO_DATE(b.booked_date, '%d-%m-%Y')) = '2022'
GROUP BY Nationality,MonthNumber
order BY MonthNumber ASC
LIMIT 100
i need the results to be like
nationality | TouritIdCount | MonthNumber
US 2 01
UK 1 04
US 1 05
UK 1 11
ZAMBIA 1 12
Try this :
SELECT nationality, COUNT(booking_id) AS TouristIdCount, MONTH(booking_date) AS MonthNumber
FROM users u
JOIN bookings b ON u.user_id = b.user_id
WHERE YEAR(booking_date) = 2022
GROUP BY nationality, MonthNumber
ORDER BY TouristIdCount DESC, MonthNumber ASC
you can use
having COUNT(b.tourist_id) >= 2
You want to count bookings per month and tourist's nationality and then show only the top nationality (or nationalities) per month.
There are two very similar approaches:
Rank the nationalities' booking counts per month with RANK and only show the best ranked rows.
Select the top booking count per month and only show rows matching their top count.
The following query uses the second method. It shows one row per month and top booking nationality. Often there may be excatly one row for a month showing the one top booking nationality, but there may also be months where nationalities tie and share the same top booking count, in which case we see more than one row for a month.
select year, month, nationality, booking_count
from
(
select
year(b.booking_date) as year,
month(b.booking_date) as month,
u.nationality,
count(*) as booking_count,
max(count(*)) over (partition by year(b.booking_date), month(b.booking_date)) as months_max_booking_count
from bookings b
join users u on u.user_id = b.tourist_id
group by year(b.booking_date), month(b.booking_date), u.nationality
) ranked
where booking_count = months_max_booking_count
order by year, month, nationality;
As your own sample data doesn't contain any edge cases, here is some other sample data along with my query's result and an explanation. (In other words, this is what you should have shown in your request ideally.)
users
user_id
username
email
nationality
1
Joseph
joseph#mail.us
US
2
Mary
mary#mail.us
US
3
Abraham
abraham#mail.uk
UK
bookings
booking_id
user_id
booking_date
tour_id
1
1
2022-01-11
1
2
2
2022-01-11
1
3
3
2022-01-11
1
4
3
2022-01-22
2
5
1
2022-05-01
3
6
2
2022-05-01
3
7
1
2022-05-12
4
8
2
2022-05-12
4
9
3
2022-05-14
5
10
3
2022-05-20
6
11
3
2022-05-27
7
result
year
month
nationality
booking_count
2022
1
UK
2
2022
1
US
2
2022
5
US
4
In January there were two tours, but we are not interested in tours. We see four bookings, two by the Americans, two by the Britsh person. This is a tie, and we show two rows, one for UK and one for US with two bookings each.
In May there were five tours, but again, we are not interested in tours. There are seven bookings, four by the Americans, three by the Britsh person. So we only show US as the top country with four bookings here.

Joining multiple SUM() across multiple tables

I have two tables, one with forecast production weights and one with actual production weight.
A customer can and will have multiple types and multiple seasons, and will also invoice those types over the year
Table Estimates
Customer Type Weight Season
John A 10 2018
John A 20 2018
John B 10 2018
Bill A 10 2018
Bill C 10 2017
Robert B 30 2017
Robert C 10 2018
Table Actual
Customer Type Weight InvoiceDate
John A 5 2018-10-30
John A 5 2018-10-30
John A 5 2018-10-30
John C 10 2018-10-30
Bill A 5 2018-11-1
Bill C 10 2017-11-30
Bill C 10 2017-11-30
Bill C 10 2017-11-30
Robert B 30 2017-11-10
Robert C 10 2019-2-20
Desired Query Would be as follows
select customer,
type,
sum(weight),
sum(weight)
from
estimates,
actual
where
season = 2018 and
InvoiceDate between 2018-7-1 and 2019-6-30 and
estimates.type = actual.type and
estimates.customer = actual.customer
group by
customer,
type
This give wildly large numbers
Desired result would be selecting for 2018
Customer Type Sum(Estimate) Sum(Actual)
John A 30 15
John B 10 0
John C 0 10
Bill A 10 5
Robert C 10 10
I have tried several join and union queries attempting to solve this issue
I cant quite get my head around which join to use to get the desired result
You can try below way -
select A.customer,A.type, estimated,actual
from
(
select customer,
type,sum(wieght) as estimated
from estimate where season=2018 group by customer,type
)A inner join
(
select customer,
type,sum(wieght) as actual
from actual where InvoiceDate between '2018-7-1' and '2019-6-30' group by customer,type
)B on A.customer=B.customer and A.type=B.type

SQL joining on two columns with a variation on the first column

I have two tables:
Table 1: planA
ID Date Count
3 2017-01-01 10
2 2017-02-03 15
10 2017-01-30 8
Table 2: planB
ID Date Value
3 2017-01-02 11
2 2017-02-04 12
21 2017-01-30 3
3 2017-02-03 33
What I want to do is to join the two tables on (ID and Date) columns.
However, on Date, I want to use the next day to the date on the table 1.
Therefore, the joined table should look like the following:
PlanA.ID PlanA.Date PlanB.Date PlanA.Count PlanB.Value
3 2017-01-01 2017-01-02 10 11
2 2017-02-03 2017-02-04 15 12
Is this even possible?
Any suggestion would be appreciated!
Yes it is possible:
select
PlanA.ID,
PlanA.Date,
PlanB.Date,
PlanA.Count,
PlanB.Value
from
PlanA inner join PlanB
on (
PlanA.ID = PlanB.ID
and
PlanA.Date + INTERVAL 1 DAY = PlanB.Date
)
if Date is a column of type date, + INTERVAL 1 DAY will return the next day of the one given, and then you can perform the join.

Mysql: find active users who logged in once a week

I have a table users and another table logins everytime the user log-in into the website we record a row in logins ex.
Users
-----
14 | name1
17 | name2
20 | name3
21 | name4
25 | name5
logins
----
14 | 2015-03-01
14 | 2015-03-07
14 | 2015-03-16
14 | 2015-03-24
14 | 2015-03-30
17 | 2015-03-01
17 | 2015-03-07
17 | 2015-03-16
17 | 2015-03-17
17 | 2015-03-30
20 | 2015-03-01
20 | 2015-03-07
20 | 2015-03-08
20 | 2015-03-16
20 | 2015-03-25
20 | 2015-03-30
if start date is 2015-03-01 and end date is 2015-04-01 then 14 & 20 should be selected while 17 wont be selected since he didn't login in the week of 03-22 to 03-28 so the result would be
Result
------
2
First you get the list of users per week which has logged in at least once, then you count per month the amount of users:
SELECT LoginYear,LoginWeek,COUNT(*) as NumbUsers
FROM (
SELECT Year(logins.date) as LoginYear, Week(logins.date) as LoginWeek, logins.UserID
FROM logins
WHERE logins.date>='2015-03-01'
GROUP BY LoginYear, LoginWeek, logins.UserID
HAVING COUNT(*)>0
) t
GROUP BY LoginYear,LoginWeek;
Week numbering: MySQL can count the weeks in different ways (such as starting on a Sunday/Monday) using the mode: WEEK(date,mode). See the WEEK MySQL documentation.
Update: to get the number of persons which has been logged in at least once every week: first we get the users that were logged in at least once per week in the subquery weektable. Then the users are select which have a week count which equals the total number of weeks in that period (thus having been online each week). Finally we count those users.
SELECT COUNT(*)
FROM (
SELECT UserID
FROM (
SELECT Year(logins.date) as LoginYear, Week(logins.date) as LoginWeek, logins.UserID
FROM logins
WHERE logins.date>='2015-03-01'
GROUP BY LoginYear, LoginWeek, logins.UserID
HAVING COUNT(*)>0
) weektable
GROUP BY UserID
HAVING COUNT(*)>=TIMESTAMPDIFF(WEEK,'2015-03-01',NOW())
) subq;
Note 1: I put the date '2015-03-01' as an example but you can change this or put as a variable.
Note 2: depending on the dates you choose it can be that the week count by TIMESTAMPDIFF is less than the maximum number of weeks (counted by COUNT(*)), since it does not count half weeks. Therefore I put >= in the last line: HAVING COUNT(*)>=TIMESTAMPDIFF(WEEK,'2015-03-01',NOW()).
I cannot test it here at the moment but something like
SELECT COUNT(Users.id) WHERE logins.date>=XXXX AND logins.date<=XXXX GROUP BY Users.id
should work

Select rows from last existing 12 months

There's a DATETIME column called time. How could I select all rows that fall within the last existing 12 months (NOT within the last year from today)? Not every month might have a row, and months may have more than one row.
For example, out of this table (ORDER BY time DESC), rows with ids 2 to 17 would be selected.
id time
-- ----
17 2015-04-01
16 2015-04-01
15 2015-03-01
14 2015-02-01
13 2015-01-01
12 2014-12-01
11 2014-11-01
10 2014-10-01
9 2013-12-01
8 2013-11-01
7 2013-10-01
6 2013-09-01
5 2013-09-01
4 2013-09-01
3 2013-09-01
2 2013-08-01
1 2013-07-01
Another way to put this:
Take the table above and group by month/year, so we get:
2015-04
2015-03
2015-02
2015-01
2014-12
2014-11
2014-10
2013-12
2013-11
2013-10
2013-09
2013-08
2013-07
Now take the 12 most recent months from this list, which is everything except 2013-07.
2015-04
2015-03
2015-02
2015-01
2014-12
2014-11
2014-10
2013-12
2013-11
2013-10
2013-09
2013-08
And select everything from those months.
I guess I could do this with multiple queries or subqueries but is there another way to do this?
If your time field is only month-precision, you could do it with a pretty simple subselect:
SELECT * FROM Table t1
WHERE time IN (
SELECT DISTINCT time FROM Table t2 ORDER BY time DESC LIMIT 12
)
If your timestamps are full-precision, you could do the same thing, but you'd need to do some date manipulation to round the dates to the month for comparison.