2012-06-29 4 views
6

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

$sql= "SELECT COUNT(*) AS count, date_trunc('minute', date) AS momento 
FROM p WHERE fk_id_b=$id_b GROUP BY date_trunc('minute', date) 
ORDER BY momento ASC"; 

Что мне нужно сделать, так это получить сумму подсчета для каждой строки со счетом за последние 2 минуты.

For example with the result of the $sql query above 
|-------date---------|----count----| 
|2012-06-21 05:20:00 |  12  | 
|2012-06-21 05:21:00 |  14  | 
|2012-06-21 05:22:00 |  10  | 
|2012-06-21 05:23:00 |  20  | 
|2012-06-21 05:24:00 |  25  | 
|2012-06-21 05:25:00 |  30  | 
|2012-06-21 05:26:00 |  10  | 

I want this result: 

|-------date---------|----count----| 
|2012-06-21 05:20:00 |  12  | 
|2012-06-21 05:21:00 |  26  |  12+14 
|2012-06-21 05:22:00 |  36  |  12+14+10 
|2012-06-21 05:23:00 |  44  |  14+10+20 
|2012-06-21 05:24:00 |  55  |  10+20+25 
|2012-06-21 05:25:00 |  75  |  20+25+30 
|2012-06-21 05:26:00 |  65  |  25+30+10 
+0

Очень сложно с SQL. Не может ли ваше приложение выполнить эту работу? Вы все равно должны получать строки, и вы можете сделать это в одном цикле. – Searle

+0

@Parth Bhatt: В разрешении, которое вы одобрили, явно задается другой вопрос. Пожалуйста, не одобряйте такие изменения. – interjay

+0

@interjay: Извините мою ошибку, я интерпретировал ее неправильно. –

ответ

10

Это не так сложно с lag() window function (также на SQL Fiddle):

CREATE TABLE t ("date" timestamptz, "count" int4); 
INSERT INTO t VALUES 
('2012-06-21 05:20:00',12), 
('2012-06-21 05:21:00',14), 
('2012-06-21 05:22:00',10), 
('2012-06-21 05:23:00',20), 
('2012-06-21 05:24:00',25), 
('2012-06-21 05:25:00',30), 
('2012-06-21 05:26:00',10); 

SELECT *, 
    "count" 
    + coalesce(lag("count", 1) OVER (ORDER BY "date"), 0) 
    + coalesce(lag("count", 2) OVER (ORDER BY "date"), 0) AS "total" 
    FROM t; 
  1. Я в двойных кавычках date и count колонны, так как они являются зарезервированными словами;
  2. lag(field, distance) дает мне значение field столбец distance в ряду от текущего, поэтому первая функция дает значение предыдущей строки, а второй вызов дает значение от предыдущего;
  3. coalesce() требуется, чтобы избежать NULL результата от lag() функции (для первой строки в запросе нет «предыдущий» один, таким образом, это NULL), в противном случае total также будет NULL.
+0

Отлично! Мне нравится концепция оконных функций, но я забываю их использовать. Имейте upvote :-) – Searle

+0

+1 для правильного решения. Я написал ответ, потому что комментариев недостаточно. –

9

@vyegorov's answer охватывает его в основном. Но у меня больше проблем, чем в комментариях.

  1. Не используйте reserved words как date и count в качестве идентификаторов на всех. PostgreSQL позволяет этим двум ключевым ключевым словам в качестве идентификатора - кроме каждого стандарта SQL. Но это все еще плохая практика. Тот факт, что вы может использовать что-либо внутри двойных кавычек как идентификатор, даже "; DELETE FROM tbl;" не делает его хорошей идеей. Название "date" для timestamp вводит в заблуждение.

  2. Неверный тип данных. Пример отображает timestamp, а не timestamptz. Здесь не имеет значения, но все еще вводит в заблуждение.

  3. Не требуется COALESCE(). С window functions lag() and lead() вы можете можете установить значение по умолчанию в качестве 3-го параметра:

Строительство на этой установке:

CREATE TABLE tbl (ts timestamp, ct int4); 
INSERT INTO tbl VALUES 
    ('2012-06-21 05:20:00', 12) 
, ('2012-06-21 05:21:00', 14) 
, ('2012-06-21 05:22:00', 10) 
, ('2012-06-21 05:23:00', 20) 
, ('2012-06-21 05:24:00', 25) 
, ('2012-06-21 05:25:00', 30) 
, ('2012-06-21 05:26:00', 10); 

Запрос:

SELECT ts, ct + lag(ct, 1, 0) OVER (ORDER BY ts) 
       + lag(ct, 2, 0) OVER (ORDER BY ts) AS total 
FROM tbl; 

Или лучше еще : используйте один sum() как функция агрегации окна wi th a custom window frame:

SELECT ts, sum(ct) OVER (ORDER BY ts ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) 
FROM tbl; 

Тот же результат.
Связанные:

+0

Значение по умолчанию 'lag' /' lead' является хорошим! – vyegorov

10

Вот более общее решение на сумму значений из текущего и предыдущих N строк (N = 2 в вашем случае).

SELECT "date", 
sum("count") OVER (order by "date" ROWS BETWEEN 2 preceding AND current row) 
FROM t 
ORDER BY "date"; 

Вы можете изменить N между 0 и «Без ограничений». Этот подход дает вам возможность иметь параметр в вашем приложении «счет за последние минута». Кроме того, нет необходимости обрабатывать значения по умолчанию, если они находятся за пределами границ.

Вы можете найти более подробно об этом в PostgreSQL документации (4.2.8. Window Function Calls)

+0

Это лучшее решение, чем принятый ответ – user2259664

Смежные вопросы