【问题标题】:Optimizing queries graphs Ruby on Rails优化查询图 Ruby on Rails
【发布时间】:2020-11-04 11:19:39
【问题描述】:

我正在使用仪表板来显示测量结果的图表。我很难使用 Rails (6) 高效/快速地处理这些数据。

它的 5 个图表(温度、湿度..)都显示来自相同时间跨度的数据,在每个图表中都是平均值、最小值和最大值。所以页面上总共有 15 个数据集。

每个测量(记录)都有五列(温度、湿度..),具有不同的测量读数。图表的测量数量可以在 +- 3000 条记录到 100.000 条以上之间变化。这些测量按月分组。然后计算最小、最大和平均数据集。所以这 15 个数据集都是基于相同的记录。

我的想法是从数据库中加载一次测量并对其进行计算。所以我不必为每个数据集查询数据。从数据库中查询时,我使用 Groupdate gem(https://github.com/ankane/groupdate) 按月快速分组测量。然后我可以每月平均,最小值和最大值。

不幸的是,我每次计算都会收到一个查询。这使页面加载缓慢..知道如何优化数据库的加载测量吗?之后我可以专注于重构代码。

def dashboard_device
    @device = Device.find(params[:id])
    @last_measurement = @device.measurements.last
    @weather_forecast_location = @device.sublocation.location
    start_date = Date.parse('1-1-2020')
    end_date = Date.parse('31-12-2020')

    selected_measurements = @device.measurements.select(:measurement_date, :moisture, :temperature, :dielectricity, :conductivity, :salinity).group_by_month(:measurement_date, range: start_date..end_date, format: '%B')

    @moisture = selected_measurements.average(:moisture).compact
    @moisture_max = selected_measurements.maximum(:moisture).compact
    @moisture_min = selected_measurements.minimum(:moisture).compact

    @temperature = selected_measurements.average(:temperature).compact
    @temperature_max = selected_measurements.maximum(:temperature).compact
    @temperature_min = selected_measurements.minimum(:temperature).compact

    @dielectricity = selected_measurements.average(:dielectricity).compact
    @dielectricity_max = selected_measurements.maximum(:dielectricity).compact
    @dielectricity_min = selected_measurements.minimum(:dielectricity).compact

    @conductivity = selected_measurements.average(:conductivity).compact
    @conductivity_max = selected_measurements.maximum(:conductivity).compact
    @conductivity_min = selected_measurements.minimum(:conductivity).compact

    @salinity = selected_measurements.average(:salinity).compact
    @salinity_max = selected_measurements.maximum(:salinity).compact
    @salinity_min = selected_measurements.minimum(:salinity).compact

    render 'dashboard'
  end

更新

使用 pluck 似乎有效,但不幸的是我没有得到数据对应的月份。

将控制器更改为

selected_measurements = @device.measurements.select(:measurement_date, :moisture, :temperature, :dielectricity, :conductivity, :salinity).group_by_month(:measurement_date, range: start_date..end_date, format: '%B')

    result = selected_measurements.select('MAX(moisture)', 'AVG(moisture)', 'MIN(moisture)')

似乎快到了,但它给出了一个错误:

PG::GroupingError: ERROR: column "measurements.measurement_date" must appear in the GROUP BY clause or be used in an aggregate function

【问题讨论】:

  • 您应该在数据库中选择聚合,而不是在 Ruby 中进行数字运算。您可以选择带有字符串.select('AVG(moisture) AS avg_moisure') 的聚合。或者使用 Arel Measurement.arel_table[:moisture].average.as('avg_moisure')
  • 如果您经常查询相同的聚合,请创建一个 SQL 视图或包含通过运行 cron 选项卡生成的每月聚合的表。
  • 这是group_by_month 来自某个宝石吗?
  • 是的,它是 Groupdate gem (github.com/ankane/groupdate)

标签: ruby-on-rails ruby postgresql


【解决方案1】:

您可以使用自定义选择语句来做到这一点

@moisture = selected_measurements.average(:moisture).compact
@moisture_max = selected_measurements.maximum(:moisture).compact
@moisture_min = selected_measurements.minimum(:moisture).compact

像这样……

result = selected_measurements.select("MAX(moisture) as max_moisture, AVG(moisture), MIN(moisture)")
result.first.max_moisture

或者,如果您只需要这些值,请大胆尝试

result = selected_measurements.pluck("MAX(moisture), AVG(moisture), MIN(moisture)")

【讨论】: