【问题标题】:Crosstab using data from 2 different tables使用来自 2 个不同表的数据的交叉表
【发布时间】:2020-12-09 05:27:21
【问题描述】:

我有 2 个表格 measure_timestamps 和 sensor_double_precision,格式如下:

id    start_time    stop_time
 1    2020-02-22    2020-02-24
 2    2020-02-25    2020-02-27 

id    sensor_name    value_cal    timestamp
 1    start_freq            15    2020-02-23
 2    stop_freq             18    2020-02-23
 3    start_freq            15    2020-02-26
 4    stop_freq             18    2020-02-26

我想要一个查看测量时间戳的视图,并且对于每个 start_time - stop_time 对,旋转(转置?)sensor_name 列,以便 start_freq 和 stop_freq 成为各自的列,并将相应的 value_cal 作为行。

所以我基本上希望 VIEW 看起来像这样:

id    start_freq    stop_freq    timestamp
 1            15           18    2020-02-23
 2            15           18    2020-02-26

请注意 VIEW 中与 id 1 关联的时间戳如何位于 measure_timestamps 表中 id 1 的 start_time 和 stop_time 之间。

这样做的合理方法是什么?我不想为每个单独的 sensor_name 创建一个 VIEW,因为我有比这更多的传感器,而且它看起来不是很健壮。以下是我被推荐的一种方法,但它似乎不起作用,因为我可能做错了什么。

SELECT * 
FROM crosstab('with current_data as (
        select distinct on (mt.id)
                mt.id, sdp.sensor_name, sdp.value_cal
            from measurement_timestamps mt, sensor_double_precision sdp
            order by mt.id desc
    ),
    ids as (
        select distinct id from current_data
    ),
    sensor_names as (
        select distinct sensor_name from current_data
    )
    select ids.id, sensor_names.sensor_name, current_data.value_cal
    from ids cross join sensor_names
    left join current_data on (ids.id=current_data.id and sensor_names.sensor_name=current_data.sensor_name)
    order by ids.id,sensor_names.sensor_name') final_data (id integer, start_freq double precision,
                                                        stop_freq double precision, timestamp timestamp)

旁注 - start_freq 和 stop_freq 看起来不像传感器名称,但我正在使用遵循标准化形式的表格,因此我们将其称为 sensor_name。还有其他传感器我不担心这项任务。

编辑 - 来自下面建议的查询的结果:

id.  start_freq       stop_freq.             timestamp
18      15             null         "2020-07-09 20:03:38.937195+00"
19     null             18.         "2020-07-09 20:03:39.051836+00"
20     null            null          "2020-07-09 20:03:39.171837+00"
21     null            null         "2020-07-09 20:03:39.287994+00"
22     null            null         "2020-07-09 20:03:39.287994+00"
23     15              null         "2020-07-09 20:03:39.287994+00"
24     null            18           "2020-07-09 20:03:39.287994+00"

EDIT2 - 我在问题中附加的数据是示例数据,以便于讨论问题。结构和一切都与真实数据集相似。

【问题讨论】:

  • 如果您只有两个传感器名称,则使用 case 表达式。您提到不想要多个视图,所以这可能不是实际数据的样子?
  • 这基本上是数据的样子。还有其他sensor_names,但我并不真正关心它们,所以我将它们排除在外。我使用了CASE 语句,但它在数据点之间留下了NULL 值。
  • 如果第二个表中有超过两行与第一个表匹配怎么办?
  • @GordonLinoff 理想情况下忽略它们,但是,我总是可以选择我需要的任何东西,所以我愿意妥协是我必须将每个 sensor_name 作为一列旋转。确实有一些传感器属于你刚才描述的范围

标签: sql postgresql pivot-table crosstab


【解决方案1】:

有两种方法可以实现它。 (条件只有一组在measurement_timestamps时间范围内的启动和停止事件)

  1. 使用普通aggregationfilter
select 
m.id, 
min(s.value_cal) filter (where sensor_name='start_freq'),
min(s.value_cal) filter (where sensor_name='stop_freq'),
min(s.timestamp) filter (where sensor_name='stop_freq')
from measurement_timestamps m 
inner join sensor_double_precision s on s.timestamp between m.start_time and m.stop_time
group by m.id

注意:你没有指定你想要的日期,所以我选择了stop_freq timestamp。你可以改变你想要的。

  1. 使用Crosstab(你的方式)
select * from crosstab('select 
m.id, 
s.timestamp,
s.sensor_name,
s.value_cal
from measurement_timestamps m 
inner join sensor_double_precision s on s.timestamp between m.start_time and m.stop_time',
'select ''start_freq'' union select ''stop_freq'' ') as (id int, timestamp date, start_freq varchar, stop_freq varchar)

注意:只有在start_freq timestampstop_freq timestamp 相同时才能正常工作

DEMO

【讨论】:

  • 我运行了您建议的查询。虽然它没有给我任何错误,但它也没有工作。它一直试图加载,似乎卡住了。我什至在最后添加了 LIMIT 10,但这也无济于事。
  • 你可以检查小提琴。但是有什么错误或区别
  • 我也不知道如何让 Crosstab 工作。请问您的建议与其他答案建议的使用 case 语句有何不同?
  • 只有一个区别,如果开始和停止日期不同,那么我的可以工作,但其他的不能工作。
  • @Parashar 你能说出究竟是什么不起作用。如果您有大量数据,则将 where 子句与 start_time 和 stop_time 一起放置,而不是 limit
【解决方案2】:

我不明白为什么这不起作用。不过,您对“数据点之间的空值”的评论对我来说没有意义。

select
    m.id,
    min(case when m.sensor_name = 'start_freq' then m.value_cal end) as start_freq,
    min(case when m.sensor_name = 'stop_freq'  then m.value_cal end) as stop_freq, 
    s.timestamp
from measurement_timestamps m inner join sensor_double_precision s
    on s.timestamp between m.start_time and m.stop_time
group by m.id, s.timestamp

【讨论】:

  • 我无法真正向您显示错误,因为我无法在此处上传图片,但我在数据点之间获得了 null 值。我的意思是当 start_freq 是 15 时,对应的 stop_freq 应该是 18,但是这个查询返回 null。当停止频率为 18 时,start_freq 为 null
  • 我刚刚编辑了问题并发布了您的查询结果。
  • 您的数据显然与您在问题中指定的不符。
  • 嗯,它确实匹配。底部的额外空值来自我并不真正关心的“其他传感器名称”。我刚刚用更多的行更新了您的查询结果。我只是添加了我在底部看到的内容。 null 中间的 3 行值来自我不关心的传感器
猜你喜欢
  • 2021-06-21
  • 1970-01-01
  • 1970-01-01
  • 2017-10-21
  • 2013-11-11
  • 1970-01-01
  • 2013-11-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多