Rolling sum previous 12 months grouped by 2 dimensions (SQL- Snowflake) - sql

I have the following table structure available in the C:
I am struggling in Snowflake with a query that should show me the the sum of previous 12 months for every distinct month in the table split into three dimensions .
The way reporting date 01.08.2022 for region='US' and type=1 is calculated: it is the sum of the past 12 months' row of "revenue_12_months" = 4000+ 45433+45777+ 8866+ 4000+ 6678+ 2456+ 6677+ 6677+ 7744+ 6775 + 7755
WITH
indata(dt,region,type,revenue) AS (
SELECT DATE '2021-04-01','US','Type 1',4000 UNION ALL SELECT DATE '2021-05-01','Europe','Type 2',5777
UNION ALL SELECT DATE '2021-05-01','US','Type 1',45433 UNION ALL SELECT DATE '2021-07-01','Europe','Type 2',8955
UNION ALL SELECT DATE '2021-06-01','US','Type 1',45777 UNION ALL SELECT DATE '2021-09-01','Asia','Type 1',7533
UNION ALL SELECT DATE '2021-07-01','US','Type 1',8866 UNION ALL SELECT DATE '2021-11-01','Asia','Type 2',5534
UNION ALL SELECT DATE '2021-08-01','US','Type 1',4000 UNION ALL SELECT DATE '2022-01-01','Asia','Type 1',7244
UNION ALL SELECT DATE '2021-09-01','US','Type 1',6678 UNION ALL SELECT DATE '2022-03-01','Asia','Type 1',5654
UNION ALL SELECT DATE '2021-10-01','US','Type 1',2456 UNION ALL SELECT DATE '2022-05-01','Asia','Type 1',4525
UNION ALL SELECT DATE '2021-11-01','US','Type 1',6677 UNION ALL SELECT DATE '2022-07-01','Asia','Type 1',6654
UNION ALL SELECT DATE '2021-12-01','US','Type 1',6677 UNION ALL SELECT DATE '2022-09-01','Asia','Type 2',5754
UNION ALL SELECT DATE '2022-01-01','US','Type 1',7744 UNION ALL SELECT DATE '2022-11-01','Asia','Type 2',5644
UNION ALL SELECT DATE '2022-02-01','US','Type 1',6775 UNION ALL SELECT DATE '2023-01-01','Asia','Type 2',6777
UNION ALL SELECT DATE '2022-03-01','US','Type 1',7755
)
select dt,region,type, SUM(revenue) OVER (ORDER BY dt,region,type ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) revenue_12_months
from indata

You want it per region and type, so that needs to part of the partition by clause
sum(revenue) over (partition by region, type order by dt rows between 11 preceding and current row)

could it be that you simply forgot the GROUP BY part in your window function?
WITH
indata(dt,region,type,revenue) AS (
SELECT DATE '2021-04-01','US','Type 1',4000 UNION ALL SELECT DATE '2021-05-01','Europe','Type 2',5777
UNION ALL SELECT DATE '2021-05-01','US','Type 1',45433 UNION ALL SELECT DATE '2021-07-01','Europe','Type 2',8955
UNION ALL SELECT DATE '2021-06-01','US','Type 1',45777 UNION ALL SELECT DATE '2021-09-01','Asia','Type 1',7533
UNION ALL SELECT DATE '2021-07-01','US','Type 1',8866 UNION ALL SELECT DATE '2021-11-01','Asia','Type 2',5534
UNION ALL SELECT DATE '2021-08-01','US','Type 1',4000 UNION ALL SELECT DATE '2022-01-01','Asia','Type 1',7244
UNION ALL SELECT DATE '2021-09-01','US','Type 1',6678 UNION ALL SELECT DATE '2022-03-01','Asia','Type 1',5654
UNION ALL SELECT DATE '2021-10-01','US','Type 1',2456 UNION ALL SELECT DATE '2022-05-01','Asia','Type 1',4525
UNION ALL SELECT DATE '2021-11-01','US','Type 1',6677 UNION ALL SELECT DATE '2022-07-01','Asia','Type 1',6654
UNION ALL SELECT DATE '2021-12-01','US','Type 1',6677 UNION ALL SELECT DATE '2022-09-01','Asia','Type 2',5754
UNION ALL SELECT DATE '2022-01-01','US','Type 1',7744 UNION ALL SELECT DATE '2022-11-01','Asia','Type 2',5644
UNION ALL SELECT DATE '2022-02-01','US','Type 1',6775 UNION ALL SELECT DATE '2023-01-01','Asia','Type 2',6777
UNION ALL SELECT DATE '2022-03-01','US','Type 1',7755
)
select dt,region,type, SUM(revenue) OVER (PARTITION BY region, type ORDER BY dt,region,type ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) revenue_12_months
from indata
ORDER BY REGION, TYPE, DT;
Best regards,
TK

