I need help optimize below postgresql query.
Currently its taking more than 3 min to get the data , due to that my API got timeout ,can you please help me to get the result set within 59 seconds.
Thanks
WITH assessment_path_resource AS
(
SELECT group_path_resources.*,
learning_record_store_user_activities.registration::text AS registration_id,
/*group_path_resources.id AS group_path_resource_id,
group_path_resources.created_at,*/
learning_record_store_user_activities.user_id,
learning_record_store_user_activities.assigned_through_id AS path_id,
spaces.title AS path_name,
programs_cohorts.id AS cohort_id,
programs_cohorts.title AS cohort_name,
programs_programs.id AS program_id,
programs_programs.title AS program_name,
programs_programs.tenant_id,
--group_path_resources.context_data ->> 'xapi_activity_id'::text AS xapi_activity_id,
concat(COALESCE(members.first_name, ''::character varying), ' ', COALESCE(members.last_name, ''::character varying)) AS participant_full_name,
row_number() OVER (PARTITION BY learning_record_store_user_activities.registration
ORDER BY group_path_resources.created_at DESC) AS duplicate_registration_id_row_number
from
(
select group_path_resources.id AS group_path_resource_id,
group_path_resources.created_at,
group_path_resources.context_data ->> 'xapi_activity_id'::text AS xapi_activity_id,
resourceable_id
FROM group_path_resources
WHERE group_path_resources.resourceable_type::text = 'LearningRecordStore::ActivityInstance'::text
AND (group_path_resources.context_data ->> 'xapi_activity_id'::text) ~~ 'https://assessment.%360%'::text
)group_path_resources
LEFT JOIN learning_record_store_user_activities ON group_path_resources.resourceable_id = learning_record_store_user_activities.activity_instance_id
JOIN members ON members.user_id = learning_record_store_user_activities.user_id
JOIN spaces ON spaces.id = learning_record_store_user_activities.assigned_through_id
JOIN programs_cohorts ON programs_cohorts.id = spaces.cohort_id
JOIN programs_programs ON programs_programs.id = programs_cohorts.program_id
-- WHERE group_path_resources.resourceable_type::text = 'LearningRecordStore::ActivityInstance'::text
-- AND (group_path_resources.context_data ->> 'xapi_activity_id'::text) ~~ 'https://assessment.%360%'::text
),
as_subject_form_response AS
(
SELECT
as_form_response.form_response_group_id,
as_form_response.updated_date
FROM assessment_service.form_response as_form_response
WHERE as_form_response.form_response_status_type = 'complete'::form_response_status_type
AND as_form_response.submitter_type = 'subject'::audience_type_enum
),
as_invitee_form_response AS
(
SELECT as_form_response.form_response_group_id,
count(as_form_response.id) AS total,
array_agg(as_form_response.updated_date ORDER BY as_form_response.updated_date) AS updated_dates
FROM assessment_service.form_response as_form_response
WHERE as_form_response.form_response_status_type = 'complete'::form_response_status_type
AND as_form_response.submitter_type = 'invitee'::audience_type_enum
GROUP BY as_form_response.form_response_group_id
),
as_feedback_requests AS
(
SELECT feedback_request.form_response_group_id,
sum(
CASE
WHEN feedback_request.is_fulfilled AND (feedback_request.is_declined IS NULL OR NOT feedback_request.is_declined) AND (feedback_request.is_archived IS NULL OR NOT feedback_request.is_archived) THEN 1
ELSE 0
END) AS total_completed,
max(EXTRACT(day FROM CURRENT_TIMESTAMP - feedback_request.created_date)::integer) AS days_passed_since_first_fbr,
min(feedback_request.created_date) AS first_fbr_created_date
FROM assessment_service.feedback_request
GROUP BY feedback_request.form_response_group_id
),
as_form_response_group AS
(
SELECT form_response_group.id,
form_response_group.registration_id::text,
form_response_group.subject_id AS user_id,
form_response_group.tenant_id,
form_response_group.path_id,
form_response_group.created_date,
form_response_group.released_date,
form_response_group.subject_viewable AS is_released,
COALESCE(as_subject_form_response.form_response_group_id IS NOT NULL AND (COALESCE(as_feedback_requests.total_completed, 0::bigint) >= COALESCE((form_response_group.form_configuration ->> 'min_feedback_requests'::text)::integer, 0) OR COALESCE(as_feedback_requests.days_passed_since_first_fbr, 0) >= COALESCE((form_response_group.form_configuration ->> 'minimum_release_timeline'::text)::integer, 21)), false) AS is_releasable,
CASE
WHEN as_subject_form_response.form_response_group_id IS NOT NULL AND COALESCE(as_feedback_requests.total_completed, 0::bigint) >= COALESCE((form_response_group.form_configuration ->> 'min_feedback_requests'::text)::integer, 0) THEN 'min_feedback_requests_threshold_met'::text
WHEN as_subject_form_response.form_response_group_id IS NOT NULL AND COALESCE(as_feedback_requests.days_passed_since_first_fbr, 0) >= COALESCE((form_response_group.form_configuration ->> 'minimum_release_timeline'::text)::integer, 21) THEN 'minimum_release_timeline_threshold_met'::text
ELSE 'not_eligible_for_release'::text
END AS is_releasable_reason,
COALESCE((form_response_group.form_configuration ->> 'min_feedback_requests'::text)::integer, 0) AS minimum_feedback_requests,
COALESCE((form_response_group.form_configuration ->> 'minimum_release_timeline'::text)::integer, 21) AS minimum_release_timeline,
COALESCE(as_feedback_requests.total_completed, 0::bigint) AS feedback_requests_completed,
COALESCE(as_feedback_requests.days_passed_since_first_fbr, 0) AS days_passed_since_first_fbr,
as_subject_form_response.form_response_group_id IS NOT NULL AS is_self_assessment_completed,
CASE
WHEN COALESCE(as_feedback_requests.total_completed, 0::bigint) >= COALESCE((form_response_group.form_configuration ->> 'min_feedback_requests'::text)::integer, 0) THEN as_invitee_form_response.updated_dates[(form_response_group.form_configuration ->> 'min_feedback_requests'::text)::integer]
ELSE NULL::timestamp with time zone
END AS minimum_feedback_requests_completed_threshold_met_date,
as_feedback_requests.first_fbr_created_date,
as_subject_form_response.updated_date AS self_response_updated_date,
as_feedback_requests.total_completed AS total_fbrs_completed
FROM assessment_service.form_response_group
LEFT JOIN as_feedback_requests ON as_feedback_requests.form_response_group_id = form_response_group.id
LEFT JOIN as_subject_form_response ON as_subject_form_response.form_response_group_id = form_response_group.id
LEFT JOIN as_invitee_form_response ON as_invitee_form_response.form_response_group_id = form_response_group.id
),
assessments_with_stage_data AS
(
SELECT as_form_response_group.id AS as_form_response_group_id,
assessment_path_resource.participant_full_name,
assessment_path_resource.path_id,
assessment_path_resource.path_name,
assessment_path_resource.cohort_id,
assessment_path_resource.cohort_name,
assessment_path_resource.program_id,
assessment_path_resource.program_name,
assessment_path_resource.tenant_id,
assessment_path_resource.xapi_activity_id,
assessment_path_resource.registration_id,
CASE
WHEN as_form_response_group.id IS NULL THEN 'Not Started'::text
WHEN as_form_response_group.id IS NOT NULL AND NOT as_form_response_group.is_self_assessment_completed THEN 'Self-assessment pending'::text
WHEN as_form_response_group.id IS NOT NULL AND as_form_response_group.is_self_assessment_completed AND NOT as_form_response_group.is_released AND NOT as_form_response_group.is_releasable THEN 'Feedback pending'::text
WHEN as_form_response_group.id IS NOT NULL AND as_form_response_group.is_self_assessment_completed AND as_form_response_group.is_releasable AND NOT as_form_response_group.is_released THEN 'Eligible for release'::text
WHEN as_form_response_group.id IS NOT NULL AND as_form_response_group.is_released THEN 'Released'::text
ELSE 'UNKNOWN STAGE'::text
END AS stage_label,
CASE
WHEN as_form_response_group.id IS NULL THEN assessment_path_resource.created_at::timestamp with time zone
WHEN as_form_response_group.id IS NOT NULL AND NOT as_form_response_group.is_self_assessment_completed THEN as_form_response_group.created_date
WHEN as_form_response_group.id IS NOT NULL AND as_form_response_group.is_self_assessment_completed AND NOT as_form_response_group.is_released AND NOT as_form_response_group.is_releasable THEN as_form_response_group.self_response_updated_date
WHEN as_form_response_group.id IS NOT NULL AND as_form_response_group.is_self_assessment_completed AND as_form_response_group.is_releasable AND NOT as_form_response_group.is_released THEN
CASE
WHEN as_form_response_group.minimum_feedback_requests = 0 OR as_form_response_group.minimum_release_timeline = 0 THEN as_form_response_group.self_response_updated_date
WHEN COALESCE(as_form_response_group.minimum_feedback_requests, 0) > 0 AND COALESCE(as_form_response_group.minimum_release_timeline, 21) > 0 THEN
CASE
WHEN as_form_response_group.feedback_requests_completed >= as_form_response_group.minimum_feedback_requests AND as_form_response_group.days_passed_since_first_fbr < as_form_response_group.minimum_release_timeline THEN as_form_response_group.minimum_feedback_requests_completed_threshold_met_date
WHEN as_form_response_group.feedback_requests_completed < as_form_response_group.minimum_feedback_requests AND as_form_response_group.days_passed_since_first_fbr >= as_form_response_group.minimum_release_timeline THEN as_form_response_group.first_fbr_created_date + make_interval(days => as_form_response_group.minimum_release_timeline)
ELSE
CASE
WHEN as_form_response_group.first_fbr_created_date < as_form_response_group.minimum_feedback_requests_completed_threshold_met_date THEN as_form_response_group.first_fbr_created_date + make_interval(days => as_form_response_group.minimum_release_timeline)
ELSE as_form_response_group.minimum_feedback_requests_completed_threshold_met_date
END
END
WHEN COALESCE(as_form_response_group.minimum_feedback_requests, 0) > 0 THEN as_form_response_group.minimum_feedback_requests_completed_threshold_met_date
WHEN COALESCE(as_form_response_group.minimum_release_timeline, 21) > 0 THEN as_form_response_group.first_fbr_created_date + make_interval(days => as_form_response_group.minimum_release_timeline)
ELSE NULL::timestamp with time zone
END
WHEN as_form_response_group.id IS NOT NULL AND as_form_response_group.is_released THEN as_form_response_group.released_date
ELSE NULL::timestamp with time zone
END AS stage_started_at,
as_form_response_group.released_date,
as_form_response_group.is_releasable_reason,
as_form_response_group.self_response_updated_date,
as_form_response_group.days_passed_since_first_fbr,
as_form_response_group.minimum_release_timeline,
as_form_response_group.first_fbr_created_date,
as_form_response_group.minimum_feedback_requests,
as_form_response_group.total_fbrs_completed,
as_form_response_group.minimum_feedback_requests_completed_threshold_met_date
FROM assessment_path_resource
LEFT JOIN as_form_response_group ON as_form_response_group.registration_id = assessment_path_resource.registration_id
WHERE assessment_path_resource.duplicate_registration_id_row_number = 1
)
SELECT assessments_with_stage_data.as_form_response_group_id,
assessments_with_stage_data.participant_full_name,
assessments_with_stage_data.path_id,
assessments_with_stage_data.path_name,
assessments_with_stage_data.cohort_id,
assessments_with_stage_data.cohort_name,
assessments_with_stage_data.program_id,
assessments_with_stage_data.program_name,
assessments_with_stage_data.tenant_id,
assessments_with_stage_data.xapi_activity_id,
assessments_with_stage_data.registration_id,
assessments_with_stage_data.stage_label,
assessments_with_stage_data.stage_started_at,
assessments_with_stage_data.released_date,
assessments_with_stage_data.is_releasable_reason,
assessments_with_stage_data.self_response_updated_date,
assessments_with_stage_data.days_passed_since_first_fbr,
assessments_with_stage_data.minimum_release_timeline,
assessments_with_stage_data.first_fbr_created_date,
assessments_with_stage_data.minimum_feedback_requests,
assessments_with_stage_data.total_fbrs_completed,
assessments_with_stage_data.minimum_feedback_requests_completed_threshold_met_date,
array_position(ARRAY['Not Started'::text, 'Self-assessment pending'::text, 'Feedback pending'::text, 'Eligible for release'::text, 'Released'::text], assessments_with_stage_data.stage_label) AS stage_order_index
FROM assessments_with_stage_data
-- ORDER BY (array_position(ARRAY['Not Started'::text, 'Self-assessment pending'::text, 'Feedback pending'::text, 'Eligible for release'::text, 'Released'::text], assessments_with_stage_data.stage_label)) desc
order by stage_order_index desc
Please find the attached execution plan for this .
its currently taking 3 min 57 seconds due to that my API timeout.is there any way to increase the query cost and get it with in 59 seconds
https://explain.depesz.com/s/ly8c
explain plan without verbose
https://explain.depesz.com/s/KBto
2
Answers
I solved this performance issue by changing the query .Now its taking 26seconds .
Updated query is below
Updated explain plan
https://explain.depesz.com/s/hbhI#stats
I cannot give a complete answer (your query is complicated), but here is a list of things that could be improved:
You have a couple of foreign table scans in the query.
Statistics for foreign tables are not collected automatically. You can either explicitly
ANALYZE
the foreign tables, or you can set theuse_remote_estimate
option on the foreign server (assuming it is postgres_fdw), so that PostgreSQL asks the remote server for estimates.The foreign scan on
assessment_service.form_response
cannot push down the condition onform_response_status_type
andsubmitter_type
, because they are of a user-defined data type. Don’t use such data types for better performance.Your foreign scans are really slooooow. Investigate the cause. One idea to mitigate that would be not to join a couple of foreign tables locally. Instead, you might be able to define a view on the remote server that joins these tables and declare a single foreign table for that view.
The estimate on
public.learning_record_store_user_activities
is very bad. Perhaps anANALYZE
on that table can help.The estimate on
public.group_path_resources
is horrible. The reason is probably the condition(group_path_resources.context_data ->> 'xapi_activity_id') LIKE 'https://assessment.%360%'
. Extended statistics on the expression (available from v14 on) might help: