Postgres - ERROR: aggregate functions are not allowed in GROUP BY Position: 305 - postgresql

Having a little trouble with a query provided by Periscope. Can you help point me in the right direction?
Error is - ERROR: aggregate functions are not allowed in GROUP BY Position: 305
with monthly_activity as (
select distinct
date_trunc('month', created_at) as month,
user_id
from oauth_refresh_tokens
),
first_activity as (
select user_id, date(min(created_at)) as month
from oauth_refresh_tokens
group by 2
)
select
this_month.month,
count(distinct user_id)
from monthly_activity this_month
left join monthly_activity last_month
on this_month.user_id = last_month.user_id
and this_month.month = last_month.month + interval '1 month'
join first_activity
on this_month.user_id = first_activity.user_id
and first_activity.month != this_month.month
where last_month.user_id is null
group by 1

Related

How to get number of consecutive days from current date using postgres?

I want to get the number of consecutive days from the current date using Postgres SQL.
enter image description here
Above is the scenario in which I have highlighted consecutive days count should be like this.
Below is the SQL query which I have created but it's not returning the expected result
with grouped_dates as (
select user_id, created_at::timestamp::date,
(created_at::timestamp::date - (row_number() over (partition by user_id order by created_at::timestamp::date) || ' days')::interval)::date as grouping_date
from watch_history
)
select * , dense_rank() over (partition by grouping_date order by created_at::timestamp::date) as in_streak
from grouped_dates where user_id = 702
order by created_at::timestamp::date
Can anyone please help me to resolve this issue?
If anyhow we can able to apply distinct for created_at field to below query then I will get solutions for my issue.
WITH list AS
(
SELECT user_id,
(created_at::timestamp::date - (row_number() over (partition by user_id order by created_at::timestamp::date) || ' days')::interval)::date as next_day
FROM watch_history
)
SELECT user_id, count(*) AS number_of_consecutive_days
FROM list
WHERE next_day IS NOT NULL
GROUP BY user_id
Does anyone have an idea how to apply distinct to created_at for the above mentioned query ?
To get the "number of consecutive days" for the same user_id :
WITH list AS
(
SELECT user_id
, array_agg(created_at) OVER (PARTITION BY user_id ORDER BY created_at RANGE BETWEEN CURRENT ROW AND '1 day' FOLLOWING) AS consecutive_days
FROM watch_history
)
SELECT user_id, count(DISTINCT d.day) AS number_of_consecutive_days
FROM list
CROSS JOIN LATERAL unnest(consecutive_days) AS d(day)
WHERE array_length(consecutive_days, 1) > 1
GROUP BY user_id
To get the list of "consecutive days" for the same user_id :
WITH list AS
(
SELECT user_id
, array_agg(created_at) OVER (PARTITION BY user_id ORDER BY created_at RANGE BETWEEN CURRENT ROW AND '1 day' FOLLOWING) AS consecutive_days
FROM watch_history
)
SELECT user_id
, array_agg(DISTINCT d.day ORDER BY d.day) AS list_of_consecutive_days
FROM list
CROSS JOIN LATERAL unnest(consecutive_days) AS d(day)
WHERE array_length(consecutive_days, 1) > 1
GROUP BY user_id
full example & result in dbfiddle

How to divide a period in columns

I am trying to create a query where the first column shows the list of the companies and the other 3 columns their revenues per month. This is what I do:
WITH time_frame AS
(SELECT date_trunc('month',NOW())-interval '0 week'),
time_frame1 AS
(SELECT date_trunc('month',NOW())-interval '1 month'),
time_frame2 AS
(SELECT date_trunc('month',NOW())-interval '2 month')
select table1.company_name,
(CASE
WHEN table2.date_of_transaction = (SELECT * FROM time_frame2) THEN sum(table2.amount)
ELSE NULL
END) AS "current week - 2",
(CASE
WHEN table2.date_of_transaction = (SELECT * FROM time_frame1) THEN sum(table2.amount)
ELSE NULL
END) AS "current week - 1",
(CASE
WHEN table2.date_of_transaction = (SELECT * FROM time_frame2) THEN
sum(table2.amount)
ELSE NULL
END) AS "current week - 2"
from table1
join table2 on table2.table1_id = table.id
where table1.company_joined >= '04-20-2019'
group by 1
When I execute the table this comes out: Error running query: column "table2.date_of_transaction" must appear in the GROUP BY clause or be used in an aggregate function LINE 15: WHEN table2.date_of_transaction = (SELECT * FROM time_frame) TH... ^
Do you have any ideas on how to solve it? Thank you.
company name
month1
month2
name 1
£233
£343
name 2
£243
£34
name 3
£133
£43
you can simplify the statement by using the filter() operator
select t1.company_name,
sum(t2.amount) filter (where t2.date_of_transaction = date_trunc('month',NOW())-interval '2 month'),
sum(t2.amount) filter (where t2.date_of_transaction = date_trunc('month',NOW())-interval '1 month'),
sum(t2.amount) filter (where t2.date_of_transaction = date_trunc('month',NOW()))
from table1 t1
join table2 t2 on t2.table1_id = t1.id
where t1.company_joined >= date '2019-04-20'
group by t1.company_name;
If you really want to put the date ranges into a CTE, you only need one:
with dates (r1, r2, r3) as (
values
(date_trunc('month',NOW())-interval '2 month',
date_trunc('month',NOW())-interval '1 month',
date_trunc('month',NOW()))
)
select t1.company_name,
sum(t2.amount) filter (where t2.date_of_transaction = d.r1),
sum(t2.amount) filter (where t2.date_of_transaction = d.r2),
sum(t2.amount) filter (where t2.date_of_transaction = d.r3)
from table1 t1
cross join dates d
join table2 t2 on t2.table1_id = t1.id
where t1.company_joined >= date '2019-04-20'
group by t1.company_name
;
The CTE dates returns a single row with three columns and thus the cross join doesn't change the resulting number of rows.

