Postgres: Date grouping in Subquery from timestamp - postgresql

I am trying find out how many leads are generated per listing per day.
I have this query:
SELECT
vl.listing_id,
vl.created_at::date as dt,
(
SELECT count(*)
FROM voice_leads vl2
WHERE vl2.listing_id = vl.listing_id
AND vl.created_at::date = vl2.created_at::date
) as cnt
FROM voice_leads vl
GROUP BY listing_id, vl.created_at::date
ORDER BY listing_id
but when executing I get "ERROR: subquery uses ungrouped column "vl.created_at" from outer query LINE 8: AND vl.created_at::date = vl2.created_at::date"
Any idea on what I could do to fix it?

SELECT
vl.listing_id,
vl.created_at::date as dt,
count(cnt.*)
FROM voice_leads vl, lateral (SELECT *
from voice_leads vl2
WHERE vl2.listing_id = vl.listing_id
AND vl.created_at::date = vl2.created_at::date) cnt
GROUP BY vl.listing_id, vl.created_at::date
ORDER BY listing_id

You don't need the subquery:
SELECT
vl.listing_id,
vl.created_at::date as dt,
count( vl.listing_id ) as cnt
FROM voice_leads vl
GROUP BY listing_id, vl.created_at::date
ORDER BY listing_id
should do the same.
count(field) will count the number of rows in each group.
count(*) will count the total number of rows.

Related

Sub query in SELECT - ungrouped column from outer query

