Как округлить до ближайших X минут с PL / pgSQL? - PullRequest
2 голосов
/ 26 января 2010

Как я могу округлить до ближайших X минут?

Вот моя попытка:

DECLARE
  _stamp ALIAS FOR $1; -- timestamp
  _nearest ALIAS FOR $2; -- minutes (integer)
  _minutes decimal;
  _ret timestamp;
BEGIN
  _ret := date_trunc('minute', _stamp);

  SELECT EXTRACT (minute FROM _ret)::integer INTO _minutes;

 IF (_minutes % _nearest < (_nearest / 2)) THEN
    RETURN _ret + _minutes * interval '1 minute';
  ELSE
    RETURN _ret - _minutes * interval '1 minute';
  END IF;

  RETURN _ret;
END;

Пример:

SELECT round_to_nearest_minute ('2010-01-01 12:34:56', 15);

Должен вернуться

2010-01-01 12:30:00

Ответы [ 7 ]

2 голосов
/ 10 сентября 2012

Функция круглого времени выглядит хорошо. Другой способ сделать это, который будет работать в течение любой единицы времени без изменений, будет следующим:

SELECT '1970-01-01'::timestamptz + EXTRACT(epoch FROM now())::integer / 300
            * 300 * interval '1 second';

Где две ссылки на 300 - это количество секунд, которое вы хотите округлить, например, 300 = 5 минут. Используя целочисленную математику, вы усекаете и затем умножаете количество секунд от эпохи, чтобы получить округленное значение.

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

Если вы хотите округлить до ближайших 15 минут, используйте 900 вместо 300. Ближайшие полтора часа будут 5400. И т.д.

2 голосов
/ 10 сентября 2012

В дополнение к моему предыдущему ответу, вот функция, которая собирает все это вместе и делает именно то, что вы просите:

CREATE FUNCTION date_round(base_date timestamptz, round_interval interval)
    RETURNS timestamptz AS $BODY$
SELECT '1970-01-01'::timestamptz 
    + (EXTRACT(epoch FROM $1)::integer + EXTRACT(epoch FROM $2)::integer / 2)
    / EXTRACT(epoch FROM $2)::integer
    * EXTRACT(epoch FROM $2)::integer * interval '1 second';
$BODY$ LANGUAGE SQL STABLE;

А вот пример того, как его назвать:

SELECT date_round(now(), '15 minutes');

Вы можете указать любой интервал, который вы хотите, и он должен работать. Как закодировано, он округляется до ближайшего интервала, вверх или вниз.

Если вы хотите усечь вместо этого, просто удалите + EXTRACT(epoch FROM $2)::integer / 2).

Надеюсь, теперь это будет действовать как окончательный ответ, который принимает правильные типы аргументов (интервал, а не целое число минут).

2 голосов
/ 26 января 2010

Вместо сложения или вычитания

_minutes * interval '1 minute'

вы должны вычитать

(_minutes % _nearest) * interval '1 minute'

или добавление

(_nearest - (_minutes % _nearest)) * interval '1 minute'

1 голос
/ 22 февраля 2012

Это также может быть полезно: функция округления метки времени до 5 минут. Вы можете легко изменить его, чтобы он работал с любым полем временной метки и любым количеством этого поля.

функция round_time

0 голосов
/ 20 февраля 2012

Используйте функцию date_trunc('src', timestamp [value]).

См. Документацию: http://www.postgresql.org/docs/9.1/static/functions-datetime.html

0 голосов
/ 26 января 2010

Это похоже на работу:

DECLARE
  _stamp ALIAS FOR $1;
  _nearest ALIAS FOR $2;
  _seconds integer;
  _ret timestamp;
  _minutes decimal;
  _mod decimal;
BEGIN
  _ret := date_trunc('minute', _stamp);

  SELECT EXTRACT (minute FROM _ret)::integer INTO _minutes;

  _mod := _minutes % _nearest;

  IF (_mod > (_nearest / 2)) THEN
    RETURN _ret + (_nearest - _mod) * interval '1 minute';
  ELSE
    RETURN _ret - (_mod) * interval '1 minute';
  END IF;

  RETURN _ret;

END;

Спасибо Стивену Денну:)

0 голосов
/ 26 января 2010

Вы можете сделать это как простой оператор SQL, заменив CURRENT_TIMESTAMP на значение TIMESTAMP.

SELECT date_trunc('minute', CURRENT_TIMESTAMP::timestamp without time zone)
    + (
    CASE WHEN extract(second from CURRENT_TIMESTAMP ) >= 30
        THEN 1::text
        ELSE 0::text
    END
    || ' minute'

    )::interval ;

Оптимизация, это добавление 30 секунд и затем просто date_trunc(), я знал это раньше, но особая благодарность irc://irc.freenode.net/#postgresql StuckMojo

SELECT date_trunc(
    'minute'
    , CURRENT_TIMESTAMP::timestamp without time zone
      + '30 seconds'::interval
);

update @stephen, конечно, это так, хотя технически это не то, что требовал вопрос .

CREATE OR REPLACE FUNCTION round_trunc ( in text, in timestamp ) RETURNS timestamp without time zone  AS $$
  SELECT pg_catalog.date_trunc(
      $1
      , $2::timestamp without time zone
        + ('0.5 ' || $1 )::interval
  )
$$ LANGUAGE sql VOLATILE;

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

...