Related

How can I use the LAG FUNCTION to show revenue this year vs last year in Snowflake with?

I would like to show the current revenue vs last's year's revenue in the same row per region per type. Example:For 2022-04-01, US, Type 1 --> REVENUE: 2456, REVENUE_LAST_YEAR: 4000
2021-04-01, US, Type 1 --> REVENUE: 4000, REVENUE_LAST_YEAR: 0
For some reason, the Lag formula in Snowflake is showing wrong values. Could someone please help ?
WITH
indata(dt,region,type,revenue) AS (
SELECT DATE '2021-04-01','US','Type 1',4000 UNION ALL SELECT DATE '2021-05-01','Europe','Type 2',5777
UNION ALL SELECT DATE '2021-06-01','US','Type 1',45433 UNION ALL SELECT DATE '2021-07-01','Europe','Type 2',8955
UNION ALL SELECT DATE '2021-08-01','US','Type 1',45777 UNION ALL SELECT DATE '2021-09-01','Asia','Type 1',7533
UNION ALL SELECT DATE '2021-10-01','US','Type 1',8866 UNION ALL SELECT DATE '2021-11-01','Asia','Type 2',5534
UNION ALL SELECT DATE '2021-12-01','US','Type 2',4000 UNION ALL SELECT DATE '2022-01-01','Asia','Type 1',7244
UNION ALL SELECT DATE '2022-02-01','US','Type 1',6678 UNION ALL SELECT DATE '2022-03-01','Asia','Type 1',5654
UNION ALL SELECT DATE '2022-04-01','US','Type 1',2456 UNION ALL SELECT DATE '2022-05-01','Asia','Type 1',4525
UNION ALL SELECT DATE '2022-06-01','US','Type 1',6677 UNION ALL SELECT DATE '2022-07-01','Asia','Type 1',6654
UNION ALL SELECT DATE '2022-08-01','US','Type 1',6677 UNION ALL SELECT DATE '2022-09-01','Asia','Type 2',5754
UNION ALL SELECT DATE '2022-10-01','US','Type 1',7744 UNION ALL SELECT DATE '2022-11-01','Asia','Type 2',5644
UNION ALL SELECT DATE '2022-12-01','Europe','Type 2',6775 UNION ALL SELECT DATE '2023-01-01','Asia','Type 2',6777
UNION ALL SELECT DATE '2023-02-01','Europe','Type 2',7755
)
SELECT indata.*,
lag(REVENUE, 1, 0) over (partition by region,type,revenue order by year(dt)) REVENUE_last_year
FROM indata
order by year(dt)
Partitioning by region, type and month-day:
SELECT indata.*,
LAG(REVENUE, 1, 0) over (partition by region,type, TO_VARCHAR(dt, 'mmdd')
order by dt) AS REVENUE_last_year
FROM indata
ORDER BY dt;
Output:

Count only when the next row is different from the previous row

