【问题标题】:SQL - Dynamic date column with min, max valuesSQL - 具有最小值、最大值的动态日期列
【发布时间】:2021-02-17 08:45:42
【问题描述】:

我在 PostgreSQL(12) 中有一张表,如下所示。

|Name|ts                |v1 |v2 |v3 |
|----|------------------|---|---|---|
|aaa |2020-02-15 0:00:00|10 |150|5  |
|bbb |2020-02-15 0:00:00|20 |160|10 |
|aaa |2020-02-15 1:00:00|30 |170|15 |
|bbb |2020-02-15 1:00:00|40 |180|20 |
|aaa |2020-02-16 0:00:00|50 |190|25 |
|bbb |2020-02-16 0:00:00|60 |200|30 |
|aaa |2020-02-16 1:00:00|70 |210|35 |
|bbb |2020-02-16 1:00:00|80 |220|40 |

我计划为每天创建一个报告表以及v1,v2,v3 的最小值和最大值之间的差异。

示例输出:

|Name|2020-02-15         |2020-02-16         |
|----|-------------------|-------------------|
|aaa |{v1=20,v2=20,v3=10}|{v1=20,v2=20,v3=10}|
|bbb |{v1=20,v2=20,v3=10}|{v1=20,v2=20,v3=10}|

ts - 将被动态提取为列名(仅日期部分)

但我正在努力编写逻辑。它应该是, 对于每个名称,我们应该计算 min(v1)、max(v1) 之间的差异,类似地是 v2 和 v3。

从示例表中,

  • 日期2020-02-15有2行
  • 对于 v1,我们必须找到 min(v1), max(v1) where date=2020-02-15 之间的区别
    • min(v1) = 10, max(v1) = 30,所以相差20
    • min(v2) = 150,max(v2) = 170,差异 = 20
    • min(v3) = 5, max(v3) =15, 差值 = 10
  • 那么对于2020-02-15这一天,输出行将是{v1=20,v2=20,v3=10}
  • 第二天重复相同的步骤。

更新 #1:

  • ts 列有许多日期值,但我只对 3 天感兴趣。可以是当前日期、当前日期 - 1DAY、当前日期 - 2 天
  • 我不关心{v1=20,v2=20,v3=10} 的格式,它可以是空格分隔或任何其他格式。我只想看看这 3 个值,仅此而已。

有人可以帮我写一下这个逻辑吗?

【问题讨论】:

  • 如果只是选择查询不行,那么我可以使用 sp 或 UDF
  • 列应该是动态的吗?还是总是有 2 个日期列?你能解释一下你想如何创建日期列吗?
  • 它是动态的,实际数据有1年的日期,但我对最近3天感兴趣
  • 你真的想要像“v1=30”这样的文本还是想要一个整数数组?
  • 每个查询的列不能不同(一个结果有 2 列,下一个结果是 3 列或其他内容)。你需要告诉:3列。也许:CURRENT_DATE、CURRENT_DATE + 1、CURRENT_DATE + 2。请在问题中描述您的确切用例

标签: sql postgresql plpgsql


【解决方案1】:

第一部分 - 每天汇总差异 - 非常简单:

select name, 
       jsonb_object_agg(date, v) as vals
from (       
  select name, 
         ts::date as date,
         jsonb_build_object('v1', max(v1) - min(v1),
                            'v2', max(v2) - min(v2),
                            'v3', max(v3) - min(v3)) as v
  from the_table
  where .... --<<< limit the dates here
  group by name, ts::date       
) t
group by name

使用您的示例数据返回:

name | vals                                                                                        
-----+---------------------------------------------------------------------------------------------
aaa  | {"2020-02-15": {"v1": 20, "v2": 20, "v3": 10}, "2020-02-16": {"v1": 20, "v2": 20, "v3": 10}}
bbb  | {"2020-02-15": {"v1": 20, "v2": 20, "v3": 10}, "2020-02-16": {"v1": 20, "v2": 20, "v3": 10}}

也许该输出已经足以在您的应用程序中进行处理。

但不可能创建每次运行时返回不同列数的查询,或者在运行查询时评估列 names 的位置。查询的所有列的数量、类型和名称在服务器解析查询时确定。


如果您可以在单独的列中使用每个结果的日期值,则可以执行以下操作:

select name, 
       vals #>> '{0,date}' as date_1, 
       vals #>> '{0,values}' as date_1_values, 
       vals #>> '{1,date}' as date_2, 
       vals #>> '{1,values}' as date_2_values
from (       
  select name, 
         jsonb_agg(jsonb_build_object('date', date, 'values', v) order by date) as vals
  from (       
    select name, 
           ts::date as date,
           jsonb_build_object('v1', max(v1) - min(v1),
                              'v2', max(v2) - min(v2),
                              'v3', max(v3) - min(v3)) as v
    from the_table
    where .... --<<< limit the dates here
    group by name, ts::date       
  ) t
  group by name
) x

这将返回如下内容:

name | date_1     | date_1_values                  | date_2     | date_2_values                 
-----+------------+--------------------------------+------------+-------------------------------
aaa  | 2020-02-15 | {"v1": 20, "v2": 20, "v3": 10} | 2020-02-16 | {"v1": 20, "v2": 20, "v3": 10}
bbb  | 2020-02-15 | {"v1": 20, "v2": 20, "v3": 10} | 2020-02-16 | {"v1": 20, "v2": 20, "v3": 10}

您可以使用“current_date”使之变得有些动态:

select name, 
       vals ->> (current_date - 2)::text as "current_date - 2",
       vals ->> (current_date - 1)::text as "current_date - 1"
from (       
  select name, 
         jsonb_object_agg(date, v) as vals
  from (       
    select name, 
           ts::date as date,
           jsonb_build_object('v1', max(v1) - min(v1),
                              'v2', max(v2) - min(v2),
                              'v3', max(v3) - min(v3)) as v
    from the_table
    where ts::date in (current_date - 2, current_date - 1)
    group by name, ts::date       
  ) t
  group by name
) x

您可以获取current_date - 1(例如2021-02-17)的作为列名。

【讨论】:

  • 让我试试这个
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-03
  • 2015-03-15
  • 1970-01-01
  • 1970-01-01
  • 2013-04-01
  • 2017-11-02
相关资源
最近更新 更多