I have to create a view in postgresql with calculated columns, but my query is taking a very very long time to run.
I have a table called surveys which contains one entry for every survey.
Sno= Serial number of person who was called
survey round = surveys are conducted for multiple periods called rounds, upon which we need to group and calculate metrics
call_status=status of survey calls
I want to get stats per round like how many users were called i.e. unique sno, how many calls were completed, count per different call status values
My current query is =
select distinct sc.survey_round,
(select count(distinct sno) FROM "Suvita".survey_call_details sc1 where sc.survey_round =sc1.survey_round ) as "Total respondents call",
(select count(*) fROM "Suvita".survey_call_details sc2 where sc.survey_round =sc2.survey_round and sc2.iscomplete = 'Yes') as "Survey Completed",
(select count(*) FROM "Suvita".survey_call_details sc3 where sc.survey_round =sc3.survey_round) as "Calls Attempted",
(select count(*) FROM "Suvita".survey_call_details sc4 where sc.survey_round =sc4.survey_round and sc4.call_status = 'Picked Up') as "Calls Answered"
FROM "Suvita".survey_call_details sc
Please suggest a better way to write these queries in postgresql.
Edit: I have got the answer. Based on Alexey's response, I re-qrote the queries using count and sum and they worked really fast!!
SELECT sc.survey_round,
COUNT(distinct sc.sno) as "Total respondents call",
COUNT(*) as "Calls Attempted",
sum(CASE WHEN sc.call_status = 'Picked Up' THEN 1 ELSE 0 END) as "Calls Answered",
sum(CASE WHEN sc.iscomplete = 'Yes' THEN 1 ELSE 0 END) as "Survey Completed",
sum(CASE WHEN sc.nominate = 'Yes' THEN 1 ELSE 0 END) as "Agreed to Nominate"
FROM "Suvita".survey_call_details sc
group by sc.survey_round order by sc.survey_round desc
CodePudding user response:
First, you do not need subqueries to count something in one table, just use CASE statement with conditions in COUNT.
Second, COUNT(DISTINCT sno) can be slower then COUNT(sno). To get distinct sno values you can use JOIN LATERAL, that would be used as a subquery.
Then your query become this
SELECT
sc.survey_round,
COUNT(sc1.sno) as "Total respondents call",
COUNT(CASE WHEN sc.iscomplete = 'Yes' THEN 1 ELSE 0 END) as "Survey Completed",
COUNT(*) as "Calls Attempted",
COUNT(CASE WHEN sc.call_status = 'Picked Up' THEN 1 ELSE 0 END) as "Calls Answered"
FROM "Suvita".survey_call_details sc
JOIN LATERAL (
SELECT DISTINCT sno
FROM "Suvita".survey_call_details
WHERE survey_round = sc.survey_round
) sc1 ON TRUE
GROUP BY sc.survey_round
Atlast, you should have indexes on survey_round and sno columns. With live data maybe complex index on survey_round sno would perform better.