I have to calculate the ARPU (Revenue / # users) but I got this error:
subquery uses ungrouped column "usage_records.date" from outer query
LINE 7: WHERE created_at <= date_trunc('day', usage_records.d... ^
Expected results:
Revenue(day) = SUM(quantity_eur) for that day
Users Count (day) = Total signed up users before that day
Postgresql (Query)
SELECT
date_trunc('day', usage_records.date) AS day,
SUM(usage_records.quantity_eur) as Revenue,
( SELECT
COUNT(users.id)
FROM users
WHERE created_at <= date_trunc('day', usage_records.date)
) as users_count
FROM users
INNER JOIN ownerships ON (ownerships.user_id = users.id)
INNER JOIN profiles ON (profiles.id = ownerships.profile_id)
INNER JOIN usage_records ON (usage_records.profile_id = profiles.id)
GROUP BY DAY
ORDER BY DAY asc
your subquery (executed for each row ) cointain a column nont mentioned in group by but not involeved in aggregation ..
this produce error
but you could refactor your query using a contional also for this value
SELECT
date_trunc('day', usage_records.date) AS day,
SUM(usage_records.quantity_eur) as Revenue,
sum( case when created_at <= date_trunc('day', usage_records.date)
AND users.id is not null
then 1 else 0 end ) users_count
FROM users
INNER JOIN ownerships ON (ownerships.user_id = users.id)
INNER JOIN profiles ON (profiles.id = ownerships.profile_id)
INNER JOIN usage_records ON (usage_records.profile_id = profiles.id)
GROUP BY DAY
ORDER BY DAY asc

postgres how to properly use max function

Help please with correct usage of max, I have the following:
select busqueda.valorBusqueda, count(*) from busqueda where usu_id = 24 group by busqueda.valorBusqueda;
and it works, but I want only the max count of it, so far I tried:
select max (busqueda.valorBusqueda, count(*) from busqueda where usu_id = 24 group by busqueda.valorBusqueda);
but no success..
The easiest solution here is probably to use a LIMIT query:
select valorBusqueda, count(*) as cnt
from busqueda
where usu_id = 24
group by valorBusqueda
order by count(*) desc
limit 1;
Postgres does not support ties with LIMIT, but we can use the RANK analytic function here if you do want all ties for the highest count:
with cte as (
select valorBusqueda, count(*) as cnt, rank() over (order by count(*) desc) rnk
from busqueda
where usu_id = 24
group by valorBusqueda
)
select valorBusqueda, cnt
from cnt
where rnk = 1;
you can use subquery.
select max(t.ct) from (
select busqueda.valorBusqueda, count(*) ct from busqueda
where usu_id = 24 group by busqueda.valorBusqueda) t;

How to select from relationship based on the height value of field postgres?

i have three tables vehicles and trips and componentValues they are related to each other by
vehicles -> trips -> componentValues
vehicles Table : id, ...
trips Table: id, vehicle_id, ...
componentValues Table: id, trip_id, damage, ...
and i'm trying to get all the trips with the highest damage component form the componentValues table like this
SELECT *
FROM (select * from trips WHERE "trips"."vehicle_id" = '7') as t
LEFT JOIN (
select * from "component_values"
where trip_id = '85'
order by damage
desc nulls last limit 1
) as h on t.id = h.trip_id
how can i change the line where trip_id = '85' to be dynamic or is their another way to do this and many thanks in advance.
expected result:
UPDATE
i have did some query that get what i want but how can i improve it by not using sub queries in the select statement
select * ,
(select damage from "component_values" where trip_id = trips.id order by damage desc nulls last limit 1) as h_damage,
(select damage from "component_values" where trip_id = trips.id order by damage asc nulls first limit 1) as l_damage,
(select component_types.name from "component_values" left join component_types on component_values.component_type_id = component_types.id where trip_id = trips.id order by damage desc nulls last limit 1) as hc_damage,
(select component_types.name from "component_values" left join component_types on component_values.component_type_id = component_types.id where trip_id = trips.id order by damage asc nulls first limit 1) as lc_damage
from trips
WHERE trips."vehicle_id" = '7'
I think you want distinct on:
select distinct on (t.id) t.*, dv.damage
from trips t join
component_values cv
on cv.trip_id = t.id
where t.vehicle_id = 7 -- not sure if this is needed
order by t.id, cv.damage desc nulls last;
distinct on is usually the most efficient method in Postgres. You can also do this with window functions:
select distinct on (t.id) t.*, cv.damage
from trips t join
(select cv.*,
row_number() over (partition by cv.trip_id, cv.damage desc nulls last) as seqnum
from component_values cv
) cv
on cv.trip_id = t.id and cv.seqnum = 1
where t.vehicle_id = 7; -- not sure if this is needed
I think you want a lateral join.
SELECT *
FROM (select * from trips WHERE "trips"."vehicle_id" = '7') as t
LEFT JOIN lateral (
select * from "component_values"
where trip_id = t.id
order by damage
desc nulls last limit 1
) as h on true
Although I don't think there is a reason for the first subquery, so:
SELECT *
FROM trips
LEFT JOIN lateral (
select * from "component_values"
where trip_id = trips.id
order by damage
desc nulls last limit 1
) as h on true
WHERE "trips"."vehicle_id" = '7'

SQL 2008 cumulative count

I have a query returning the number of rows grouped by date :
SELECT convert(date, run.TimeStamp) as TimeStamp, count(*)
FROM ScriptResult AS res INNER JOIN
ScriptRun AS run ON run.ScriptRunID = res.ScriptRunID INNER JOIN
WorkListItems AS wli ON wli.WorkListItemID = res.WorklistItemID INNER JOIN
WorkList AS wl ON wl.WorkListID = wli.WorkListID
WHERE (wli.WorkListID = #WLID)
GROUP by convert(date, run.TimeStamp)
ORDER BY convert(date, run.TimeStamp);
This produces a result set like this :
TimeStamp (ItemCount)
2015-03-10 5364
2015-03-11 22027
2015-03-12 18037
Now what I want, is to cumulatively summarize the itemcount, like this :
TimeStamp ItemCount TotalCount
2015-03-10 5364 5364
2015 -03-11 22027 27391
2015-03-12 18037 45428
The query needs to be compatible with 2008R2.
I have played with [count ...over..partition by] in several variations but the problem is that the window function boundary should chage. And I cannot use ROWS or RANGE.
Any ideas please ?
Thanks in advance.
Try with correlated subquery:
;WITH cte as(
SELECT convert(date, run.TimeStamp) as TimeStamp, count(*) AS S
FROM ScriptResult AS res INNER JOIN
ScriptRun AS run ON run.ScriptRunID = res.ScriptRunID INNER JOIN
WorkListItems AS wli ON wli.WorkListItemID = res.WorklistItemID INNER JOIN
WorkList AS wl ON wl.WorkListID = wli.WorkListID
WHERE (wli.WorkListID = #WLID)
GROUP by convert(date, run.TimeStamp)
)
SELECT TimeStamp,
S,
(SELECT SUM(S) FROM cte t2 WHERE t2.TimeStamp <= t1.TimeStamp) AS TS
FROM cte t1
You could try creating a temp table to hold the first query results that you can further aggregate to return the cumulative sum on the ItemCount field:
CREATE TABLE #TempTable(
[SeqNo] [int] NULL,
[TimeStamp] [Date] NULL,
[ItemCount] [int] NULL
) ON [PRIMARY]
SELECT
ROW_NUMBER() OVER (PARTITION BY res.ScriptRunID ORDER BY run.TimeStamp) AS SeqNo,
CONVERT(Date, run.TimeStamp) AS TimeStamp,
COUNT(*) AS ItemCount
INTO #TempTable
FROM ScriptResult AS res
INNER JOIN ScriptRun AS run
ON run.ScriptRunID = res.ScriptRunID
INNER JOIN WorkListItems AS wli
ON wli.WorkListItemID = res.WorklistItemID
INNER JOIN WorkList AS wl
ON wl.WorkListID = wli.WorkListID
WHERE (wli.WorkListID = #WLID)
GROUP BY CONVERT(Date, run.TimeStamp)
ORDER BY CONVERT(Date, run.TimeStamp);
SELECT
t1.TimeStamp,
t1.ItemCount,
SUM(t2.ItemCount) AS TotalCount
FROM #TempTable AS t1
INNER JOIN #TempTable AS t2
on t1.SeqNo >= t2.SeqNo
GROUP BY t1.TimeStamp, t1.ItemCount
ORDER BY t1.TimeStamp
SQL Fiddle Example
Note: This links to a Microsoft SQL Server 2014 database version SQL fiddle which should work with SQL Server 2008 as well.

Postgresql query COUNT and MAX together?

SELECT id,icon,type,cnt
FROM capability
JOIN (
SELECT s0_.capability_id AS capability_id0 ,
count(capability_id) as cnt
FROM service_offer_capability s0_
INNER JOIN service_offer s1_ ON s0_.service_offer_id = s1_.id
WHERE s0_.value <> 'i:0;' AND s1_.service_id = 2
GROUP BY s0_.capability_id
) af
ON af.capability_id0=id;
All i want to do is to have a max(cnt) as an extra column. I know that you can order by cnt and get the first but i am looking for an alternative..Is it possible or i have to run multiple queries?
This should do it:
SELECT id,
icon,
type,
cnt,
max(cnt) over () as max_cnt
FROM capability
JOIN (
SELECT s0_.capability_id AS capability_id0 ,
count(capability_id) as cnt
FROM service_offer_capability s0_
INNER JOIN service_offer s1_ ON s0_.service_offer_id = s1_.id
WHERE s0_.value <> 'i:0;' AND s1_.service_id = 2
GROUP BY s0_.capability_id
) af
ON af.capability_id0=id;