Create dynamic SQL statement for one year - sql

I would like to compare the number of newly made subscriptions with the number of ending subscriptions per month in 2018 and combine that into one table.
With x-axis being the months of 2018 (so January, February, and so on) and y-axis as first row my first sql query = the number of ran out subscriptions in that month. The second row would be my second sql query = the number of newly made subscriptions in that month. My "queries" down there are as an example for March 2018.
SELECT
COUNT (UserId)
FROM
UserInAppPurchase
WHERE
ValidTo > '2018-03-01' AND ValidTo < '2018-03-31'
GROUP BY
UserId
SELECT COUNT(UserId)
FROM UserInAppPurchase
WHERE PurchaseDate > '2018-03-01'
AND PurchaseDate < '2018-03-31'
GROUP BY UserId
Thanks very much for the help

If I understood it right, the above query will return multiple rows for a single month which will depend to the total number of unique users placing order. Then for each month and each user being two verticals, the said graph will be a 3D-graph which I don't think the case here is.
So Instead, I could think of 2 cases,
You want total number of subscriptions expired per month of each month
SELECT MONTH(ValidTo) ExpireMonth, COUNT(UserId) ExpireCount
FROM UserInAppPurchase
WHERE YEAR(ValidTo) = 2018
GROUP BY MONTH(ValidTo);
You want to know the number of users whose at least one subscription have expired
WITH UniqueUsers AS
(
SELECT DISTINCT MONTH(ValidTo) ExpireMonth, UserId
FROM UserInAppPurchase
WHERE YEAR(ValidTo) = 2018
)
SELECT ExpireMonth, COUNT(UserId) UserCount
FROM UniqueUsers
GROUP BY ExpireMonth;
The similar query will be for purchased subscriptions as well.
Please let me know if what you require is different.

if you want the count of user id you should not group by for user id
SELECT COUNT (UserId), 'valid_to'
FROM UserInAppPurchase WHERE ValidTo>'2018-03-01' and ValidTo<'2018-03-31'
union all
SELECT COUNT (UserId), 'PurchaseDate'
FROM UserInAppPurchase WHERE PurchaseDate>'2018-03-01' and PurchaseDate<'2018-03-31'

Related

Filter Context and Group by in SQL

Consider the table with 2 columns which has a new row every time a user sign in. (Approximately 10M rows and can have duplicates as users can sign in multiple times per month)
Sign in Month
MemberID
2020-10
1000000
2020-12
1000001
Now to find out the unique user each month I can use the following query.
Select Sign_in_Month, count(distinct(memberid)) as unique_users from table group by Sign_in_Month
But I want to break the distinct count in the first query further into 2 cohorts where the user either sign in the last 3 month before or didn't. (For October 2020 it would be July-Sep 2020 and for Sep 2020 it would be June-August 2020)
I used to do this in powerBI through DAX which was easy with filter context but I am not sure how to implement this in SQL.
Desire Result
Sign in Month
Total Unique Users
Unique Users who signed in in the last 3 months
Unique Users who did not sign in in the last 3 months
2020-10
4000
3000
1000
2020-11
5000
2500
2500
2020-12
3500
1500
2000
The last 2 column should add up to the second column.
How do I create some sort of indicator as to where the member has shopped in the last 3 month in reference to that particular month?
Thanks
I would approach this as follows:
Get a unique value per month.
Use lag() to see the previous month when someone logged in.
Compare the time difference.
For convenience, I would convert the sign_in_month to a date:
select sign_in_date,
count(*) as num_members,
sum(case when prev_sign_in_date >= dateadd(month, -4, sign_in_date)
then 1 else 0
end) as num_members_who_signed_in
from (select t.memberId, v.sign_in_date,
lag(v.sign_in_date) over (partition by t.memberId order by v.sign_in_date) as prev_sign_in_date
from t cross apply
(values (convert(date, t.sign_in_month, '-01'))
) v(sign_in_date)
group by t.memberId, v.sign_in_date
) t
group by sign_in_date;
Note that count(distinct) is no longer needed because the subquery takes care of that.
You can try this below script-
Note: syntax is for MSSQL, but the logic is global
select
sign_in_month,
count(distinct MemberId) as total_unique_user,
count(
CASE
WHEN CAST(sign_in_month+'-01' as Date) >= DATEADD(mm,-4,getdate()) then MemberId
else null
end
) as last_3_month,
count(
CASE
WHEN CAST(sign_in_month+'-01' as Date) < DATEADD(mm,-4,getdate()) then MemberId
else null
end
) as before_3_month
from your_table_name
GROUP BY sign_in_month

