SQL active users by month - sql

I would like to know the number of active users by month, I use SQL Server 2017.
I have an AuditLog table like:
- UserID: int
- DateTime: datetime
- AuditType: int
UserID DateTime AuditType
------------------------------
1 2022-01-01 1
1 2022-01-15 4
1 2022-02-20 3
2 2022-01-10 8
2 2022-03-10 1
3 2022-03-20 1
If someone has at least one entry in a given month then he/she is treated as active.
I would like to have a result like:
Date Count
2022-01 2
2022-02 1
2022-03 2

I think you can combine the function Month(datetime) in the GROUP BY with the Count function SELECT COUNT(UserID)

SELECT (CAST(YEAR(C.DATE)AS CHAR(4))+'-'+CAST(MONTH(C.DATE)AS CHAR(2)))YEAR_MONTH,COUNT(C.USER_ID)CNTT
FROM AUDITLOG AS C
GROUP BY (CAST(YEAR(C.DATE)AS CHAR(4))+'-'+CAST(MONTH(C.DATE)AS CHAR(2)))
ORDER BY (CAST(YEAR(C.DATE)AS CHAR(4))+'-'+CAST(MONTH(C.DATE)AS CHAR(2)));

Here is solutions,
Select [Date],count(1) as Count From (
select Cast(cast(d.DateTime as date) as varchar(7)) as [Date],UserId
from AuditLog d
Group by Cast(cast(d.DateTime as date) as varchar(7)),UserId
) as q1 Group by [Date]
Order by 1
Hope, it will works.

GROUP DATE (Year and Month) either combine or separate and count distinct userId
SELECT CONVERT(VARCHAR(7), [DateTime], 126)[Date], COUNT(DISTINCT UserID)[Count]
FROM AuditLog
GROUP BY CONVERT(VARCHAR(7), [DateTime], 126)

Related

SQL - Query to return active subscriptions on a given day

I have a table that shows when a user signs up for a subscription and when their membership will expire. A user can purchase a new subscription even if their current one is in force.
userid|purchasedate|expirydate
1 |2019-01-01 |2019-02-01
2 |2019-01-02 |2019-02-02
3 |2019-01-03 |2019-02-03
3 |2019-01-04 |2019-03-03
I need a SQL query that will GROUP BY the date and return the number of active subscriptions on that date. So it would return:
date |count
2019-01-01|1
2019-01-02|2
2019-01-03|3
2019-01-04|3
Below is for BigQuery Standard SQL
#standardSQL
SELECT day, COUNT(DISTINCT userid) active_subscriptions
FROM (SELECT AS STRUCT MIN(purchasedate) min_date, MAX(expirydate) max_date FROM `project.dataset.table`),
UNNEST(GENERATE_DATE_ARRAY(min_date, max_date)) day
JOIN `project.dataset.table`
ON day BETWEEN purchasedate AND expirydate
GROUP BY day
You can test, play with above using dummy data from your question as in below example
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 userid, DATE '2019-01-01' purchasedate, DATE '2019-02-01' expirydate UNION ALL
SELECT 2, '2019-01-02', '2019-02-02' UNION ALL
SELECT 3, '2019-01-03', '2019-02-03' UNION ALL
SELECT 3, '2019-01-04', '2019-03-03'
)
SELECT day, COUNT(DISTINCT userid) active_subscriptions
FROM (SELECT AS STRUCT MIN(purchasedate) min_date, MAX(expirydate) max_date FROM `project.dataset.table`),
UNNEST(GENERATE_DATE_ARRAY(min_date, max_date)) day
JOIN `project.dataset.table`
ON day BETWEEN purchasedate AND expirydate
GROUP BY day
with below output
Row day active_subscriptions
1 2019-01-01 1
2 2019-01-02 2
3 2019-01-03 3
4 2019-01-04 3
5 2019-01-05 3
6 2019-01-06 3
... ... ...
... ... ...
31 2019-01-31 3
32 2019-02-01 3
33 2019-02-02 2
34 2019-02-03 1
35 2019-02-04 1
... ... ...
... ... ...
61 2019-03-02 1
62 2019-03-03 1
You need a list of dates and count(distinct):
select d.dte, count(distinct t.userid) as num_users
from (select distinct purchase_date as dte from t) d left join
t
on d.dte >= t.dte and
d.dte <= t.expiry_date
group by d.dte
order by d.dte;
EDIT:
BigQuery can be fickle about inequalities in the on clause. Here is another approach:
select dte, count(distinct t.userid) as num_users
from t cross join
unnest(generate_date_array(t.purchase_date, t.expiry_date, interval 1 day)) dte
group by dte
order by dte;
You can use a where clause to filter down to particular dates.
I make the table name 'test_expirydate' and use your data
and this one work
select
tb1.expirydate,
count(*) as total
from test_expirydate as tb1
left join (
select
expirydate
from test_expirydate as tb2
group by userid
) as tb2
on tb1.expirydate >= tb2.expirydate
group by tb1.expirydate
I don't sure is it work in other case or not but it fine with current data
Oh, I interpret that the left column should be the expiration date.

