Skip to content

Commit 2e3112a

Browse files
authored
Merge pull request #4344 from nhsuk/optimise-programmes-query
Optimise a number of queries to improve performance
2 parents 421584f + 705111d commit 2e3112a

File tree

13 files changed

+77
-59
lines changed

13 files changed

+77
-59
lines changed
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
# frozen_string_literal: true
22

33
class AppProgrammeStatsComponent < ViewComponent::Base
4-
def initialize(programme, academic_year:, patients:)
4+
def initialize(programme, academic_year:, patient_ids:)
55
super
66

77
@programme = programme
88
@academic_year = academic_year
9-
@patients = patients
9+
@patient_ids = patient_ids
1010
end
1111

12-
delegate :count, to: :patients, prefix: true
12+
def patients_count = patient_ids.length
1313

1414
def vaccinations_count
1515
helpers
1616
.policy_scope(VaccinationRecord)
1717
.administered
18-
.where(patient: patients, programme:)
18+
.where(patient_id: patient_ids, programme:)
1919
.count
2020
end
2121

@@ -25,11 +25,11 @@ def consent_notifications_count
2525
.has_programme(programme)
2626
.joins(:session)
2727
.where(session: { academic_year: })
28-
.where(patient: patients)
28+
.where(patient_id: patient_ids)
2929
.count
3030
end
3131

3232
private
3333

34-
attr_reader :programme, :academic_year, :patients
34+
attr_reader :programme, :academic_year, :patient_ids
3535
end

app/controllers/programmes/base_controller.rb

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,23 @@ def set_academic_year
2020
end
2121
end
2222

23-
def patients
24-
# We do this instead of using `team.patients` as that has a `distinct` on
25-
# it which means we cannot apply ordering or grouping.
26-
@patients ||=
27-
Patient
28-
.where(id: current_team.patient_sessions.select(:patient_id).distinct)
29-
.appear_in_programmes([@programme], academic_year: @academic_year)
23+
def patient_ids
24+
@patient_ids ||=
25+
PatientSession
26+
.distinct
27+
.joins(:patient, :session)
28+
.where(session_id: session_ids)
29+
.appear_in_programmes([@programme])
3030
.not_archived(team: current_team)
31+
.pluck(:patient_id)
32+
end
33+
34+
def session_ids
35+
@session_ids ||=
36+
current_team
37+
.sessions
38+
.where(academic_year: @academic_year)
39+
.has_programmes([@programme])
40+
.pluck(:id)
3141
end
3242
end

app/controllers/programmes/overview_controller.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
class Programmes::OverviewController < Programmes::BaseController
44
before_action :set_consents
5-
before_action :set_patients
5+
before_action :set_patient_ids
66
before_action :set_patient_count_by_year_group
77

88
def show
@@ -13,21 +13,21 @@ def show
1313
def set_consents
1414
@consents =
1515
policy_scope(Consent).where(
16-
patient: patients,
16+
patient_id: patient_ids,
1717
programme: @programme,
1818
academic_year: @academic_year
1919
)
2020
end
2121

22-
def set_patients
23-
@patients = patients
22+
def set_patient_ids
23+
@patient_ids = patient_ids
2424
end
2525

2626
def set_patient_count_by_year_group
2727
year_groups = current_team.programme_year_groups[@programme]
2828

2929
patient_count_by_birth_academic_year =
30-
patients.group(:birth_academic_year).count
30+
Patient.where(id: patient_ids).group(:birth_academic_year).count
3131

3232
@patient_count_by_year_group =
3333
year_groups.index_with do |year_group|

app/controllers/programmes/patients_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def index
99
@year_groups = current_team.programme_year_groups[@programme]
1010

