PostgreSQL — получить значение из той же таблицы

Получите средний трафик из данных за последнюю неделю по номеру WEEK и получите данные о трафике за последнюю неделю. Трафик (D-7)

Например, если дата = 13.05.2023, необходимо вывести данные о трафике (Трафик (D-7)) для даты = 06.05.2023.

Мне удается получить среднее значение, но я не знаю, как получить данные даты-7 и вывести их в целом.

create table a
(
date  varchar(50),
Tname varchar(50),
Week varchar(5),
Traffic float
)

insert into  a values ('5/1/2023', 'ID1', '18', 7.98)
insert into  a values ('5/2/2023', 'ID1', '18', 4.44)
insert into  a values ('5/3/2023', 'ID1', '18', 5.66)
insert into  a values ('5/4/2023', 'ID1', '18', 10.01)
insert into  a values ('5/5/2023', 'ID1', '18', 9.41)
insert into  a values ('5/6/2023', 'ID1', '18', 6.71)
insert into  a values ('5/7/2023', 'ID1', '18', 8.24)
insert into  a values ('5/8/2023', 'ID1', '19', 8.97)
insert into  a values ('5/9/2023', 'ID1', '19', 6.74)
insert into  a values ('5/10/2023', 'ID1', '19', 6.45)
insert into  a values ('5/11/2023', 'ID1', '19', 9.33)
insert into  a values ('5/12/2023', 'ID1', '19', 8.08)
insert into  a values ('5/13/2023', 'ID1', '19', 8.36)


SELECT date, Tname, Week,
AVG(Traffic) OVER(PARTITION BY Week) AS AVTraffic
FROM a
ORDER BY week

http://sqlfiddle.com/#!18/538b7/3

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
4
0
70
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Прежде всего, вам нужно исправить свои недостатки в дизайне схемы таблицы и объявить:

  • даты с типом "ДАТА" (вместо VARCHAR(50))
  • недельные значения с типом INT (вместо VARCHAR(5))
  • значения трафика с типом DECIMAL (вместо FLOAT)
CREATE TABLE tab(
    DATE      DATE,
    Tname     VARCHAR(50),
    Week      INT,
    Traffic   DECIMAL(4,2)
);

После того, как вы выполнили это, вы можете решить эту проблему следующим образом:

  • создание значения рейтинга для каждого дня недели в ваших неделях, используя EXTRACT на вашей дате
  • извлечение значения вашего трафика с предыдущей недели с помощью LAG, путем разделения вашего рейтинга, созданного на предыдущем шаге, и упорядочения по номеру недели.
WITH cte AS (
    SELECT date, Tname, Week, Traffic,
           ROUND(AVG(Traffic) OVER(PARTITION BY Week), 2) AS AVGTraffic,
           EXTRACT(ISODOW FROM date) - 1                  AS week_day
    FROM tab
)
SELECT date, Tname, Week, 
       LAG(Traffic) OVER(PARTITION BY week_day ORDER BY Week) AS prevweek_traffic,
       AVGTraffic
FROM cte
ORDER BY Week, week_day

И если вы понимаете, что у вас могут быть пробелы среди ваших недель (..., неделя 17, неделя 18, неделя 20, неделя 21, ...) и вам нужны значения именно за предыдущую неделю (которые могут отсутствовать), вы можно добавить фильтр к функции LAG, которая проверяет, являются ли неделя и предыдущая неделя последовательными:

...
CASE WHEN LAG(Week) OVER(PARTITION BY week_day ORDER BY Week) = Week-1
     THEN LAG(Traffic) OVER(PARTITION BY week_day ORDER BY Week) 
END
...

(только вместо LAG(Traffic) OVER(...))

Выход:

дата имя неделя prevweek_traffic средний трафик 2023-05-01T00:00:00.000Z ID1 18 нулевой 2023-05-02T00:00:00.000Z ID1 18 нулевой 2023-05-03T00:00:00.000Z ID1 18 нулевой 2023-05-04T00:00:00.000Z ID1 18 нулевой 2023-05-05T00:00:00.000Z ID1 18 нулевой 2023-05-06T00:00:00.000Z ID1 18 нулевой 2023-05-07T00:00:00.000Z ID1 18 нулевой 2023-05-08T00:00:00.000Z ID1 19 7,98 2023-05-09T00:00:00.000Z ID1 19 4,44 2023-05-10T00:00:00.000Z ID1 19 5,66 2023-05-11T00:00:00.000Z ID1 19 10.01 2023-05-12T00:00:00.000Z ID1 19 9.41 2023-05-13T00:00:00.000Z ID1 19 6,71

