活动记录'不是空的'查询很慢

时间:2017-10-25 20:16:36

标签: ruby-on-rails activerecord

预期行为:

预期返回数据不会更快

实际行为:

我尝试运行此查询,但花费这么多时间非常慢:

result.distinct(true)
.where(obras: { id: construction_site_id })
.where.not(dados_inspecao: { quantidade_erros: 0} )
.where.not(dados_inspecao: { quantidade_erros: nil } )

如果我没有'不是空的'声明很快

我在pgAdmin 3上尝试此查询并且非常快(非空):

SELECT DISTINCT servicos.id, servicos.titulo as service_title, sum(quantidade_erros) as qtd FROM "dados_inspecao" INNER JOIN "fvs_metodos_verificados" ON "fvs_metodos_verificados"."id" = "dados_inspecao"."fvs_metodos_verificados_id" INNER JOIN "fvs_preenchimento" ON "fvs_preenchimento"."id" = "fvs_metodos_verificados"."fvs_preenchimento_id" INNER JOIN "fvs" ON "fvs"."id" = "fvs_preenchimento"."fvs_id" AND "fvs"."empresas_id" = 44093 INNER JOIN "atividade" ON "atividade"."id" = "fvs_preenchimento"."atividade_id" AND "atividade"."empresas_id" = 44093 INNER JOIN "servicos" ON "servicos"."id" = "atividade"."servicos_id" AND "servicos"."empresas_id" = 44093 INNER JOIN "local" ON "local"."id" = "atividade"."local_id" INNER JOIN "obras" ON "obras"."id" = "local"."obras_id" AND "obras"."empresas_id" = 44093 WHERE "obras"."id" = 96520 AND ("dados_inspecao"."quantidade_erros" != 0) AND ("dados_inspecao"."quantidade_erros" IS NULL) GROUP BY servicos.id

我已经尝试过了,但没有成功:

ActiveRecord::Base.connection.execute(%Q{ SELECT DISTINCT servicos.id, servicos.titulo as service_title, sum(quantidade_erros) as qtd FROM "dados_inspecao" INNER JOIN "fvs_metodos_verificados" ON "fvs_metodos_verificados"."id" = "dados_inspecao"."fvs_metodos_verificados_id" INNER JOIN "fvs_preenchimento" ON "fvs_preenchimento"."id" = "fvs_metodos_verificados"."fvs_preenchimento_id" INNER JOIN "fvs" ON "fvs"."id" = "fvs_preenchimento"."fvs_id" AND "fvs"."empresas_id" = 44093 INNER JOIN "atividade" ON "atividade"."id" = "fvs_preenchimento"."atividade_id" AND "atividade"."empresas_id" = 44093 INNER JOIN "servicos" ON "servicos"."id" = "atividade"."servicos_id" AND "servicos"."empresas_id" = 44093 INNER JOIN "local" ON "local"."id" = "atividade"."local_id" INNER JOIN "obras" ON "obras"."id" = "local"."obras_id" AND "obras"."empresas_id" = 44093 WHERE "obras"."id" = 96520 AND ("dados_inspecao"."quantidade_erros" != 0) AND ("dados_inspecao"."quantidade_erros" IS NULL) GROUP BY servicos.id })

系统配置:

Rails版本:4.2.0

Ruby版本:2.2.5

Postgres:9.5

[编辑]

解释postgres上的分析输出:

