Использование функции 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