Посмотрите демо здесь.

Этот запрос допускает любые дыры в ваших данных, если это необходимо.

Примечание. Последнее предложение ORDER BY не требуется. Он там только для целей визуализации.

Неделю и день недели можно извлечь из даты с помощью EXTRACT. Это может дать более надежные результаты в конце года и в начале нового года.

Frank Heikens 02.06.2023 13:59

Умно, возьму на заметку, спасибо.

lemon 02.06.2023 14:20

Мне было интересно, есть ли способ автоматически инициализировать значения недели, используя DEFAULT с EXTRACT внутри оператора CREATE TABLE, и не указывать их один за другим внутри оператора INSERT...

lemon 02.06.2023 14:35

В чем разница между EXTRACT (ISODOW FROM date) - 1 и EXTRACT (Week FROM date). Если я использую EXTRACT (Week....), он получает значение предыдущей строки, а не значение предыдущей недели

joas 02.06.2023 14:56

ISODOW извлекает день недели (1,2,3,4,5,6,7), а WEEK извлекает неделю в году (в диапазоне 1-56), в этом разница.

lemon 02.06.2023 14:57

Ясно, поэтому он получает предыдущую строку, когда я использую Extract Week. Спасибо лимон!

joas 02.06.2023 14:59

@lemon Я предполагаю, что это простая ошибка ввода (на клавиатуре), но неделя в диапазоне 1-53 года.

Belayer 03.06.2023 20:03

Да, точно 53 недели в году, ключевая ошибка. @Belayer

lemon 03.06.2023 20:38

Используйте оконную функцию LAG с фреймом 7 PRECEDING, чтобы получить значения за 7 дней назад, чтобы избежать использования ненужных подзапросов или даже самостоятельных соединений, например.

SELECT 
  date, Tname, Week,
  LAG(traffic,7) OVER (PARTITION BY tname ORDER BY tname,to_date(date,'mm/dd/yyyy')),
  AVG(traffic)   OVER (PARTITION BY week,tname)
FROM a
ORDER BY tname,week;

   date    | tname | week |  lag  |        avg        
-----------+-------+------+-------+-------------------
 5/1/2023  | ID1   | 18   |       | 7.492857142857143
 5/2/2023  | ID1   | 18   |       | 7.492857142857143
 5/3/2023  | ID1   | 18   |       | 7.492857142857143
 5/4/2023  | ID1   | 18   |       | 7.492857142857143
 5/5/2023  | ID1   | 18   |       | 7.492857142857143
 5/6/2023  | ID1   | 18   |       | 7.492857142857143
 5/7/2023  | ID1   | 18   |       | 7.492857142857143
 5/8/2023  | ID1   | 19   |  7.98 | 7.988333333333333
 5/9/2023  | ID1   | 19   |  4.44 | 7.988333333333333
 5/10/2023 | ID1   | 19   |  5.66 | 7.988333333333333
 5/11/2023 | ID1   | 19   | 10.01 | 7.988333333333333
 5/12/2023 | ID1   | 19   |  9.41 | 7.988333333333333
 5/13/2023 | ID1   | 19   |  6.71 | 7.988333333333333
(13 rows)

Примечание:

  • этот запрос предполагает, что существует только одно измерение на дату/имя
  • Попробуйте изменить тип данных «дата» на date. Мне пришлось применить его, чтобы запрос работал правильно.

Демо: db<>рабочий пример

Попробуйте левое самосоединение следующим образом:

SELECT t1.date,
       t1.Tname,
       t1.week,
       t1.traffic,
       t2.traffic traffic_D7,
       AVG(t1.traffic) OVER(PARTITION BY t1.week) AS AVTraffic
FROM a t1 LEFT JOIN a t2
ON t1.date = t2.date + interval '7 day'

демо

Другие вопросы по теме