How do I avoid joining multiple times when using union all statement?

I was working on a query where I hit a point:
SELECT tpd.timestamp::Date,'Mon' AS Label,
count(tpd.aggregated)
FROM tap.deving AS tpd INNER JOIN
(select DATE_TRUNC('week', timestamp), MAX(timestamp) AS max_timestamp
from tap.deving
group by DATE_TRUNC('week', timestamp)
) b
on tpd.timestamp = b.max_timestamp
left JOIN ca.hardware AS ch ON tpd.dev = ch.name
left JOIN ca.sites AS css ON css.id = ch.id
WHERE (tpd.aggregated=TRUE)
AND (css.country='USA') and (tpd.timestamp::date=now()::Date - interval '1 day') group by tpd.timestamp
UNION ALL
SELECT tpd.timestamp::date,'Tap but not' AS Label,
count(tpd.tap)
FROM tap.deving AS tpd INNER JOIN
(select DATE_TRUNC('week', timestamp), MAX(timestamp) AS max_timestamp
from tap.deving
group by DATE_TRUNC('week', timestamp)
) b
on tpd.timestamp = b.max_timestamp
left JOIN ca.hardware AS ch ON tpd.dev = ch.name
left JOIN ca.sites AS css ON css.id = ch.id
WHERE (tpd.tap=true)
AND (tpd.aggregated=false) and (tpd.needs_to_be=true)
AND (css.country='USA') and (tpd.timestamp::date=now()::Date - interval '1 day') group by tpd.timestamp
I wrote this query with the help of many SO posts and it has gone quite messy and super slow. I could not get my head on how to optimize this query.
Can you please try this query.
SELECT tpd.timestamp::Date,CASE tpd.aggregated
WHEN false THEN 'Tap but not'
WHEN true THEN 'Mon' as Label,
count(tpd.aggregated)
FROM tap.deving AS tpd INNER JOIN
(select DATE_TRUNC('week', timestamp), MAX(timestamp) AS max_timestamp
from tap.deving
group by DATE_TRUNC('week', timestamp)
) b
on tpd.timestamp = b.max_timestamp
left JOIN ca.hardware AS ch ON tpd.dev = ch.name
left JOIN ca.sites AS css ON css.id = ch.id
WHERE ((tpd.aggregated=TRUE) or ((tpd.tap=true) AND (tpd.aggregated=false) and (tpd.needs_to_be=true)))
AND (css.country='USA') and (tpd.timestamp::date=now()::Date - interval '1 day') group by tpd.timestamp;

Generate count of group memberships x days after group creation

I have a table of group_id, member_id, and created_at.
I'm trying to track the growth in group membership across time. Since group_id's are created when the first member_id joins, the min(created_at) for a given group should give the created date. I think this broken code gets the point across for what I'm trying to do (at the month level in this case):
SELECT brand_id,
min(created_at) as created_date,
min(created_at) + INTERVAL '1 month' as end_date,
count(member_id)
FROM member_group
HAVING created_at < end_date
group by 1
It seems to me that you are looking for a query like this:
SELECT g.brand_id, x.created_date, x.end_date, count(g.member_id)
FROM member_group g
JOIN (
SELECT brand_id,
min(created_at) as created_date,
min(created_at) + INTERVAL '1' month as end_date
FROM member_group
GROUP BY brand_id
) x
ON ( g.brand_id = x.brand_id
AND g.created_at BETWEEN x.created_date AND x.end_date )
GROUP BY g.brand_id, x.created_date, x.end_date
select
brand_id,
count(created_at < brand_date + interval '1 month' or null) as total
from
member_group
inner join (
select brand_id, min(created_at) as brand_date
from member_group
group by 1
) s using (brand_id)
group by 1
order by 1
;

Join on generate_series and count

I'm trying to find the # users who did action A or action B on a monthly basis.
Table: User
- id
- "creationDate"
Table: action_A
- user_id (= user.id)
- "creationDate"
Table: action_B
- user_id (= user.id)
- "creationDate"
The general idea of what I was trying to do was that I'd find the list of users who did action A in Month X and the list of users who did action B in Month X, then count how many ids are there for every month based on a generate_series of monthly dates.
I tried the following, however, the query times out when running and I'm not sure if there's any way to optimize it (or if it is even correct).
SELECT monthseries."Month", count(*)
FROM
(SELECT to_char(DAY::date, 'YYYY-MM') AS "Month"
FROM generate_series('2014-01-01'::date, CURRENT_DATE, '1 month') DAY) monthseries
LEFT JOIN
(SELECT to_char("creationDate", 'YYYY-MM') AS "Month",
id
FROM action_A) did_action_A ON monthseries."Month" = did_action_A."Month"
LEFT JOIN
(SELECT to_char("creationDate", 'YYYY-MM') AS "Month",
id
FROM action_B) did_action_B ON monthseries."Month" = did_action_B."Month"
GROUP BY monthseries."Month"
Any comments/ help would be immensely helpful!
If you want to count distinct users:
select to_char(month, 'YYYY-MM') as "Month", count(*)
from
generate_series(
'2014-01-01'::date, current_date, '1 month'
) monthseries (month)
left join (
(
select distinct date_trunc('month', "creationDate") as month, id
from action_a
) a
full outer join (
select distinct date_trunc('month', "creationDate") as month, id
from action_b
) b using (month, id)
) s using (month)
group by 1
order by 1