#ruby-on-rails #postgresql #indexing #max #rails-activerecord
#ruby-on-rails #postgresql #индексирование #макс #rails-activerecord
Вопрос:
У меня есть 3 основные модели, потенциально могут быть тысячи компонентов и тестовых записей.
class Component
has_many :test_records, as: :testable
end
class TestRecord
belongs_to :testable, polymorphic: true
belongs_to :test_form
end
class TestForm
has_many :test_records
end
Тестовые записи создаются после тестирования проверяемого элемента, и они приводят к пропуску или сбою, сохраняемому как логическое значение
test_record.passed
То, что я пытаюсь сделать, это извлечь самую последнюю тестовую запись для каждого компонента и определить, прошла она или нет. Сначала я проверяю, был ли он протестирован в приведенной ниже области:
scope :tested, ->(test_form_id) {
where("test_records.created_at > ?", 45.days.ago)
.order(created_at: :desc)
.distinct
.joins(:test_records)
.where("test_records.test_form_id = ?", test_form_id)
.active
}
и затем я передаю его:
scope :passed, ->(test_form_id, type) {
tested(test_form_id)
.includes(:test_records)
.where('test_records.test_form_id = ?', test_form_id)
.where("test_records.created_at = (
SELECT MAX(test_records.created_at)
FROM test_records
WHERE test_records.testable_id = #{type}.id)")
.where("tested_records.passed = none")
}
Выполнение этого запроса в рабочей среде занимает 20 секунд.
Я пробовал следующее:
scope :passed, ->(test_form_id, type) {
tested(test_form_id)
.distinct('ON (test_records.testable_id)')
.order('test_records.created_at DESC')
.where('test_records.test_form_id = ?', test_form_id)
.where('test_records.passed = ?', true)
.includes(:test_records)
}
но он не дает правильного результата, потому что он отфильтровывает переданную тестовую запись == false перед вызовом distinct.
TLDR: у меня есть компоненты, которые были протестированы, я хочу выбрать последнюю test_record, которая была создана для определенной test_form, если test_record.passed == true; В настоящее время запрос занимает в 40 раз больше времени, чем хотелось бы, и я был бы признателен за любую помощь в очистке моего активного запроса записи
Спасибо!
редактировать: вот PG, ОБЪЯСНЯЮЩИЙ результаты анализа:
Unique (cost=1155.71..1155.76 rows=1 width=150) (actual time=8504.313..8504.326 rows=2 loops=1)
-> Sort (cost=1155.71..1155.72 rows=1 width=150) (actual time=8504.312..8504.317 rows=2 loops=1)
Sort Key: components.created_at DESC, components.id, components.barcode, components.description, components.make, components.model, components.system_id, components.updated_at, components.active, components.component_type, components.abbreviation, components.address, test_records.id, test_records.user_id, test_records.passed, test_records.created_at, test_records.updated_at, test_records.manufacturer_test_form_id
Sort Method: quicksort Memory: 25kB
-> Hash Join (cost=798.86..1155.70 rows=1 width=150) (actual time=8502.399..8504.226 rows=2 loops=1)
Hash Cond: ((test_records.testable_id = components.id) AND (test_records.created_at = (SubPlan 1)))
-> Hash Join (cost=42.09..243.44 rows=9 width=70) (actual time=1.000..1.010 rows=4 loops=1)
Hash Cond: (test_records.testable_id = components_systems.component_id)
-> Bitmap Heap Scan on test_records (cost=19.85..218.41 rows=718 width=62) (actual time=0.409..0.718 rows=418 loops=1)
Recheck Cond: (test_form_id = 14)
Filter: (passed AND (created_at > '2020-11-03 02:49:31.234492'::timestamp without time zone) AND ((testable_type)::text = 'Component'::text))
Rows Removed by Filter: 1106
Heap Blocks: exact=92
-> Bitmap Index Scan on index_test_records_on_test_form_id (cost=0.00..19.67 rows=1518 width=0) (actual time=0.072..0.073 rows=1525 loops=1)
Index Cond: (test_form_id = 14)
-> Hash (cost=16.43..16.43 rows=465 width=8) (actual time=0.214..0.215 rows=478 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 27kB
-> Index Only Scan using index_components_systems_on_system_id_and_component_id on components_systems (cost=0.29..16.43 rows=465 width=8) (actual time=0.039..0.119 rows=478 loops=1)
Index Cond: (system_id = 2747)
Heap Fetches: 0
-> Hash (cost=704.23..704.23 rows=3503 width=88) (actual time=8499.537..8499.538 rows=1509 loops=1)
Buckets: 4096 Batches: 1 Memory Usage: 246kB
-> Bitmap Heap Scan on components (cost=43.81..704.23 rows=3503 width=88) (actual time=0.197..3.433 rows=3478 loops=1)
Recheck Cond: (component_profile_id = 13)
Filter: (active AND active)
Rows Removed by Filter: 22
Heap Blocks: exact=235
-> Bitmap Index Scan on index_components_on_component_profile_id (cost=0.00..42.94 rows=3553 width=0) (actual time=0.165..0.165 rows=3695 loops=1)
Index Cond: (component_profile_id = 13)
SubPlan 1
-> Aggregate (cost=345.43..345.44 rows=1 width=8) (actual time=2.440..2.440 rows=1 loops=3480)
-> Seq Scan on test_records test_records_1 (cost=0.00..345.43 rows=1 width=8) (actual time=1.775..2.437 rows=0 loops=3480)
Filter: (testable_id = components.id)
Rows Removed by Filter: 13897
Planning Time: 0.794 ms
Execution Time: 8504.505 ms
Ответ №1:
Ну, решение было действительно более простым, чем я думал. Я добавил миграцию ниже:
add_index :test_records, [:testable_id, :testable_type]
После этого запрос упал до 1.010 мс