【问题标题】:Optimize PostgreSQL VIEW优化 PostgreSQL 视图
【发布时间】:2020-12-22 01:12:40
【问题描述】:

我有一段代码在各个方面都运行良好,但只有一个。执行时间太长。我的怀疑是执行时间过长是由于过度使用 JOIN(准确地说是 11 个)造成的。 这是我想要做的:

设置 我有 3 张桌子:

  1. v_na_measurement_status
id.           start_measurement.                  stop_measurement.

0       2020-09-01 22:19:48.480668+00:00    2020-09-01 22:29:29.713952+00:00
1       2020-09-01 22:17:10.392089+00:00    2020-09-01 22:18:08.139668+00:00
2       2020-09-01 22:12:49.795404+00:00    2020-09-01 22:15:14.564597+00:00 
...
  1. sensor_double_precision
id.         sensor_name.                  timestamps.                 value_cal
40548   "curved_mirror_steps"   "2020-09-01 22:29:23.526468+00"     432131  
40547   "na_average_enable".    "2020-09-01 22:29:23.410416+00"     1   
40546   "na_averages"           "2020-09-01 22:29:23.295404+00".    16  
40545   "na_power"              "2020-09-01 22:29:23.174255+00"     -5  
40544   "na_stop_freq"          "2020-09-01 22:29:23.05868+00"      18000000000 
40543   "na_start_freq"         "2020-09-01 22:29:22.944205+00"     15000000000 
...
  1. sensor_double_precision_arr
id.        sensor_name.                        timestamp                     value_cal
3831    "na_s11_iq_data_trace2"     "2020-09-01 22:29:27.456345+00".   [array with ~2000 points]
3830    "na_s21_iq_data"            "2020-09-01 22:29:27.389617+00".   [array with ~2000 points]
3829    "na_s11_iq_data_trace2"     "2020-09-01 22:29:20.543466+00".   [array with ~2000 points]
3828    "na_s21_iq_data"            "2020-09-01 22:29:20.443416+00".   [array with ~2000 points]

目标 使用这 3 个表,我想创建一个名为 v_na_log 的视图,如下所示:

  start_measurement                  stop_measurement            curved_mirror_steps.  na_averages   na_start_freq      na_stop_freq.     na_s21_iq_data.               na_s11_iq_data   
"2020-09-01 22:29:22.913366+00"   "2020-09-01 22:29:27.478287+00"    432131                16         15000000000.      18000000000      [array with ~2000 points].     [array with ~2000 points]
...
...

基本上,我想转置 sensor_double_precision_arr 和 sensor_double_precision 以使 sensor_name 列中的行本身成为列。

一个解决方案 这是我用来实现此目的的长代码:

DROP VIEW IF EXISTS v_na_log;
      CREATE VIEW v_na_log AS
        SELECT
          final_view.id, vnms.start_measurement,
          vnms.stop_measurement, final_view.curved_mirror_steps,
          final_view.na_averages, final_view.na_start_freq,
          final_view.na_stop_freq,iq_data.freq_resolution,
          iq_data.na_s21_iq_data, iq_data.na_s11_iq_data
        FROM crosstab(
          'WITH sorted_data AS (WITH current_data AS
          (SELECT DISTINCT ON (id,sensor_name)
             id, sensor_name, value_cal, timestamp
             FROM sensor_double_precision
             WHERE sensor_name in (''na_start_freq'', ''na_stop_freq'',
                                  ''na_averages'',''curved_mirror_steps'')
          ORDER BY id, sensor_name, timestamp ASC)
        SELECT
          m.id,
          s.sensor_name,
          s.value_cal
          FROM v_na_measurement_status m
        INNER JOIN current_data s ON s.timestamp
          BETWEEN m.start_measurement AND m.stop_measurement),
        log_ids AS (SELECT distinct id FROM sorted_data),
        sensor_names AS (SELECT distinct sensor_name FROM sorted_data)
        SELECT log_ids.id, sensor_names.sensor_name, sorted_data.value_cal
          FROM log_ids CROSS JOIN sensor_names
        LEFT JOIN sorted_data on (log_ids.id= sorted_data.id and
                                  sensor_names.sensor_name=sorted_data.sensor_name)')
        final_view(id bigint, curved_mirror_steps double precision,
                    na_averages double precision, na_start_freq double precision,
                    na_stop_freq double precision)
        LEFT JOIN v_na_measurement_status vnms on vnms.id = final_view.id
        LEFT JOIN
        (SELECT final_view.id, vnms.start_measurement, vnms.stop_measurement,
          array_length(final_view.na_s21_iq_data, 1) AS freq_resolution,
          final_view.na_s11_iq_data, final_view.na_s21_iq_data
          FROM crosstab(
            'WITH sorted_data AS (with current_data AS
            (SELECT DISTINCT ON (id,sensor_name)
              id, sensor_name, value_cal, timestamp
              FROM sensor_double_precision_arr
                WHERE  sensor_name LIKE ''%na_s21_iq_data%'' OR sensor_name LIKE ''%na_s11_iq_data%''
              ORDER BY id, sensor_name, timestamp ASC)
        SELECT
          m.id,
          s.sensor_name,
          s.value_cal
          FROM v_na_measurement_status m
        INNER JOIN current_data s ON s.timestamp
          BETWEEN m.start_measurement AND m.stop_measurement),
        log_ids AS (SELECT distinct id FROM sorted_data),
        sensor_names AS (SELECT distinct sensor_name FROM sorted_data)
        SELECT log_ids.id, sensor_names.sensor_name, sorted_data.value_cal
          FROM log_ids CROSS JOIN sensor_names
        LEFT JOIN sorted_data ON (log_ids.id= sorted_data.id AND sensor_names.sensor_name = sorted_data.sensor_name)')
        final_view(id bigint, na_s11_iq_data double precision[], na_s21_iq_data double precision[])
        LEFT JOIN v_na_measurement_status vnms ON vnms.id = final_view.id) iq_data ON iq_data.id = final_view.id
        ORDER BY vnms.start_measurement ASC;