"HashAggregate  (cost=7881.74..7881.75 rows=1 width=47) (actual time=66.832..66.832 rows=4 loops=1)"
"  Group Key: servicos.id, servicos.titulo, sum(dados_inspecao.quantidade_erros)"
"  ->  HashAggregate  (cost=7881.73..7881.74 rows=1 width=47) (actual time=66.825..66.825 rows=4 loops=1)"
"        Group Key: servicos.id"
"        ->  Nested Loop  (cost=4240.53..7881.72 rows=1 width=47) (actual time=26.741..66.766 rows=55 loops=1)"
"              ->  Nested Loop  (cost=4240.38..7873.55 rows=1 width=51) (actual time=26.709..66.545 rows=55 loops=1)"
"                    ->  Nested Loop  (cost=4240.10..7872.44 rows=1 width=51) (actual time=26.694..66.311 rows=55 loops=1)"
"                          ->  Nested Loop  (cost=4239.82..7871.98 rows=1 width=12) (actual time=26.687..66.087 rows=55 loops=1)"
"                                ->  Hash Join  (cost=4239.40..7811.45 rows=47 width=8) (actual time=26.669..65.739 rows=55 loops=1)"
"                                      Hash Cond: (fvs_metodos_verificados.id = dados_inspecao.fvs_metodos_verificados_id)"
"                                      ->  Hash Join  (cost=793.60..4342.23 rows=1836 width=8) (actual time=8.161..47.684 rows=10202 loops=1)"
"                                            Hash Cond: (fvs_metodos_verificados.fvs_preenchimento_id = fvs_preenchimento.id)"
"                                            ->  Seq Scan on fvs_metodos_verificados  (cost=0.00..2980.10 rows=146710 width=8) (actual time=0.004..18.224 rows=146710 loops=1)"
"                                            ->  Hash  (cost=791.63..791.63 rows=158 width=8) (actual time=5.505..5.505 rows=623 loops=1)"
"                                                  Buckets: 1024  Batches: 1  Memory Usage: 33kB"
"                                                  ->  Hash Join  (cost=74.41..791.63 rows=158 width=8) (actual time=0.514..5.375 rows=623 loops=1)"
"                                                        Hash Cond: (fvs_preenchimento.fvs_id = fvs.id)"
"                                                        ->  Seq Scan on fvs_preenchimento  (cost=0.00..668.28 rows=12628 width=12) (actual time=0.003..3.468 rows=12628 loops=1)"
"                                                        ->  Hash  (cost=73.95..73.95 rows=37 width=4) (actual time=0.477..0.477 rows=37 loops=1)"
"                                                              Buckets: 1024  Batches: 1  Memory Usage: 10kB"
"                                                              ->  Seq Scan on fvs  (cost=0.00..73.95 rows=37 width=4) (actual time=0.038..0.467 rows=37 loops=1)"
"                                                                    Filter: (empresas_id = 44093)"
"                                                                    Rows Removed by Filter: 2919"
"                                      ->  Hash  (cost=3398.39..3398.39 rows=3793 width=8) (actual time=15.435..15.435 rows=3901 loops=1)"
"                                            Buckets: 4096  Batches: 1  Memory Usage: 185kB"
"                                            ->  Seq Scan on dados_inspecao  (cost=0.00..3398.39 rows=3793 width=8) (actual time=0.005..14.312 rows=3901 loops=1)"
"                                                  Filter: ((quantidade_erros IS NOT NULL) AND (quantidade_erros <> 0))"
"                                                  Rows Removed by Filter: 121170"
"                                ->  Index Scan using atividade_pkey on atividade  (cost=0.41..1.28 rows=1 width=12) (actual time=0.005..0.005 rows=1 loops=55)"
"                                      Index Cond: (id = fvs_preenchimento.atividade_id)"
"                                      Filter: (empresas_id = 44093)"
"                          ->  Index Scan using servicos_pkey on servicos  (cost=0.28..0.46 rows=1 width=43) (actual time=0.003..0.003 rows=1 loops=55)"
"                                Index Cond: (id = atividade.servicos_id)"
"                                Filter: (empresas_id = 44093)"
"                    ->  Index Scan using local_pkey on local  (cost=0.29..1.09 rows=1 width=8) (actual time=0.003..0.003 rows=1 loops=55)"
"                          Index Cond: (id = atividade.local_id)"
"                          Filter: (obras_id = 96520)"
"              ->  Index Scan using obras_pkey on obras  (cost=0.14..8.16 rows=1 width=4) (actual time=0.003..0.003 rows=1 loops=55)"
"                    Index Cond: (id = 96520)"
"                    Filter: (empresas_id = 44093)"
"Planning time: 3.469 ms"
"Execution time: 66.995 ms"

2 个答案:

答案 0 :(得分:1)

这可能会更快:

result.distinct(true)
  .where(obras: { id: construction_site_id })
  .where.not(dados_inspecao: { quantidade_erros: [0,nil]} )

您还可能希望确保所有id和外键都有索引。您可能还想将索引添加到quantidade_erros

我会为以下内容添加索引:

  • fvs_metodos_verificados.fvs_preenchimento_id
  • fvs_preenchimento.fvs_id
  • fvs.empreses.id
  • dados_inspecao.quantidade_errors

您应该可以通过迁移执行此操作。运行:

rails g migration AddIndeciesToForeignKeys

然后修改生成的迁移文件:

class AddIndeciesToForeignKeys < ActiveRecord::Migration
  add_index: :fvs_metodos_verificados, :fvs_preenchimento_id
  add_index: :fvs_preenchimento, :fvs_id
  add_index: :fvs, :empreses.id 
  add_index: :dados_inspecao, :quantidade_errors
end

然后bundle exec rake db:migrate你应该好好去。

通常,只要您在postgres Seq Scan中看到EXPLAIN ANALYZE,就意味着数据库正在扫描而没有索引。顺序(非索引)扫描比索引扫描慢得多。向正在扫描的列添加索引通常会加快速度。

完成上述更改后,如果再次运行EXPLAIN ANALYZE,我认为您会发现所有扫描都是索引扫描,查询运行速度会快得多。

最后,通过将index: true添加到创建列的迁移行,可以将索引添加到新列。所有foreigns键(即引用另一个表的id的列)都应该有一个索引。

答案 1 :(得分:1)

我今天用这个慢查询来解决问题,问题是作用域查询这次验证了很多次“empresas_id”= 44093 ,我删除了它并且效果很好。感谢

  • INNER JOIN "fvs" ON "fvs"."id" = "fvs_preenchimento"."fvs_id" AND "fvs"."empresas_id" = 44093

  • INNER JOIN "atividade" ON "atividade"."id" = "fvs_preenchimento"."atividade_id" AND "atividade"."empresas_id" = 44093

  • INNER JOIN "servicos" ON "servicos"."id" = "atividade"."servicos_id" AND "servicos"."empresas_id" = 44093

  • INNER JOIN "obras" ON "obras"."id" = "local"."obras_id" AND "obras"."empresas_id" = 44093