How to get number of billable customers per month SQL

This is what my table looks like:
NOTE: Don't worry about the BMI field being empty in some rows. We assume that each row is a reading. I have omitted some columns for privacy reasons.
I want to get a count of the number of active customers per month. A customer is active if they have at least 18 readings in total (1 reading per day for 18 days in a given month). How do I write this SQL query? Assume the table name is 'cust'. I'm using SQL Server. Any help is appreciated.
Presumably a patient is a customer in your world. If so, you can use two levels of aggregation:
select yyyy, mm, count(*)
from (select year(createdat) as yyyy, month(createdat) as mm,
patient_id,
count(distinct convert(date, createdat)) as num_days
from t
group by year(createdat), month(createdat), patient_id
) ymp
where num_days >= 18
group by yyyy, mm;
You need to group by patient and the month, then group again by just the month
SELECT
mth,
COUNT(*) NumPatients
FROM (
SELECT
EOMONTH(c.createdat) mth
FROM cust c
GROUP BY EOMONTH(c.createdat), c.patient_id
HAVING COUNT(*) >= 18
-- for distinct days you could change it to:
-- HAVING COUNT(DISTINCT CAST(c.createdat AS date)) >= 18
) c
GROUP BY mth;

Group Data by Year, Oracle SQL

I am trying to create a query that counts records that existed within a year. The table looks like this:
Title_ID ISSUE_DATE EXPIRY_DATE CLIENT_NUMBER
123 '26-JUN-19' '17-AUG-20' 8529
124 '04-APR-19' '17-SEP-22' 8529
125 '09-MAY-15' '11-SEP-19' 3654
126 '31-DEC-19' '25-NOV-22' 9852
127 '27-OCT-18' '26-FEB-21' 2254
128 '05-OCT-11' '01-JAN-19' 9852
Specifically, I want to count the number of distinct CLIENT_NUMBERS of the records that existed in a given calendar year.
The record (title) exists from the ISSUE_DATE until the EXPIRY_DATE. If the record existed at any point within a year (Let's say 2019), then we are interested in including it in our client count.
So, if the record was issued in 2019 or if the record expired in 2019 or if the record was issued before 2019 and expired after 2019, then we are interested in including it in the client count for the year it existed.
I have built the following query that does this, but only for one specific year (2019). I'd like to build the query further so it look at each calendar year and counts the distinct client numbers when the client has an active title:
SELECT *
-- count(distinct client_number)
FROM
TITLE
WHERE
issue_date between '01-Jan-19' and '31-Dec-19'
or expiry_date between '01-Jan-19' and '31-Dec-19'
or (issue_date < '01-Jan-19' and expiry_date > '31-Dec-19')
Where I am having trouble is, my data is much larger than the subset I have provided. I would like to recursively get counts of distinct client numbers by year using the same kind of logic to include a record within a calendar year as I have outlined above. So, I'd like to have a table like this:
YEAR COUNT_OF_CLIENT_NUMBERS
2020 5469
2019 5587
2018 4852
2017 4501
2016 3265
etc
I think I've stretched by current SQL abilities at this point, so I thought Id ask to see if there are any suggestions to make this happen?
Thanks.
EDIT: to clarify, the issue date and the expiry date apply to the title, not the client. So, the title is issued on the issue date and expires on the expiry date. A client can own one or more title(s).
So, I am looking to get a count of how many distinct clients own active titles within a give year if one or more of their titles is active within that year. So the key is, a title is considered active if it was issued in that year OR it expired within that year OR it was issued before that year and expired after that year. A title CAN be active in multiple years (i.e. Issued on Feb. 4, 2014 and expires on Apr.7 2017, I want to include the client count for each year that titles exists....2014, 2015, 2016 and 2017).
So, I created a table to join to (thanks #GMB for the suggestion):
with calendar_year (y) as
(
select 2010 from dual
union all select y + 1 from calendar_year where y < 2020
)
select * from calendar_year
Which returns:
2010
2011
2012
2013
2014
etc
I want to join that to my titles table, but I am having issues recursively looking at the issue date and expiry date to join up the title to each year it existed in. Any help in that area, would be great!
You can use a recursive query to generate the years, then bring the table with a left join, and aggregate:
with dates (dt) as (
select date '2016-01-01' from dual
union all select add_months(dt, 1) from dates where dt < date '2020-01-01'
)
select d.dt, count(distinct t.client_number) count_of_client_numbers
from dates d
left join title t
on t.issue_date <= d.dt
and t.expiry_date > d.dt
group by d.dt
The upside of this approach is that you get results for each and every year, even those where no title started or ended.
You can get number of clients on any day by unpivoting the data, so there is one row per date. Then keep track of the "ins" and "outs".
You don't specify the database, but here is one approach:
select dte, sum(inc),
sum(sum(inc)) over (order by dte) as active_on_date
from ((select issue_date as dte, 1 as inc
from t
) union all
(select expiry_date as dte, -1 as inc
from t
)
) t
group by dte
order by dte;
EDIT:
Hmmm, the above may not do exactly what you want. If you want to count distinct client numbers rather than overall rows, then it might be simpler to just list the dates and join:
select d.dte, count(distinct t.client_id)
from (select date '2020-01-01' as dte from dual union all
select date '2019-01-01' as dte from dual union all
select date '2018-01-01' as dte from dual union all
. . .
) d left join
t
on d.dte between t.issue_dte and t.expiry_dte
group by d.dte
order by d.dte;

SQL window-over by-incremental distinct users

I am sure this must be fairly easy for you but unfortunately it is not for me !
I am trying to write a query that counts incremental distinct user id grouped by month.
Understand if user X has a row in both january and february he should be counted as 1 in January but not in February.
I can do the following below for a given month but I would like to automate it
EDIT :
Let me try to clarify: a row in table UX is created every time a user performs a given action. I would like to count the number of unique NEW(/incremental) users every month who performed this action. Meaning if user A performed this action in January AND February he would only be counted in January.
select
count(distinct ux.account_id)
, trunc(ux.date_key,'MM') as month
from
ux
left join
(
select
distinct ux.account_id as account_id
from
ux
where
and ux.date_key < '2019-02-01'
) bf on ux.account_id=bf.account_id
where
and ux.date_key >= '2019-02-01'
and bf.account_id IS NULL
group by
trunc(ux.date_key,'MM')
"incremental distinct user" to me sounds a lot like a user starting. Does this do what you want?
select trunc(min_date_key, 'MM') as month
from (select account_id, min(ux.date_key) as min_date_key
from ux
group by account_id
) ux
where min_date_key < '2019-02-01' and ux.account_id IS NULL
group by trunc(ux.date_key, 'MM')

Using SQL to compute average daily unique usage

I have a MySQL table "stats", which is a list of entries for each login into a website. Each entry has a "userId" string, a "loginTime" timestamp and other fields. There can be more than one entry for each user - one for each login that he makes. I want to write a query that will calculate the average of unique daily logins over, say, 30 days.
Any ideas?
/*
This should give you one row for each date and unique visits on that date
*/
SELECT DATE(loginTime) LoginDate, COUNT(userID) UserCount
FROM stats
WHERE DATE(loginTime) BETWEEN [start date] AND [end date]
GROUP BY DATE(logintime), userID
Note: It will be more helpful if you can provide some sample data with the result you are looking for.
i'm probably wrong but if you did: select count(distinct userid) from stats where logintime between start of :day and end of day for day in each of those 30 days fetched those 30 counts (which could be pre-calculated cached (as you probably don't have users logging in at past times)) and them just average them in the programing language that your executing the query from
i read http://unganisha.org/home/pages/Generating_Sequences_With_SQL/index.html while looking and thought if you had a table of say the numbers 0 to 30 lets name it offsets for this example:
select avg(userstoday)
from (select count(userid) as userstoday, day
from stats join offsets on (stats.logintime=(current_day)-offsets.day)
group by day)
and as i noted, the userstoday value could be pre-calculated and stored in a table
Thanks everyone, eventually I used:
SELECT SUM( uniqueUsers ) / 30 AS DAU
FROM (
SELECT DATE( loginTime ) AS DATE, COUNT( DISTINCT userID ) AS uniqueUsers
FROM user_requests
WHERE DATE( loginTime ) > DATE_SUB( CURDATE( ) , INTERVAL 30
DAY )
GROUP BY DATE( loginTime )
) AS daily_users
I use a SUM and divide by 30 instead of average, because on some days I may not have any logins and I want to account for that. But on any daily heavy-traffic website simply using AVG will give the same results