问题 我使用这段代码来做到这一点。它工作得很好,到目前为止,即使在边缘情况下我也没有遇到问题。但是,运行需要很长的时间。 例如,如果我运行: SELECT * FROM v_na_log LIMIT 10 执行大约需要 13 秒。如果我删除 LIMIT 子句,则需要更长的时间。我通常要处理超过 10 行,所以花费的时间越长,我分析数据就越糟糕。 就像我说的,我认为这与使用这么多的 JOIN 有关。但是,我没有看到更好的方法来做到这一点。我想知道是否有更好的解决方案,因为我怀疑随着表变大,这个问题会越来越严重。

我已经发布了其他类似任务的简化版本,并收到了宝贵的建议。我已将这些建议纳入我的代码中。这是我遇到的与此相关的问题的链接: Crosstab using data from 2 different tables

也欢迎任何关于更好地组织数据库本身的表的建议。

【问题讨论】:

  • 交叉表sensor_name 列是否是您预先知道的少数值?如果不是,你为什么需要一个包含数百或数千列的宽表?您是否使用应用层(即 Python、R)进行分析?
  • 是的,他们是我事先知道的少数sensor_names。这个 VIEW 有两个目的。主要原因是我想在一个名为 Grafana 的网络应用程序中创建绘图,而这个 VIEW 将使这成为可能。次要原因(不那么重要)是在进行数据分析时使用 python 对数据库的调用较少
  • 无法回答。尝试组成视图的部分,看看减速发生在哪里。
  • 你好,我已经试过了。可能应该提到它。对不起!每个单独的过程都非常缓慢。第一个交叉表需要 2.5 秒,第二个交叉表需要 9-10 秒(都带有limit 10 子句)。我不确定为什么第二个 Crosstab 需要更长的时间,因为它做完全相同的事情但使用不同的表。我的猜测是大型数组可能与它有关,但我不知道为什么。

标签: sql postgresql query-optimization sql-view sql-tuning


【解决方案1】:

根据发布的数据和所需的输出以及预先知道给定的透视值,考虑运行条件聚合或使用 Postgres 的选择性聚合与FILTER()

SELECT m.start_measurement
       , m.stop_measurement
       , MAX(p.value_cal) FILTER(WHERE p.sensor_name = 'curved_mirror_steps') AS curved_mirror_steps
       , ...
       , MAX(pa.value_cal) FILTER(WHERE pa.sensor_name = 'na_s11_iq_data') AS na_s11_iq_data
      
FROM v_na_measurement_status m 
LEFT JOIN sensor_double_precision p 
  ON p.timestamp BETWEEN m.start_measurement 
                     AND m.stop_measurement
LEFT JOIN sensor_double_precision_arr pa
  ON pa.timestamp BETWEEN m.start_measurement 
                     AND m.stop_measurement
GROUP BY m.start_measurement
       , m.stop_measurement

【讨论】:

  • 嘿,谢谢!虽然这并没有解决查询需要很长时间才能运行的问题,但它绝对比我的代码干净得多。出于某种原因,执行时间稍长。
  • 有趣。我怀疑这是时间戳范围JOIN 匹配,并且可能是每行中具有~2k 点的数组值。删除数组表会发生什么?使用INNER JOIN?到处玩看看。另外,查看EXPLAIN 计划。考虑进一步研究像 DBA post 这样的时间戳索引。
猜你喜欢
  • 2012-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-22
  • 2011-07-31
  • 2012-06-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多