how do i divide and add column

i have a list with peoples id and date, the list say when a person Entered to website (his id and date).
how can i show for all the dates how many people enter the site two days in a row?
the data ( 30,000 like this in diffrent dates)
01/03/2019 4616
01/03/2019 17584
01/03/2019 7812
01/03/2019 34
01/03/2019 12177
01/03/2019 7129
01/03/2019 11660
01/03/2019 2428
01/03/2019 17514
01/03/2019 10781
01/03/2019 7629
01/03/2019 11119
I succeeded to show the amount of pepole enter the site on the same day but i didnt succeeded to add a column that show the pepole that enter 2 days in row.
date number_of_entrance
2019-03-01 7099
2019-03-02 7021
2019-03-03 7195
2019-03-04 7151
2019-03-05 7260
2019-03-06 7169
2019-03-07 7076
2019-03-08 7081
2019-03-09 6987
2019-03-10 7172
select date,count(*) as number_of_entrance
fROM [finalaa].[dbo].[Daily_Activity]
group by Date
order by date;
how can i show for all the dates how many people enter the site two days in a row?
I would just use lag():
select count(distinct person)
from (select t.*,
lag(date) over (partition by person order by date) as prev_date
from t
) t
where prev_date = dateadd(day, -1, date);
Your code suggests SQL Server, so I used the date functions in that database.
If you want this per date:
select date, count(distinct person)
from (select t.*,
lag(date) over (partition by person order by date) as prev_date
from t
) t
where prev_date = dateadd(day, -1, date)
group by date;
You can use a subquery which returns the number of common entrances in 2 days:
select
t.date,
count(*) as number_of_entrance,
(
SELECT COUNT(g.id) FROM (
SELECT id
FROM [Daily_Activity]
WHERE date IN (t.date, t.date - 1)
GROUP BY id
HAVING COUNT(DISTINCT date) = 2
) g
) number_of_entrance_2_days_in_a_row
FROM [Daily_Activity] t
group by t.date
order by t.date;
Replace id with the 2nd column's name in the table.

Count distinct records but only 1 time every XX Days

