Использование функции sql generate_series() в красном смещении

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

Документация красного смещения говорит, что это не поддерживается. Следующий код работает:

select *
from generate_series(1,10,1)

выходы:

1
2
3
...
10

Я хотел бы сделать то же самое с датами. Я пробовал несколько вариантов, в том числе:

select *
from generate_series(date('2008-10-01'),date('2008-10-10 00:00:00'),1)

выгоняет:

 ERROR: function generate_series(date, date, integer) does not exist
 Hint: No function matches the given name and argument types.
 You may need to add explicit type casts. [SQL State=42883]

Также попробовал:

select *
from generate_series('2008-10-01 00:00:00'::timestamp,
'2008-10-10 00:00:00'::timestamp,'1 day')

И попробовал:

select *
from generate_series(cast('2008-10-01 00:00:00' as datetime),
cast('2008-10-10 00:00:00' as datetime),'1 day')

оба выгоняют

ERROR: function generate_series(timestamp without time zone, timestamp without time zone, "unknown") does not exist
Hint: No function matches the given name and argument types.
You may need to add explicit type casts. [SQL State=42883]

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

SELECT to_char(DATE '2008-01-01'
+ (interval '1 month' * generate_series(0,57)), 'YYYY-MM-DD') AS ym

PostgreSQL generate_series () с функцией SQL в качестве аргументов

10 ответов

Amazon Redshift, похоже, основан на PostgreSQL 8.0.2. Аргументы меток времени для generate_series() были добавлены в 8.4.

Нечто подобное, которое обходит эту проблему, может работать в Redshift.

SELECT current_date + (n || ' days')::interval
from generate_series (1, 30) n

Он работает в PostgreSQL 8.3, самой ранней версии, которую я могу протестировать. Это задокументировано в 8.0.26.

Потом. , ,

Кажется, generate_series() не поддерживается в Redshift. Но, учитывая, что вы убедились, что select * from generate_series(1,10,1) действительно работает, синтаксис выше по крайней мере дает вам шанс для борьбы. (Хотя тип данных интервала также задокументирован как неподдерживаемый в Redshift.)

Еще позже. , ,

Вы также можете создать таблицу целых чисел.

create table integers (
  n integer primary key
);

Заполните его так, как вам нравится. Возможно, вы сможете использовать generate_series() локально, сбросить таблицу и загрузить ее в Redshift. (Я не знаю; я не использую Redshift.)

В любом случае, вы можете сделать простую арифметику дат с этой таблицей, не обращаясь непосредственно к generate_series() или к интервальным типам данных.

select (current_date + n)
from integers
where n < 31;

Это работает в 8.3, по крайней мере.

Используя Redshift сегодня, вы можете сгенерировать диапазон дат, используя функции datetime и подачу в таблицу чисел.

select (getdate()::date - generate_series)::date from generate_series(1,30,1)

Создает это для меня

date
2015-11-06
2015-11-05
2015-11-04
2015-11-03
2015-11-02
2015-11-01
2015-10-31
2015-10-30
2015-10-29
2015-10-28
2015-10-27
2015-10-26
2015-10-25
2015-10-24
2015-10-23
2015-10-22
2015-10-21
2015-10-20
2015-10-19
2015-10-18
2015-10-17
2015-10-16
2015-10-15
2015-10-14
2015-10-13
2015-10-12
2015-10-11
2015-10-10
2015-10-09
2015-10-08

generate_series() функция не полностью поддерживается Redshift. См. Раздел " Неподдерживаемые функции PostgreSQL " в руководстве разработчика.

На момент написания этого, generate_series()на нашем экземпляре Redshift (1.0.33426) нельзя было использовать, например, для создания таблицы:

      # select generate_series(1,100,1);
1
2
...

# create table normal_series as select generate_series(1,100,1);
INFO: Function "generate_series(integer, integer, integer) not supported.
ERROR: Specified types or functions (one per INFO message) not supported on Redshift tables.

Однако, with recursiveработает:

      # create table recursive_series as with recursive t(n) as (select 1::integer union all select n+1 from t where n < 100) select n from t;
SELECT

-- modify as desired, here is a date series:
# select getdate()::date + n from recursive_series;
2021-12-18
2021-12-19
...