I have a table with 12 registers. I want to count the row just if the column "AREA_OPERATIVA" is different from the previous row (ordering by date asc).
For example, from row 1 to row 2 it shouldn't count anything because both have same area 'CROSS' but between rows 2 and 3, it should count (or sum 1, I don't care) since 'CROSS' and 'UTRDANIOS' are different. So the final count should be 3 for the whole table.
Is it possible to do this via query or do I need to make a script with a cursor for this purpose?
I've tried this:
SELECT a.creclama,
sum (CASE WHEN b.area_operativa NOT LIKE a.area_operativa THEN 1 ELSE 0 END) AS increment
FROM TR_ASGAREOPE a
INNER JOIN TR_ASGAREOPE b ON a.creclama = b.creclama
and a.cdistribuidora = b.cdistribuidora
and a.secuencia = b.secuencia
WHERE a.creclama = 10008354
group by a.creclama;
But is counting the full 12 rows.
EDIT:
Finally I could resolve this by the next query:
select sum (
CASE WHEN (comparacion.area_operativa not like comparacion.siguiente_fila THEN 1 ELSE 0 END) AS incremento
from (
select creclama,
area_operativa,
lead(area_operativa) over (order by fmodifica) as siguiente_fila
from TR_ASGAREOPE
where creclama = 10008354
order by fmodifica
);
Hope it is useful for someone in the future, it really got me stuck for a day. Thank you all guys.
You could try using analytic functions like lead or lag, for example
SELECT CRECLAMA,
CASE WHEN AREA_OPERATIVA <> NEXTROW THEN 1 ELSE 0 END AS INCREMENT
FROM (
SELECT CRECLAMA,
AREA_OPERATIVA,
LEAD(AREA_OPERATIVA) OVER (PARTITION BY 1 ORDER BY CRECLAMA) AS NEXTROW
FROM TR_ASGAREOPE
)
Here's an approach using LEAD:
WITH TR_ASGAREOPE(CRECLAMA, AREA_OPERATIVA, DATE_FIELD) AS
(SELECT 10008354, 'CROSS', DATE '2019-01-01' FROM DUAL UNION ALL
SELECT 10008354, 'CROSS', DATE '2019-01-02' FROM DUAL UNION ALL -- 1
SELECT 10008354, 'UTRDANIOS', DATE '2019-01-03' FROM DUAL UNION ALL -- 2
SELECT 10008354, 'EXP263', DATE '2019-01-04' FROM DUAL UNION ALL -- 3
SELECT 10008354, 'EXP6', DATE '2019-01-05' FROM DUAL UNION ALL
SELECT 10008354, 'EXP6', DATE '2019-01-06' FROM DUAL UNION ALL
SELECT 10008354, 'EXP6', DATE '2019-01-07' FROM DUAL UNION ALL
SELECT 10008354, 'EXP6', DATE '2019-01-08' FROM DUAL UNION ALL
SELECT 10008354, 'EXP6', DATE '2019-01-09' FROM DUAL UNION ALL
SELECT 10008354, 'EXP6', DATE '2019-01-10' FROM DUAL UNION ALL
SELECT 10008354, 'EXP6', DATE '2019-01-11' FROM DUAL UNION ALL
SELECT 10008354, 'EXP6', DATE '2019-01-12' FROM DUAL UNION ALL
SELECT 12345678, 'AREA49', DATE '2019-02-01' FROM DUAL UNION ALL
SELECT 12345678, 'AREA49', DATE '2019-02-02' FROM DUAL UNION ALL -- 1
SELECT 12345678, 'AREA50', DATE '2019-02-03' FROM DUAL UNION ALL
SELECT 12345678, 'AREA50', DATE '2019-02-04' FROM DUAL UNION ALL -- 2
SELECT 12345678, 'AREA52', DATE '2019-02-05' FROM DUAL UNION ALL
SELECT 12345678, 'AREA52', DATE '2019-02-06' FROM DUAL UNION ALL
SELECT 12345678, 'AREA52', DATE '2019-02-07' FROM DUAL UNION ALL -- 3
SELECT 12345678, 'AREA53', DATE '2019-02-08' FROM DUAL UNION ALL -- 4
SELECT 12345678, 'AREA52', DATE '2019-02-09' FROM DUAL UNION ALL -- 5
SELECT 12345678, 'AREA53', DATE '2019-02-10' FROM DUAL),
cteData AS (SELECT CRECLAMA,
LEAD(CRECLAMA) OVER (ORDER BY DATE_FIELD) AS NEXT_CRECLAMA,
AREA_OPERATIVA,
LEAD(AREA_OPERATIVA) OVER (ORDER BY DATE_FIELD) AS NEXT_AREA_OPERATIVA
FROM TR_ASGAREOPE)
SELECT CRECLAMA, COUNT(*)
FROM cteData
WHERE CRECLAMA = NEXT_CRECLAMA AND
AREA_OPERATIVA <> NEXT_AREA_OPERATIVA
GROUP BY CRECLAMA
ORDER BY CRECLAMA;
I added data for another CRECLAMA value to show how it would work.
Results:
CRECLAMA COUNT(*)
10008354 3
12345678 5
dbfiddle here
You can use lag() analytic function :
with t as
(
select a.*,
lag(a.area_operativa,1,a.area_operativa) over (order by a."date") as lg
from asgareope a
where a.creclama = 10008354
)
select t.creclama, sum(case when lg = area_operativa then 0 else 1 end) as "increment"
from t
group by t.creclama
I think you can simple use COUNT(DISTINCT ...) for your problem, if the AREA_OPERATIVA could not return to previously used value:
SELECT CRECLAMA, COUNT(DISTINCT AREA_OPERATIVA)
FROM TR_ASGAREOPE
GROUP BY CRECLAMA

PL/SQL distinct date for loop

I want to use for loop for date in my table which only cares years and months, not days.
CURSOR ret_cur is SELECT orderdate FROM Orders WHERE status
= 'DELAYED';
ret_rec ret_cur%ROWTYPE;
I currently have
insert into Orders(OrderId, CustomerId, RetailerId, ProductId, Count,
UnitPrice, OrderDate, Status) values (2,2,1,10,45,60,
to_date('20180102','YYYYMMDD'),'DELIVERED');
this data type in my orders table. (its an example for format)
I want to use DISTINCT to iterate through orderdate based on YYYY-MM. (dont care Day)
I have tried select distinct to_char(orderdate, 'YYYY-MM') but I seems to not work.
for example, if i have 20180103, 20180104, 20180105 , it should be one iteration since they all have same years and months.
To select days without time you could tunc(sysdate). For months we have to group by a char-value:
select to_char(mydatecol,'yyyymm'), count(*) from
(
select sysdate mydatecol from dual UNION ALL -- Fake-Table with some dates
select sysdate - 1 mydatecol from dual UNION ALL
select sysdate - 2 mydatecol from dual UNION ALL
select sysdate - 3 mydatecol from dual UNION ALL
select sysdate - 4 mydatecol from dual UNION ALL
select sysdate - 30 mydatecol from dual UNION ALL
select sysdate - 31 mydatecol from dual UNION ALL
select sysdate - 32 mydatecol from dual UNION ALL
select sysdate - 33 mydatecol from dual
)
group by to_char(mydatecol,'yyyymm')
Result:
201809 3
201810 6
I think you'd like to have such a collation as below :
with Orders
(
OrderId, CustomerId, RetailerId, ProductId,
Count, UnitPrice, OrderDate, Status
) as
(
select 2,2,1,10,45,60, to_date('20180102','YYYYMMDD'),'DELIVERED' from dual
)
select o.*
from Orders o
where to_char(OrderDate,'yyyy-mm')
= to_char(to_date('&myDate','yyyymmdd'),'yyyy-mm');
-- for "myDate" substitution variable use 20180103 or 20180104 or 20180105 .. etc.
The best way todo this kind of query is to truncate the date value:
SELECT CustomerId, trunc(OrderDate,'MM') OrderMonth
, sum(Count) totalCount
, sum(Count*UnitPrice) totalPrice
FROM Orders
GROUP BY CustomerId, trunc(OrderDate,'MM')
for example...

SQL: Dynamic Date creation issue

Need Suggestion to make it dynamic On Dates.
Expected:
Date, Total Sellers, Sellers From Previous Date
Currently:
Data in table(active_seller_codes): date, seller_code
Queries:
-- Date Wise Sellers Count
select date,count(distinct seller_code) as Sellers_COunt
from active_seller_codes where date between '2016-12-15' AND '2016-12-15'
-- Sellers from previous Days
select date,count(distinct seller_code) as Last_Day_Seller
from active_seller_codes
where date between '2016-12-15' AND '2016-12-15'
and seller_code IN(
select seller_code from active_seller_codes
where date between '2016-12-14' AND '2016-12-14'
)
group by 1
Database Using: Vertica
Reading attentively, you seem to want one row in the report, with the data from the search date in the first two columns and the data of the day before the search date in the third and fourth column, like so:
sales_date|sellers_count|prev_date |prev_sellers_count
2016-12-15| 8|2016-12-14| 5
The solution could be something like this (without the first Common Table Expression, which, in my case, contains the data, but in your case, the data would be in your active_seller_codes table.
WITH
-- initial input
(sales_date,seller_code) AS (
SELECT DATE '2016-12-15',42
UNION ALL SELECT DATE '2016-12-15',43
UNION ALL SELECT DATE '2016-12-15',44
UNION ALL SELECT DATE '2016-12-15',45
UNION ALL SELECT DATE '2016-12-15',46
UNION ALL SELECT DATE '2016-12-15',47
UNION ALL SELECT DATE '2016-12-15',48
UNION ALL SELECT DATE '2016-12-15',49
UNION ALL SELECT DATE '2016-12-14',42
UNION ALL SELECT DATE '2016-12-14',44
UNION ALL SELECT DATE '2016-12-14',46
UNION ALL SELECT DATE '2016-12-14',48
UNION ALL SELECT DATE '2016-12-14',50
UNION ALL SELECT DATE '2016-12-13',42
UNION ALL SELECT DATE '2016-12-13',43
UNION ALL SELECT DATE '2016-12-13',44
UNION ALL SELECT DATE '2016-12-13',45
UNION ALL SELECT DATE '2016-12-13',46
UNION ALL SELECT DATE '2016-12-13',47
UNION ALL SELECT DATE '2016-12-13',48
UNION ALL SELECT DATE '2016-12-13',49
)
,
-- search argument this, in the real query, would come just after the WITH keyword
-- as the above would be the source table
search_dt(search_dt) AS (SELECT DATE '2016-12-15')
,
-- the two days we're interested in, de-duped
distinct_two_days AS (
SELECT DISTINCT
sales_date
, seller_code
FROM active_seller_codes
WHERE sales_date IN (
SELECT search_dt FROM search_dt -- the search date
UNION ALL SELECT search_dt - 1 FROM search_dt -- the day before
)
)
,
-- the two days we want one above the other,
-- with index for the final pivot
vertical AS (
SELECT
ROW_NUMBER() OVER (ORDER BY sales_date DESC) AS idx
, sales_date
, count(DISTINCT seller_code) AS seller_count
FROM distinct_two_days
GROUP BY 2
)
SELECT
MAX(CASE idx WHEN 1 THEN sales_date END) AS sales_date
, SUM(CASE idx WHEN 1 THEN seller_count END) AS sellers_count
, MAX(CASE idx WHEN 2 THEN sales_date END) AS prev_date
, SUM(CASE idx WHEN 2 THEN seller_count END) AS prev_sellers_count
FROM vertical
;
sales_date|sellers_count|prev_date |prev_sellers_count
2016-12-15| 8|2016-12-14| 5

SQL: calculate MAU by window function

I'm trying unsuccessfully to calculate a MAU- monthly distinct active users, by using window functions.
I need the calculation for each day during the month, for the preceding 30 days
This is what I have so far:
select
t.datee
, t.app,i.sourcee
, i.campaign
, t.mobile
, sum(count(distinct t.user_id)) over (
PARTITION BY
date_trunc('month',datee)
, t.app
, i.sourcee
, i.campaign
, t.mobile
ORDER BY datee asc
ROWS BETWEEN 30 PRECEDING AND CURRENT ROW
)
FROM dim_x i
JOIN agg_y t
ON i.app=t.app
AND i.mobile=t.mobile
WHERE t.datee>=CURRENT_DATE-30
AND t.datee<CURRENT_DATE
GROUP BY 1,2,3,4,5
order by 1 desc
But all I get is a sum of active users by all days instead of sum of distinct users. I'm using Vertica db.
Any suggestions?
I'm not getting, really, why you should need an OLAP expression for that.
Aren't you looking for the total number of distinct users per:
year-month combination out of datee
app
sourcee (whatever that might be)
campaign
mobile (probably mobile number)
?
A simple GROUP BY would do, as far as I'm concerned. If I disregard sourcee, campaign and mobile, selecting just from one table: input for argument's sake, with some sample data I just made up, this query:
SELECT
YEAR(datee) * 100 + MONTH(datee) AS yearmonth
, app
, COUNT(DISTINCT user_id) AS monthly_active_users
FROM input
GROUP BY 1,2
ORDER BY 1
;
... would return:
YEARMONTH|app |monthly_active_users
201,601|app-a| 2
201,601|app-b| 2
201,602|app-a| 2
201,602|app-b| 2
201,603|app-a| 2
201,603|app-b| 2
201,604|app-a| 2
201,604|app-b| 2
201,605|app-a| 2
201,605|app-b| 2
201,606|app-a| 1
201,606|app-b| 1
Just editing my previous answer. You seem to need the running COUNT DISTINCT of user id-s , partitioned by several expressions.
With the input from the WITH clause below, would you need a report like this (only showing the first 12 rows of 53, ordered by datee, app)?
datee |app |user_id |running_active_users
2016-01-01|app-a|arthur | 1
2016-01-04|app-b|ford | 1
2016-01-07|app-a|trillian| 2
2016-01-10|app-b|zaphod | 2
2016-01-13|app-a|arthur | 2
2016-01-16|app-b|ford | 2
2016-01-19|app-a|trillian| 2
2016-01-22|app-b|zaphod | 2
2016-01-25|app-a|arthur | 2
2016-01-28|app-b|ford | 2
2016-01-31|app-a|trillian| 2
2016-02-03|app-b|zaphod | 2
?
If that's the case, I don't see the reason for existence of your GROUP BY clause, though.
Below is the query with GROUP BY as above with test data returning the results above in a WITH clause. Regard that input as the join between your two tables.
WITH
input(datee,app,user_id) AS (
SELECT DATE '2016-01-01','app-a','arthur'
UNION ALL SELECT DATE '2016-01-04','app-b','ford'
UNION ALL SELECT DATE '2016-01-07','app-a','trillian'
UNION ALL SELECT DATE '2016-01-10','app-b','zaphod'
UNION ALL SELECT DATE '2016-01-25','app-a','arthur'
UNION ALL SELECT DATE '2016-01-28','app-b','ford'
UNION ALL SELECT DATE '2016-03-04','app-b','ford'
UNION ALL SELECT DATE '2016-03-25','app-a','arthur'
UNION ALL SELECT DATE '2016-04-09','app-b','ford'
UNION ALL SELECT DATE '2016-04-30','app-a','arthur'
UNION ALL SELECT DATE '2016-05-06','app-a','trillian'
UNION ALL SELECT DATE '2016-05-09','app-b','zaphod'
UNION ALL SELECT DATE '2016-05-15','app-b','ford'
UNION ALL SELECT DATE '2016-06-05','app-a','arthur'
UNION ALL SELECT DATE '2016-01-13','app-a','arthur'
UNION ALL SELECT DATE '2016-01-16','app-b','ford'
UNION ALL SELECT DATE '2016-01-31','app-a','trillian'
UNION ALL SELECT DATE '2016-02-03','app-b','zaphod'
UNION ALL SELECT DATE '2016-02-06','app-a','arthur'
UNION ALL SELECT DATE '2016-02-09','app-b','ford'
UNION ALL SELECT DATE '2016-02-12','app-a','trillian'
UNION ALL SELECT DATE '2016-02-15','app-b','zaphod'
UNION ALL SELECT DATE '2016-02-18','app-a','arthur'
UNION ALL SELECT DATE '2016-02-21','app-b','ford'
UNION ALL SELECT DATE '2016-02-24','app-a','trillian'
UNION ALL SELECT DATE '2016-02-27','app-b','zaphod'
UNION ALL SELECT DATE '2016-03-01','app-a','arthur'
UNION ALL SELECT DATE '2016-03-10','app-b','zaphod'
UNION ALL SELECT DATE '2016-03-13','app-a','arthur'
UNION ALL SELECT DATE '2016-03-16','app-b','ford'
UNION ALL SELECT DATE '2016-03-28','app-b','ford'
UNION ALL SELECT DATE '2016-03-31','app-a','trillian'
UNION ALL SELECT DATE '2016-04-06','app-a','arthur'
UNION ALL SELECT DATE '2016-04-12','app-a','trillian'
UNION ALL SELECT DATE '2016-04-15','app-b','zaphod'
UNION ALL SELECT DATE '2016-04-27','app-b','zaphod'
UNION ALL SELECT DATE '2016-05-03','app-b','ford'
UNION ALL SELECT DATE '2016-05-27','app-b','ford'
UNION ALL SELECT DATE '2016-05-30','app-a','trillian'
UNION ALL SELECT DATE '2016-01-19','app-a','trillian'
UNION ALL SELECT DATE '2016-01-22','app-b','zaphod'
UNION ALL SELECT DATE '2016-03-07','app-a','trillian'
UNION ALL SELECT DATE '2016-03-19','app-a','trillian'
UNION ALL SELECT DATE '2016-03-22','app-b','zaphod'
UNION ALL SELECT DATE '2016-04-03','app-b','zaphod'
UNION ALL SELECT DATE '2016-04-18','app-a','arthur'
UNION ALL SELECT DATE '2016-04-21','app-b','ford'
UNION ALL SELECT DATE '2016-04-24','app-a','trillian'
UNION ALL SELECT DATE '2016-05-12','app-a','arthur'
UNION ALL SELECT DATE '2016-05-18','app-a','trillian'
UNION ALL SELECT DATE '2016-05-21','app-b','zaphod'
UNION ALL SELECT DATE '2016-05-24','app-a','arthur'
UNION ALL SELECT DATE '2016-06-02','app-b','zaphod'
)
SELECT
YEAR(datee) * 100 + MONTH(datee) AS YEARMONTH
, app
, COUNT(DISTINCT user_id) AS monthly_active_users
FROM input
GROUP BY 1,2
ORDER BY 1
;