1111
scope =
12-
patients.includes(
12+
Patient.where(id: patient_ids).includes(
1313
:consent_statuses,
1414
:triage_statuses,
1515
:vaccination_statuses,

app/helpers/imports_helper.rb

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@
33
module ImportsHelper
44
def import_issues_count
55
vaccination_records_with_issues =
6-
policy_scope(VaccinationRecord).with_pending_changes.distinct
6+
policy_scope(VaccinationRecord).with_pending_changes.distinct.pluck(
7+
:patient_id
8+
)
79

8-
patients_with_issues = policy_scope(Patient).with_pending_changes
10+
patients_with_issues = policy_scope(Patient).with_pending_changes.pluck(:id)
911

10-
unique_import_issues =
11-
(vaccination_records_with_issues + patients_with_issues).uniq do |record|
12-
record.is_a?(VaccinationRecord) ? record.patient_id : record.id
13-
end
14-
15-
unique_import_issues.count
12+
(vaccination_records_with_issues + patients_with_issues).uniq.length
1613
end
1714
end

app/models/patient_session.rb

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,26 +93,28 @@ class PatientSession < ApplicationRecord
9393

9494
scope :appear_in_programmes,
9595
->(programmes) do
96+
# Are any of the programmes administered in the session?
97+
programme_in_session =
98+
SessionProgramme
99+
.where(programme: programmes)
100+
.where("session_programmes.session_id = sessions.id")
101+
.arel
102+
.exists
103+
96104
# Is the patient eligible for any of those programmes by year group?
97-
location_programme_year_groups =
105+
patient_in_administered_year_groups =
98106
LocationProgrammeYearGroup
99-
.where("programme_id = session_programmes.programme_id")
107+
.where(programme: programmes)
100108
.where("location_id = sessions.location_id")
101109
.where(
102110
"year_group = sessions.academic_year " \
103111
"- patients.birth_academic_year " \
104112
"- #{Integer::AGE_CHILDREN_START_SCHOOL}"
105113
)
106-
107-
# Are any of the programmes administered in the session?
108-
where(
109-
SessionProgramme
110-
.where(programme: programmes)
111-
.where("session_programmes.session_id = sessions.id")
112-
.where(location_programme_year_groups.arel.exists)
113114
.arel
114115
.exists
115-
)
116+
117+
where(programme_in_session).where(patient_in_administered_year_groups)
116118
end
117119

118120
scope :search_by_name,

app/models/session.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
#
1919
# Indexes
2020
#
21-
# index_sessions_on_location_id (location_id)
22-
# index_sessions_on_team_id_and_location_id (team_id,location_id)
21+
# index_sessions_on_location_id (location_id)
22+
# index_sessions_on_team_id_and_academic_year (team_id,academic_year)
23+
# index_sessions_on_team_id_and_location_id (team_id,location_id)
2324
#
2425
# Foreign Keys
2526
#
@@ -69,7 +70,6 @@ class Session < ApplicationRecord
6970
SessionProgramme
7071
.select("COUNT(session_programmes.id)")
7172
.where("sessions.id = session_programmes.session_id")
72-
.joins(:programme)
7373
.where(programme: programmes),
7474
programmes.count
7575
)

app/views/programmes/overview/show.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
<%= govuk_button_to "Download vaccination report", programme_reports_path(@programme, @academic_year), secondary: true, class: "nhsuk-u-margin-bottom-5" %>
1515

16-
<%= render AppProgrammeStatsComponent.new(@programme, academic_year: @academic_year, patients: @patients) %>
16+
<%= render AppProgrammeStatsComponent.new(@programme, academic_year: @academic_year, patient_ids: @patient_ids) %>
1717

1818
<h2 class="nhsuk-heading-m">Cohorts</h2>
1919

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
class AddIndexOnSessionTeamAndAcademicYear < ActiveRecord::Migration[8.0]
4+
disable_ddl_transaction!
5+
6+
def change
7+
add_index :sessions, %i[team_id academic_year], algorithm: :concurrently
8+
end
9+
end

db/schema.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[8.0].define(version: 2025_08_14_154032) do
13+
ActiveRecord::Schema[8.0].define(version: 2025_08_20_085332) do
1414
# These are extensions that must be enabled in order to support this database
1515
enable_extension "pg_catalog.plpgsql"
1616
enable_extension "pg_trgm"
@@ -772,6 +772,7 @@
772772
t.date "send_invitations_at"
773773
t.boolean "requires_registration", default: true, null: false
774774
t.index ["location_id"], name: "index_sessions_on_location_id"
775+
t.index ["team_id", "academic_year"], name: "index_sessions_on_team_id_and_academic_year"
775776
t.index ["team_id", "location_id"], name: "index_sessions_on_team_id_and_location_id"
776777
end
777778

0 commit comments

Comments
 (0)