SQL — Динамический столбец даты с минимальными и максимальными значениями

#sql #postgresql #plpgsql

#sql #postgresql #plpgsql

Вопрос:

У меня есть таблица в 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.

Из таблицы примеров,

  • для даты есть 2 строки 2020-02-15
  • Для 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 дня. Это может быть текущая дата, текущая дата — 1 день, текущая дата — 2 дня
  • Меня не волнует формат, потому {v1=20,v2=20,v3=10} что он может быть разделен пробелом или чем-то еще. Я просто хочу увидеть эти 3 значения, вот и все.

Может кто-нибудь помочь мне написать логику для этого?

Комментарии:

1. Если просто запрос выбора не подойдет, тогда я в порядке с sp или UDF

2. Должны ли столбцы быть динамическими? Или всегда есть 2 столбца даты? Не могли бы вы объяснить, как вы хотите создать столбец даты?

3. Его динамические, фактические данные имеют дату 1 год, но меня интересуют последние 3 дня

4. Вам действительно нужен текст типа «v1 = 30» или вам нужен целочисленный массив?

5. Столбцы не могут отличаться для каждого запроса (один результат с 2 столбцами, а следующий — с 3 столбцами или чем-то еще). Вам нужно указать: 3 столбца. Может быть: CURRENT_DATE, CURRENT_DATE 1, CURRENT_DATE 2. Пожалуйста, опишите ваш ТОЧНЫЙ вариант использования в вопросе

Ответ №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}}
 

Возможно, этого вывода уже достаточно для обработки в вашем приложении.

Но невозможно создать запрос, который возвращает разное количество столбцов каждый раз, когда вы его запускаете, или где имена столбцов вычисляются во время выполнения запроса. Количество, тип и имя всех столбцов запроса определяются при анализе запроса сервером.


Если вы можете использовать значение даты для каждого результата в отдельном столбце, вы можете сделать что-то вроде этого:

 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) в качестве имени столбца.

Комментарии:

1. Позвольте мне попробовать это