EDIT: Start date as of Jan 1 XXXX
I need to create a count of distinct userID's based on a 7 day grouping. Basically if a User calls on day 1 and day 2 of the month, they are counted 1 time. However if they call on Day 1 and day 10, then they are counted 2 times.
Table layout:
userId CallId datetime
0 123 01/01/2016 xx:xx:xx
0 124 01/10/2016 xx:xx:xx
1 125 01/10/2016 xx:xx:xx
1 126 01/10/2016 xx:xx:xx
2 127 01/10/2016 xx:xx:xx
1 128 01/30/2016 xx:xx:xx
2 129 01/31/2016 xx:xx:xx
What I need the return to look like:
Count(UserID) Week#
1 1
3 2
2 4
Thank you for your time.
Based on Gurwinders response I have produced the following and included years so that it is still usuable in a years time.
SELECT COUNT(UserID), CallYear, CallWeek
FROM (
SELECT DISTINCT UserID,
datepart(year,datetime) as CallYear,
datepart(week,datetime) as CallWeek
FROM my_table
)
Group By CallYear,CallWeek
This will produce a rolling distinct count begining Jan 1
Declare #YourTable table (userId int,CallId int,datetime datetime)
Insert Into #YourTable values
(0,123,'2016-01-01'),
(0,124,'2016-01-10'),
(1,125,'2016-01-10'),
(1,126,'2016-01-10'),
(2,127,'2016-01-10'),
(1,128,'2016-01-30'),
(2,129,'2016-01-31')
Select D1
,D1 =DateAdd(DD,6,D1)
,Cnt=count(Distinct UserID)
From #YourTable A
Join (Select Top 500 D1=DateAdd(DD,(Row_Number() Over (Order By Number)-1)*7,'2016-01-01') From master..spt_values ) B
on datetime between D1 and DateAdd(DD,6,D1)
Group By D1
Returns
D1 D1 Cnt
2016-01-01 2016-01-07 1
2016-01-08 2016-01-14 3
2016-01-29 2016-02-04 2
you can use this:
select count(distinct userid), datepart(week, datetime) week, datepart(year, datetime) year
from my_table
group by datepart(week, datetime), datepart(year, datetime);
What is your starting date? Have you looked at the DateDiff() function?
Try this:
With ABC
As
(select datepart(week, datetime) as week#
from table)
Select count(week#) as Times,week#
From ABC

How to duplicate data in sql with conditions

I havea table as table_A . table_A includes these columns
-CountryName
-Min_Date
-Max_Date
-Number
I want to duplicate data with seperating by months. For example
Argentina | 2015-01-04 | 2015-04-07 | 100
England | 2015-02-08 | 2015-03-11 | 90
I want to see a table as this (Monthly seperated)
Argentina | 01-2015 | 27 //(days to end of the min_date's month)
Argentina | 02-2015 | 29 //(days full month)
Argentina | 03-2015 | 31 //(days full month)
Argentina | 04-2015 | 7 //(days from start of the max_date's month)
England | 02-2015 | 21 //(days)
England | 03-2015 | 11 //(days)
I tried too much thing to made this for each records. But now my brain is so confusing and my project is delaying.
Does anybody know how can i solve this. I tried to duplicate each rows with datediff count but it is not working
WITH cte AS (
SELECT CountryName, ISNULL(DATEDIFF(M,Min_Date ,Max_Date )+1,1) as count FROM table_A
UNION ALL
SELECT CountryName, count-1 FROM cte WHERE count>1
)
SELECT CountryName,count FROM cte
-Generate all the dates between min and max dates for each country.
-Then get the month start and month end dates for each country,year,month.
-Finally get the date differences of the month start and month end.
WITH cte AS (
SELECT Country, min_date dt,min_date,max_date FROM t
UNION ALL
SELECT Country, dateadd(dd,1,dt),min_date,max_date FROM cte WHERE dt < max_date
)
,monthends as (
SELECT country,year(dt) yr,month(dt) mth,max(dt) monthend,min(dt) monthstart
FROM cte
GROUP BY country,year(dt),month(dt))
select country
,cast(mth as varchar(2))+'-'+cast(yr as varchar(4)) yr_month
,datediff(dd,monthstart,monthend)+1 days_diff
from monthends
Sample Demo
EDIT: Another option would be to generate all the dates once (the example shown here generates 51 years of dates from 2000 to 2050) and then joining it to the table to get the days by month.
WITH cte AS (
SELECT cast('2000-01-01' as date) dt,cast('2050-12-31' as date) maxdt
UNION ALL
SELECT dateadd(dd,1,dt),maxdt FROM cte WHERE dt < maxdt
)
SELECT country,year(dt) yr,month(dt) mth, datediff(dd,min(dt),max(dt))+1 days_diff
FROM cte c
JOIN t on c.dt BETWEEN t.min_date and t.max_date
GROUP BY country,year(dt),month(dt)
OPTION (MAXRECURSION 0)
I think you have the right idea. But you need to construct the months:
WITH cte AS (
SELECT CountryName, Min_Date as dte, Min_Date, Max_Date
FROM table_A
UNION ALL
SELECT CountryName, DATEADD(month, 1, dte), Min_Date, Max_Date
FROM cte
WHERE dte < Max_date
)
SELECT CountryName, dte
FROM cte;
Getting the number of days in the month is a bit more complicated. That requires some thought.
Oh, I forgot about EOMONTH():
select countryName, dte,
(case when dte = min_date
then datediff(day, min_date, eomonth(dte)) + 1
when dte = max_date
then day(dte)
else day(eomonth(dte))
end) as days
from cte;
Using a Calendar Table makes this stuff pretty easy. RexTester: http://rextester.com/EBTIMG23993
begin
create table #enderaric (
CountryName varchar(16)
, Min_Date date
, Max_Date date
, Number int
)
insert into #enderaric values
('Argentina' ,'2015-01-04' ,'2015-04-07' ,'100')
, ('England' ,'2015-02-08' ,'2015-03-11' ,'90')
end;
-- select * from #enderaric
--*/"
declare #FromDate date;
declare #ThruDate date;
set #FromDate = '2015-01-01';
set #ThruDate = '2015-12-31';
with x as (
select top (cast(sqrt(datediff(day, #FromDate, #ThruDate)) as int) + 1)
[number]
from [master]..spt_values v
)
/* Date Range CTE */
,cal as (
select top (1+datediff(day, #FromDate, #ThruDate))
DateValue = convert(date,dateadd(day,
row_number() over (order by x.number)-1,#FromDate)
)
from x cross join x as y
order by DateValue
)
select
e.CountryName
, YearMonth = convert(char(7),left(convert(varchar(10),DateValue),7))
, [Days]=count(c.DateValue)
from #enderaric as e
inner join cal c on c.DateValue >= e.min_date
and c.DateValue <= e.max_date
group by
e.CountryName
, e.Min_Date
, e.Max_Date
, e.Number
, convert(char(7),left(convert(varchar(10),DateValue),7))
results in:
CountryName YearMonth Days
---------------- --------- -----------
Argentina 2015-01 28
Argentina 2015-02 28
Argentina 2015-03 31
Argentina 2015-04 7
England 2015-02 21
England 2015-03 11
More about calendar tables:
Aaron Bertrand - Generate a set or sequence without loops
generate-a-set-1
generate-a-set-2
generate-a-set-3
David Stein - Creating a Date Table/Dimension on SQL 2008
Michael Valentine Jones - F_TABLE_DATE

SQL Count by Active Date

If I have a table of records and active/inacitve dates, is there a simple way to count active records by month? For example:
tbl_a
id dt_active dt_inactive
a 2013-01-01 2013-08-24
b 2013-01-01 2013-07-05
c 2012-02-01 2012-01-01
If I have to generate an output of active records by month like this:
active: dt_active < first_day_of_month <= dt_inactive
month count
2013-01 2
2013-02 2
2013-03 2
2013-04 2
2013-05 2
2013-06 2
2013-07 2
2013-08 1
2013-09 0
Is there any clever way to do this besides uploading a temp table of dates and using subqueries?
Here is one method that gives the count of actives on the beginning of the month. It creates a list of all the months and then joins this information to tbl_a.
with dates as (
select cast('2013-01-01' as date) as month
union all
select dateadd(month, 1, dates.month)
from dates
where month < cast('2013-09-01' as date)
)
select convert(varchar(7), month, 121), count(a.id)
from dates m left outer join
tbl_a a
on m.month between a.dt_active and a.dt_inactive
group by convert(varchar(7), month, 121)
order by 1;
Note: if dt_inactive is the first date of inactivity, then the on clause should be:
on m.month >= a.dt_active and m.month < a.dt_inactive
Here is a SQL Fiddle with the working query.