Мне нужно было сделать нечто подобное, но с 5-минутными интервалами в течение 7 дней. Итак, вот взлом на основе CTE (некрасиво, но не слишком многословно)

INSERT INTO five_min_periods
WITH 
periods  AS (select 0 as num UNION select 1 as num UNION select 2 UNION select 3 UNION select 4 UNION select 5 UNION select 6 UNION select 7 UNION select 8 UNION select 9 UNION select 10 UNION select 11),
hours    AS (select num from periods UNION ALL select num + 12 from periods),
days     AS (select num from periods where num <= 6),
rightnow AS (select CAST( TO_CHAR(GETDATE(), 'yyyy-mm-dd hh24') || ':' || trim(TO_CHAR((ROUND((DATEPART (MINUTE, GETDATE()) / 5), 1) * 5 ),'09')) AS TIMESTAMP) as start)
select  
  ROW_NUMBER() OVER(ORDER BY d.num DESC, h.num DESC, p.num DESC) as idx
  , DATEADD(minutes, -p.num * 5, DATEADD( hours, -h.num, DATEADD( days, -d.num, n.start ) ) ) AS period_date
from days d, hours h, periods p, rightnow n

Должно быть в состоянии распространить это на другие схемы генерации. Хитрость здесь заключается в том, чтобы использовать декартово объединение продуктов (т.е. без предложения JOIN/WHERE) для умножения созданных вручную CTE для получения необходимых приращений и применения к дате привязки.

Функция Redshift generate_series() является функцией только ведущего узла, и поэтому вы не можете использовать ее для последующей обработки на вычислительных узлах. Это можно заменить рекурсивным CTE (или сохранить таблицу «даты» в вашей базе данных). У меня есть пример такого в недавнем ответе:

Перекрестное соединение Redshift с последовательностью дат

Одно предостережение, которое я хотел бы дать в подобных ответах, заключается в том, чтобы быть осторожным с соединениями неравенства (или перекрестными соединениями или любыми недостаточно квалифицированными соединениями) при работе с ОЧЕНЬ БОЛЬШИМИ таблицами, что может часто происходить в Redshift. Если вы присоединяетесь к умеренной таблице Redshift, скажем, 1M строк, тогда все будет хорошо. Но если вы делаете это с таблицей из 1 млрд строк, взрыв данных, скорее всего, вызовет серьезные проблемы с производительностью, поскольку запрос будет перенесен на диск.

Я написал пару официальных документов о том, как написать этот тип запроса с учетом пространства данных. Эта проблема больших промежуточных результатов не уникальна для Redshift, и я впервые разработал свой подход к решению проблемы запроса клиента HIVE. «Первое правило написания SQL для больших данных — не делайте больше»

Согласно комментариям @Ryan Tuck и @Slobodan Pejic generate_series() не работает на Redshift при присоединении к другому столу.

Обходной путь, который я использовал, заключался в том, чтобы записать каждое значение в серии в запросе:

SELECT
'2019-01-01'::date AS date_month
UNION ALL
SELECT
'2019-02-01'::date AS date_month

Используя такую ​​функцию Python:

import arrow

def generate_date_series(start, end):
    start = arrow.get(start)
    end = arrow.get(end)

    months = list(
        f"SELECT '{month.format('YYYY-MM-DD')}'::date AS date_month"
        for month in arrow.Arrow.range('month', start, end)
    )

    return "\nUNION ALL\n".join(months)

возможно, не так элегантно, как другие решения, но вот как я это сделал:

      drop table if exists #dates;
create temporary table #dates as
with recursive cte(val_date) as
    (select 
        cast('2020-07-01' as date) as val_date
    union all
    select
        cast(dateadd(day, 1, val_date) as date) as val_date
    from 
        cte
    where 
        val_date <= getdate()
    )
select 
    val_date as yyyymmdd
from
    cte
order by
    val_date
;

Для пятиминутных ведер я бы сделал следующее:

      select date_trunc('minute', getdate()) - (i || ' minutes')::interval
from generate_series(0, 60*5-1, 5) as i

Вы можете заменить 5 любым заданным интервалом и 60 количеством строк, которые вы хотите.

      SELECT CURRENT_DATE::TIMESTAMP - (i * interval '1 day') as date_datetime 
FROM generate_series(1,(select datediff(day,'01-Jan-2021',now()::date))) i 
ORDER BY 1
Другие вопросы по тегам