web-dev-qa-db-fra.com

Utilisation de la fonction SQL Generate_series () dans Redshift

J'aimerais utiliser la fonction de série Generate dans Redshift, mais n'a pas réussi.

La documentation redshift dit qu'il n'est pas pris en charge. Le code suivant fonctionne:

select *
from generate_series(1,10,1)

les sorties:

1
2
3
...
10

Je voudrais faire la même chose avec les dates. J'ai essayé un certain nombre de variations, notamment:

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

démunie:

 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]

Aussi essayé:

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

Et essayé:

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

les deux lancent:

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]

Sinon on dirait que je vais utiliser ce code d'un autre poste:

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

PostgreSQL Generate_series () avec fonction SQL comme arguments

19
Elm

Amazon Redshift semble être basé sur PostgreSQL 8.0.2 . Les arguments de l'horodatage pour générer des_series () ont été ajoutés en 8.4.

Quelque chose comme ça, qui évite ce problème, pourrait Travailler dans Redshift.

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

Cela fonctionne dans PostgreSQL 8.3, qui est la version la plus ancienne que je puisse tester. Il est documenté dans 8.0.26.

plus tard. . .

Il semble que générate_series () n'est pas pris en charge dans Redshift. Mais étant donné que vous avez vérifié que select * from generate_series(1,10,1) le fait Travail, la syntaxe ci-dessus vous donne au moins une chance de combattre. (Bien que le type de données d'intervalle soit également documenté comme non pris en charge sur Redshift.)

encore plus tard. . .

Vous pouvez également créer une table d'entiers.

create table integers (
  n integer primary key
);

Peuplez-le cependant que vous aimez. Vous pourrez peut-être utiliser Generate_Series () localement, vider la table et le charger sur RedShift. (Je ne sais pas; Je n'utilise pas Redshift.)

Quoi qu'il en soit, vous pouvez faire une date simple par rapport à cette table sans se référer directement à Generate_series () ou aux types de données d'intervalle.

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

Cela fonctionne dans 8.3, au moins.

En utilisant RedShift aujourd'hui, vous pouvez générer une gamme de dates à l'aide de fonctions DateTime et d'alimentation dans une table numérique.

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

Génère ceci pour moi

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
13
Gabe Brown

La fonction generate_series() n'est pas entièrement prise en charge par RedShift. Voir le (((( Fonctions PostgreSQL non prises Section du Guide du développeur.

[~ # ~ ~] Mise à jour [~ # ~]

generate_series travaille avec Redshift maintenant.

SELECT CURRENT_DATE::TIMESTAMP  - (i * interval '1 day') as date_datetime 
FROM generate_series(1,31) i 
ORDER BY 1

Cela générera une date de 30 jours

Réf: Generate_series fonctionne dans Amazon Redshift

6
DJo

Je devais faire quelque chose de similaire, mais avec des intervalles de 5 minutes sur 7 jours. Alors voici un piratage basé sur CTE (laid mais pas trop verbeux)

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

Devrait être capable de l'étendre à d'autres régimes de génération. L'astuce ici utilise la jointure du produit cartésien (c'est-à-dire aucune clause de jointure/où) pour multiplier les CTE fabriqués à la main pour produire les augmentations nécessaires et s'appliquer à une date d'ancrage.

1
El Jeffe

Pour les commentaires de @ ryan Tuck et @ slobodan pejicgenerate_series() ne fonctionne pas sur Redshift lors de la jonction à une autre table.

La solution que j'ai utilisée était d'écrire chaque valeur de la série de la requête:

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

Utilisation d'un Python fonction comme ceci:

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)
0
Derek Hill